| ... | @@ -6,7 +6,7 @@ const assert = std.debug.assert; | ... | @@ -6,7 +6,7 @@ const assert = std.debug.assert; |
| 6 | | 6 | |
| 7 | const Pdb = @This(); | 7 | const Pdb = @This(); |
| 8 | | 8 | |
| 9 | in_file: File, | 9 | file_reader: *File.Reader, |
| 10 | msf: Msf, | 10 | msf: Msf, |
| 11 | allocator: Allocator, | 11 | allocator: Allocator, |
| 12 | string_table: ?*MsfStream, | 12 | string_table: ?*MsfStream, |
| ... | @@ -36,31 +36,28 @@ pub const Module = struct { | ... | @@ -36,31 +36,28 @@ pub const Module = struct { |
| 36 | } | 36 | } |
| 37 | }; | 37 | }; |
| 38 | | 38 | |
| 39 | pub fn init(allocator: Allocator, path: []const u8) !Pdb { | 39 | pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { |
| 40 | const file = try std.fs.cwd().openFile(path, .{}); | | |
| 41 | errdefer file.close(); | | |
| 42 | | | |
| 43 | return .{ | 40 | return .{ |
| 44 | .in_file = file, | 41 | .file_reader = file_reader, |
| 45 | .allocator = allocator, | 42 | .allocator = gpa, |
| 46 | .string_table = null, | 43 | .string_table = null, |
| 47 | .dbi = null, | 44 | .dbi = null, |
| 48 | .msf = try Msf.init(allocator, file), | 45 | .msf = try Msf.init(gpa, file_reader), |
| 49 | .modules = &[_]Module{}, | 46 | .modules = &.{}, |
| 50 | .sect_contribs = &[_]pdb.SectionContribEntry{}, | 47 | .sect_contribs = &.{}, |
| 51 | .guid = undefined, | 48 | .guid = undefined, |
| 52 | .age = undefined, | 49 | .age = undefined, |
| 53 | }; | 50 | }; |
| 54 | } | 51 | } |
| 55 | | 52 | |
| 56 | pub fn deinit(self: *Pdb) void { | 53 | pub fn deinit(self: *Pdb) void { |
| 57 | self.in_file.close(); | 54 | const gpa = self.allocator; |
| 58 | self.msf.deinit(self.allocator); | 55 | self.msf.deinit(gpa); |
| 59 | for (self.modules) |*module| { | 56 | for (self.modules) |*module| { |
| 60 | module.deinit(self.allocator); | 57 | module.deinit(gpa); |
| 61 | } | 58 | } |
| 62 | self.allocator.free(self.modules); | 59 | gpa.free(self.modules); |
| 63 | self.allocator.free(self.sect_contribs); | 60 | gpa.free(self.sect_contribs); |
| 64 | } | 61 | } |
| 65 | | 62 | |
| 66 | pub fn parseDbiStream(self: *Pdb) !void { | 63 | pub fn parseDbiStream(self: *Pdb) !void { |
| ... | @@ -68,11 +65,7 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -68,11 +65,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 68 | return error.InvalidDebugInfo; | 65 | return error.InvalidDebugInfo; |
| 69 | | 66 | |
| 70 | const gpa = self.allocator; | 67 | const gpa = self.allocator; |
| 71 | | 68 | const reader = &stream.interface; |
| 72 | const deprecated_reader = stream.reader(); | | |
| 73 | var adapted_buffer: [1024]u8 = undefined; | | |
| 74 | var adapted_reader = deprecated_reader.adaptToNewApi(&adapted_buffer); | | |
| 75 | const reader = &adapted_reader.new_interface; | | |
| 76 | | 69 | |
| 77 | const header = try reader.takeStruct(std.pdb.DbiStreamHeader, .little); | 70 | const header = try reader.takeStruct(std.pdb.DbiStreamHeader, .little); |
| 78 | if (header.version_header != 19990903) // V70, only value observed by LLVM team | 71 | if (header.version_header != 19990903) // V70, only value observed by LLVM team |
| ... | @@ -113,7 +106,7 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -113,7 +106,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 113 | this_record_len += march_forward_bytes; | 106 | this_record_len += march_forward_bytes; |
| 114 | } | 107 | } |
| 115 | | 108 | |
| 116 | try modules.append(Module{ | 109 | try modules.append(.{ |
| 117 | .mod_info = mod_info, | 110 | .mod_info = mod_info, |
| 118 | .module_name = try module_name.toOwnedSlice(), | 111 | .module_name = try module_name.toOwnedSlice(), |
| 119 | .obj_file_name = try obj_file_name.toOwnedSlice(), | 112 | .obj_file_name = try obj_file_name.toOwnedSlice(), |
| ... | @@ -156,29 +149,28 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -156,29 +149,28 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 156 | } | 149 | } |
| 157 | | 150 | |
| 158 | pub fn parseInfoStream(self: *Pdb) !void { | 151 | pub fn parseInfoStream(self: *Pdb) !void { |
| 159 | var stream = self.getStream(pdb.StreamType.pdb) orelse | 152 | var stream = self.getStream(pdb.StreamType.pdb) orelse return error.InvalidDebugInfo; |
| 160 | return error.InvalidDebugInfo; | 153 | const reader = &stream.interface; |
| 161 | const reader = stream.reader(); | | |
| 162 | | 154 | |
| 163 | // Parse the InfoStreamHeader. | 155 | // Parse the InfoStreamHeader. |
| 164 | const version = try reader.readInt(u32, .little); | 156 | const version = try reader.takeInt(u32, .little); |
| 165 | const signature = try reader.readInt(u32, .little); | 157 | const signature = try reader.takeInt(u32, .little); |
| 166 | _ = signature; | 158 | _ = signature; |
| 167 | const age = try reader.readInt(u32, .little); | 159 | const age = try reader.takeInt(u32, .little); |
| 168 | const guid = try reader.readBytesNoEof(16); | 160 | const guid = try reader.takeArray(16); |
| 169 | | 161 | |
| 170 | if (version != 20000404) // VC70, only value observed by LLVM team | 162 | if (version != 20000404) // VC70, only value observed by LLVM team |
| 171 | return error.UnknownPDBVersion; | 163 | return error.UnknownPDBVersion; |
| 172 | | 164 | |
| 173 | self.guid = guid; | 165 | self.guid = guid.*; |
| 174 | self.age = age; | 166 | self.age = age; |
| 175 | | 167 | |
| | 168 | const gpa = self.allocator; |
| | 169 | |
| 176 | // Find the string table. | 170 | // Find the string table. |
| 177 | const string_table_index = str_tab_index: { | 171 | const string_table_index = str_tab_index: { |
| 178 | const name_bytes_len = try reader.readInt(u32, .little); | 172 | const name_bytes_len = try reader.takeInt(u32, .little); |
| 179 | const name_bytes = try self.allocator.alloc(u8, name_bytes_len); | 173 | const name_bytes = try reader.readAlloc(gpa, name_bytes_len); |
| 180 | defer self.allocator.free(name_bytes); | | |
| 181 | try reader.readNoEof(name_bytes); | | |
| 182 | | 174 | |
| 183 | const HashTableHeader = extern struct { | 175 | const HashTableHeader = extern struct { |
| 184 | size: u32, | 176 | size: u32, |
| ... | @@ -188,23 +180,23 @@ pub fn parseInfoStream(self: *Pdb) !void { | ... | @@ -188,23 +180,23 @@ pub fn parseInfoStream(self: *Pdb) !void { |
| 188 | return cap * 2 / 3 + 1; | 180 | return cap * 2 / 3 + 1; |
| 189 | } | 181 | } |
| 190 | }; | 182 | }; |
| 191 | const hash_tbl_hdr = try reader.readStruct(HashTableHeader); | 183 | const hash_tbl_hdr = try reader.takeStruct(HashTableHeader, .little); |
| 192 | if (hash_tbl_hdr.capacity == 0) | 184 | if (hash_tbl_hdr.capacity == 0) |
| 193 | return error.InvalidDebugInfo; | 185 | return error.InvalidDebugInfo; |
| 194 | | 186 | |
| 195 | if (hash_tbl_hdr.size > HashTableHeader.maxLoad(hash_tbl_hdr.capacity)) | 187 | if (hash_tbl_hdr.size > HashTableHeader.maxLoad(hash_tbl_hdr.capacity)) |
| 196 | return error.InvalidDebugInfo; | 188 | return error.InvalidDebugInfo; |
| 197 | | 189 | |
| 198 | const present = try readSparseBitVector(&reader, self.allocator); | 190 | const present = try readSparseBitVector(reader, gpa); |
| 199 | defer self.allocator.free(present); | 191 | defer gpa.free(present); |
| 200 | if (present.len != hash_tbl_hdr.size) | 192 | if (present.len != hash_tbl_hdr.size) |
| 201 | return error.InvalidDebugInfo; | 193 | return error.InvalidDebugInfo; |
| 202 | const deleted = try readSparseBitVector(&reader, self.allocator); | 194 | const deleted = try readSparseBitVector(reader, gpa); |
| 203 | defer self.allocator.free(deleted); | 195 | defer gpa.free(deleted); |
| 204 | | 196 | |
| 205 | for (present) |_| { | 197 | for (present) |_| { |
| 206 | const name_offset = try reader.readInt(u32, .little); | 198 | const name_offset = try reader.takeInt(u32, .little); |
| 207 | const name_index = try reader.readInt(u32, .little); | 199 | const name_index = try reader.takeInt(u32, .little); |
| 208 | if (name_offset > name_bytes.len) | 200 | if (name_offset > name_bytes.len) |
| 209 | return error.InvalidDebugInfo; | 201 | return error.InvalidDebugInfo; |
| 210 | const name = std.mem.sliceTo(name_bytes[name_offset..], 0); | 202 | const name = std.mem.sliceTo(name_bytes[name_offset..], 0); |
| ... | @@ -302,14 +294,12 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S | ... | @@ -302,14 +294,12 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S |
| 302 | const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset; | 294 | const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset; |
| 303 | try self.string_table.?.seekTo(strtab_offset); | 295 | try self.string_table.?.seekTo(strtab_offset); |
| 304 | const source_file_name = s: { | 296 | const source_file_name = s: { |
| 305 | const deprecated_reader = self.string_table.?.reader(); | 297 | const string_reader = &self.string_table.?.interface; |
| 306 | var adapted_buffer: [1024]u8 = undefined; | | |
| 307 | var adapted_reader = deprecated_reader.adaptToNewApi(&adapted_buffer); | | |
| 308 | var source_file_name: std.Io.Writer.Allocating = .init(gpa); | 298 | var source_file_name: std.Io.Writer.Allocating = .init(gpa); |
| 309 | defer source_file_name.deinit(); | 299 | defer source_file_name.deinit(); |
| 310 | _ = try adapted_reader.new_interface.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); | 300 | _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); |
| 311 | assert(adapted_reader.new_interface.buffered()[0] == 0); // TODO change streamDelimiterLimit API | 301 | assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| 312 | adapted_reader.new_interface.toss(1); | 302 | string_reader.toss(1); |
| 313 | break :s try source_file_name.toOwnedSlice(); | 303 | break :s try source_file_name.toOwnedSlice(); |
| 314 | }; | 304 | }; |
| 315 | errdefer gpa.free(source_file_name); | 305 | errdefer gpa.free(source_file_name); |
| ... | @@ -366,19 +356,16 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { | ... | @@ -366,19 +356,16 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { |
| 366 | | 356 | |
| 367 | const stream = self.getStreamById(mod.mod_info.module_sym_stream) orelse | 357 | const stream = self.getStreamById(mod.mod_info.module_sym_stream) orelse |
| 368 | return error.MissingDebugInfo; | 358 | return error.MissingDebugInfo; |
| 369 | const reader = stream.reader(); | 359 | const reader = &stream.interface; |
| 370 | | 360 | |
| 371 | const signature = try reader.readInt(u32, .little); | 361 | const signature = try reader.takeInt(u32, .little); |
| 372 | if (signature != 4) | 362 | if (signature != 4) |
| 373 | return error.InvalidDebugInfo; | 363 | return error.InvalidDebugInfo; |
| 374 | | 364 | |
| 375 | mod.symbols = try self.allocator.alloc(u8, mod.mod_info.sym_byte_size - 4); | 365 | const gpa = self.allocator; |
| 376 | errdefer self.allocator.free(mod.symbols); | | |
| 377 | try reader.readNoEof(mod.symbols); | | |
| 378 | | 366 | |
| 379 | mod.subsect_info = try self.allocator.alloc(u8, mod.mod_info.c13_byte_size); | 367 | mod.symbols = try reader.readAlloc(gpa, mod.mod_info.sym_byte_size - 4); |
| 380 | errdefer self.allocator.free(mod.subsect_info); | 368 | mod.subsect_info = try reader.readAlloc(gpa, mod.mod_info.c13_byte_size); |
| 381 | try reader.readNoEof(mod.subsect_info); | | |
| 382 | | 369 | |
| 383 | var sect_offset: usize = 0; | 370 | var sect_offset: usize = 0; |
| 384 | var skip_len: usize = undefined; | 371 | var skip_len: usize = undefined; |
| ... | @@ -404,8 +391,7 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { | ... | @@ -404,8 +391,7 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { |
| 404 | } | 391 | } |
| 405 | | 392 | |
| 406 | pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream { | 393 | pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream { |
| 407 | if (id >= self.msf.streams.len) | 394 | if (id >= self.msf.streams.len) return null; |
| 408 | return null; | | |
| 409 | return &self.msf.streams[id]; | 395 | return &self.msf.streams[id]; |
| 410 | } | 396 | } |
| 411 | | 397 | |
| ... | @@ -419,17 +405,14 @@ const Msf = struct { | ... | @@ -419,17 +405,14 @@ const Msf = struct { |
| 419 | directory: MsfStream, | 405 | directory: MsfStream, |
| 420 | streams: []MsfStream, | 406 | streams: []MsfStream, |
| 421 | | 407 | |
| 422 | fn init(allocator: Allocator, file: File) !Msf { | 408 | fn init(gpa: Allocator, file_reader: *File.Reader) !Msf { |
| 423 | const in = file.deprecatedReader(); | 409 | const superblock = try file_reader.interface.takeStruct(pdb.SuperBlock, .little); |
| 424 | | | |
| 425 | const superblock = try in.readStruct(pdb.SuperBlock); | | |
| 426 | | 410 | |
| 427 | // Sanity checks | | |
| 428 | if (!std.mem.eql(u8, &superblock.file_magic, pdb.SuperBlock.expect_magic)) | 411 | if (!std.mem.eql(u8, &superblock.file_magic, pdb.SuperBlock.expect_magic)) |
| 429 | return error.InvalidDebugInfo; | 412 | return error.InvalidDebugInfo; |
| 430 | if (superblock.free_block_map_block != 1 and superblock.free_block_map_block != 2) | 413 | if (superblock.free_block_map_block != 1 and superblock.free_block_map_block != 2) |
| 431 | return error.InvalidDebugInfo; | 414 | return error.InvalidDebugInfo; |
| 432 | const file_len = try file.getEndPos(); | 415 | const file_len = try file_reader.getSize(); |
| 433 | if (superblock.num_blocks * superblock.block_size != file_len) | 416 | if (superblock.num_blocks * superblock.block_size != file_len) |
| 434 | return error.InvalidDebugInfo; | 417 | return error.InvalidDebugInfo; |
| 435 | switch (superblock.block_size) { | 418 | switch (superblock.block_size) { |
| ... | @@ -442,163 +425,182 @@ const Msf = struct { | ... | @@ -442,163 +425,182 @@ const Msf = struct { |
| 442 | if (dir_block_count > superblock.block_size / @sizeOf(u32)) | 425 | if (dir_block_count > superblock.block_size / @sizeOf(u32)) |
| 443 | return error.UnhandledBigDirectoryStream; // cf. BlockMapAddr comment. | 426 | return error.UnhandledBigDirectoryStream; // cf. BlockMapAddr comment. |
| 444 | | 427 | |
| 445 | try file.seekTo(superblock.block_size * superblock.block_map_addr); | 428 | try file_reader.seekTo(superblock.block_size * superblock.block_map_addr); |
| 446 | const dir_blocks = try allocator.alloc(u32, dir_block_count); | 429 | const dir_blocks = try gpa.alloc(u32, dir_block_count); |
| 447 | for (dir_blocks) |*b| { | 430 | for (dir_blocks) |*b| { |
| 448 | b.* = try in.readInt(u32, .little); | 431 | b.* = try file_reader.interface.takeInt(u32, .little); |
| 449 | } | 432 | } |
| 450 | var directory = MsfStream.init( | 433 | var directory_buffer: [64]u8 = undefined; |
| 451 | superblock.block_size, | 434 | var directory = MsfStream.init(superblock.block_size, file_reader, dir_blocks, &directory_buffer); |
| 452 | file, | | |
| 453 | dir_blocks, | | |
| 454 | ); | | |
| 455 | | 435 | |
| 456 | const begin = directory.pos; | 436 | const begin = directory.logicalPos(); |
| 457 | const stream_count = try directory.reader().readInt(u32, .little); | 437 | const stream_count = try directory.interface.takeInt(u32, .little); |
| 458 | const stream_sizes = try allocator.alloc(u32, stream_count); | 438 | const stream_sizes = try gpa.alloc(u32, stream_count); |
| 459 | defer allocator.free(stream_sizes); | 439 | defer gpa.free(stream_sizes); |
| 460 | | 440 | |
| 461 | // Microsoft's implementation uses @as(u32, -1) for inexistent streams. | 441 | // Microsoft's implementation uses @as(u32, -1) for inexistent streams. |
| 462 | // These streams are not used, but still participate in the file | 442 | // These streams are not used, but still participate in the file |
| 463 | // and must be taken into account when resolving stream indices. | 443 | // and must be taken into account when resolving stream indices. |
| 464 | const Nil = 0xFFFFFFFF; | 444 | const nil_size = 0xFFFFFFFF; |
| 465 | for (stream_sizes) |*s| { | 445 | for (stream_sizes) |*s| { |
| 466 | const size = try directory.reader().readInt(u32, .little); | 446 | const size = try directory.interface.takeInt(u32, .little); |
| 467 | s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.block_size); | 447 | s.* = if (size == nil_size) 0 else blockCountFromSize(size, superblock.block_size); |
| 468 | } | 448 | } |
| 469 | | 449 | |
| 470 | const streams = try allocator.alloc(MsfStream, stream_count); | 450 | const streams = try gpa.alloc(MsfStream, stream_count); |
| | 451 | errdefer gpa.free(streams); |
| | 452 | |
| 471 | for (streams, 0..) |*stream, i| { | 453 | for (streams, 0..) |*stream, i| { |
| 472 | const size = stream_sizes[i]; | 454 | const size = stream_sizes[i]; |
| 473 | if (size == 0) { | 455 | if (size == 0) { |
| 474 | stream.* = MsfStream{ | 456 | stream.* = .empty; |
| 475 | .blocks = &[_]u32{}, | | |
| 476 | }; | | |
| 477 | } else { | 457 | } else { |
| 478 | var blocks = try allocator.alloc(u32, size); | 458 | const blocks = try gpa.alloc(u32, size); |
| 479 | var j: u32 = 0; | 459 | errdefer gpa.free(blocks); |
| 480 | while (j < size) : (j += 1) { | 460 | for (blocks) |*block| { |
| 481 | const block_id = try directory.reader().readInt(u32, .little); | 461 | const block_id = try directory.interface.takeInt(u32, .little); |
| 482 | const n = (block_id % superblock.block_size); | 462 | const n = (block_id % superblock.block_size); |
| 483 | // 0 is for pdb.SuperBlock, 1 and 2 for FPMs. | 463 | // 0 is for pdb.SuperBlock, 1 and 2 for FPMs. |
| 484 | if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.block_size > file_len) | 464 | if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.block_size > file_len) |
| 485 | return error.InvalidBlockIndex; | 465 | return error.InvalidBlockIndex; |
| 486 | blocks[j] = block_id; | 466 | block.* = block_id; |
| 487 | } | 467 | } |
| 488 | | 468 | const buffer = try gpa.alloc(u8, 64); |
| 489 | stream.* = MsfStream.init( | 469 | errdefer gpa.free(buffer); |
| 490 | superblock.block_size, | 470 | stream.* = .init(superblock.block_size, file_reader, blocks, buffer); |
| 491 | file, | | |
| 492 | blocks, | | |
| 493 | ); | | |
| 494 | } | 471 | } |
| 495 | } | 472 | } |
| 496 | | 473 | |
| 497 | const end = directory.pos; | 474 | const end = directory.logicalPos(); |
| 498 | if (end - begin != superblock.num_directory_bytes) | 475 | if (end - begin != superblock.num_directory_bytes) |
| 499 | return error.InvalidStreamDirectory; | 476 | return error.InvalidStreamDirectory; |
| 500 | | 477 | |
| 501 | return Msf{ | 478 | return .{ |
| 502 | .directory = directory, | 479 | .directory = directory, |
| 503 | .streams = streams, | 480 | .streams = streams, |
| 504 | }; | 481 | }; |
| 505 | } | 482 | } |
| 506 | | 483 | |
| 507 | fn deinit(self: *Msf, allocator: Allocator) void { | 484 | fn deinit(self: *Msf, gpa: Allocator) void { |
| 508 | allocator.free(self.directory.blocks); | 485 | gpa.free(self.directory.blocks); |
| 509 | for (self.streams) |*stream| { | 486 | for (self.streams) |*stream| { |
| 510 | allocator.free(stream.blocks); | 487 | gpa.free(stream.interface.buffer); |
| | 488 | gpa.free(stream.blocks); |
| 511 | } | 489 | } |
| 512 | allocator.free(self.streams); | 490 | gpa.free(self.streams); |
| 513 | } | 491 | } |
| 514 | }; | 492 | }; |
| 515 | | 493 | |
| 516 | const MsfStream = struct { | 494 | const MsfStream = struct { |
| 517 | in_file: File = undefined, | 495 | file_reader: *File.Reader, |
| 518 | pos: u64 = undefined, | 496 | next_read_pos: u64, |
| 519 | blocks: []u32 = undefined, | 497 | blocks: []u32, |
| 520 | block_size: u32 = undefined, | 498 | block_size: u32, |
| 521 | | 499 | interface: std.Io.Reader, |
| 522 | pub const Error = @typeInfo(@typeInfo(@TypeOf(read)).@"fn".return_type.?).error_union.error_set; | 500 | err: ?Error, |
| | 501 | |
| | 502 | const Error = File.Reader.SeekError; |
| | 503 | |
| | 504 | const empty: MsfStream = .{ |
| | 505 | .file_reader = undefined, |
| | 506 | .next_read_pos = 0, |
| | 507 | .blocks = &.{}, |
| | 508 | .block_size = undefined, |
| | 509 | .interface = .ending_instance, |
| | 510 | .err = null, |
| | 511 | }; |
| 523 | | 512 | |
| 524 | fn init(block_size: u32, file: File, blocks: []u32) MsfStream { | 513 | fn init(block_size: u32, file_reader: *File.Reader, blocks: []u32, buffer: []u8) MsfStream { |
| 525 | const stream = MsfStream{ | 514 | return .{ |
| 526 | .in_file = file, | 515 | .file_reader = file_reader, |
| 527 | .pos = 0, | 516 | .next_read_pos = 0, |
| 528 | .blocks = blocks, | 517 | .blocks = blocks, |
| 529 | .block_size = block_size, | 518 | .block_size = block_size, |
| | 519 | .interface = .{ |
| | 520 | .vtable = &.{ .stream = stream }, |
| | 521 | .buffer = buffer, |
| | 522 | .seek = 0, |
| | 523 | .end = 0, |
| | 524 | }, |
| | 525 | .err = null, |
| 530 | }; | 526 | }; |
| 531 | | | |
| 532 | return stream; | | |
| 533 | } | 527 | } |
| 534 | | 528 | |
| 535 | fn read(self: *MsfStream, buffer: []u8) !usize { | 529 | fn stream(r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { |
| 536 | var block_id = @as(usize, @intCast(self.pos / self.block_size)); | 530 | const ms: *MsfStream = @alignCast(@fieldParentPtr("interface", r)); |
| 537 | if (block_id >= self.blocks.len) return 0; // End of Stream | | |
| 538 | var block = self.blocks[block_id]; | | |
| 539 | var offset = self.pos % self.block_size; | | |
| 540 | | 531 | |
| 541 | try self.in_file.seekTo(block * self.block_size + offset); | 532 | var block_id: usize = @intCast(ms.next_read_pos / ms.block_size); |
| 542 | const in = self.in_file.deprecatedReader(); | 533 | if (block_id >= ms.blocks.len) return error.EndOfStream; |
| | 534 | var block = ms.blocks[block_id]; |
| | 535 | var offset = ms.next_read_pos % ms.block_size; |
| 543 | | 536 | |
| 544 | var size: usize = 0; | 537 | ms.file_reader.seekTo(block * ms.block_size + offset) catch |err| { |
| 545 | var rem_buffer = buffer; | 538 | ms.err = err; |
| 546 | while (size < buffer.len) { | 539 | return error.ReadFailed; |
| 547 | const size_to_read = @min(self.block_size - offset, rem_buffer.len); | 540 | }; |
| 548 | size += try in.read(rem_buffer[0..size_to_read]); | 541 | |
| 549 | rem_buffer = buffer[size..]; | 542 | var remaining = @intFromEnum(limit); |
| 550 | offset += size_to_read; | 543 | while (remaining != 0) { |
| | 544 | const stream_len: usize = @min(remaining, ms.block_size - offset); |
| | 545 | const n = try ms.file_reader.interface.stream(w, .limited(stream_len)); |
| | 546 | remaining -= n; |
| | 547 | offset += n; |
| 551 | | 548 | |
| 552 | // If we're at the end of a block, go to the next one. | 549 | // If we're at the end of a block, go to the next one. |
| 553 | if (offset == self.block_size) { | 550 | if (offset == ms.block_size) { |
| 554 | offset = 0; | 551 | offset = 0; |
| 555 | block_id += 1; | 552 | block_id += 1; |
| 556 | if (block_id >= self.blocks.len) break; // End of Stream | 553 | if (block_id >= ms.blocks.len) break; // End of Stream |
| 557 | block = self.blocks[block_id]; | 554 | block = ms.blocks[block_id]; |
| 558 | try self.in_file.seekTo(block * self.block_size); | 555 | ms.file_reader.seekTo(block * ms.block_size) catch |err| { |
| | 556 | ms.err = err; |
| | 557 | return error.ReadFailed; |
| | 558 | }; |
| 559 | } | 559 | } |
| 560 | } | 560 | } |
| 561 | | 561 | |
| 562 | self.pos += buffer.len; | 562 | const total = @intFromEnum(limit) - remaining; |
| 563 | return buffer.len; | 563 | ms.next_read_pos += total; |
| | 564 | return total; |
| 564 | } | 565 | } |
| 565 | | 566 | |
| 566 | pub fn seekBy(self: *MsfStream, len: i64) !void { | 567 | pub fn logicalPos(ms: *const MsfStream) u64 { |
| 567 | self.pos = @as(u64, @intCast(@as(i64, @intCast(self.pos)) + len)); | 568 | return ms.next_read_pos - ms.interface.bufferedLen(); |
| 568 | if (self.pos >= self.blocks.len * self.block_size) | | |
| 569 | return error.EOF; | | |
| 570 | } | 569 | } |
| 571 | | 570 | |
| 572 | pub fn seekTo(self: *MsfStream, len: u64) !void { | 571 | pub fn seekBy(ms: *MsfStream, len: i64) !void { |
| 573 | self.pos = len; | 572 | ms.next_read_pos = @as(u64, @intCast(@as(i64, @intCast(ms.logicalPos())) + len)); |
| 574 | if (self.pos >= self.blocks.len * self.block_size) | 573 | if (ms.next_read_pos >= ms.blocks.len * ms.block_size) return error.EOF; |
| 575 | return error.EOF; | 574 | ms.interface.tossBuffered(); |
| 576 | } | 575 | } |
| 577 | | 576 | |
| 578 | fn getSize(self: *const MsfStream) u64 { | 577 | pub fn seekTo(ms: *MsfStream, len: u64) !void { |
| 579 | return self.blocks.len * self.block_size; | 578 | ms.next_read_pos = len; |
| | 579 | if (ms.next_read_pos >= ms.blocks.len * ms.block_size) return error.EOF; |
| | 580 | ms.interface.tossBuffered(); |
| 580 | } | 581 | } |
| 581 | | 582 | |
| 582 | fn getFilePos(self: MsfStream) u64 { | 583 | fn getSize(ms: *const MsfStream) u64 { |
| 583 | const block_id = self.pos / self.block_size; | 584 | return ms.blocks.len * ms.block_size; |
| 584 | const block = self.blocks[block_id]; | | |
| 585 | const offset = self.pos % self.block_size; | | |
| 586 | | | |
| 587 | return block * self.block_size + offset; | | |
| 588 | } | 585 | } |
| 589 | | 586 | |
| 590 | pub fn reader(self: *MsfStream) std.io.GenericReader(*MsfStream, Error, read) { | 587 | fn getFilePos(ms: *const MsfStream) u64 { |
| 591 | return .{ .context = self }; | 588 | const pos = ms.logicalPos(); |
| | 589 | const block_id = pos / ms.block_size; |
| | 590 | const block = ms.blocks[block_id]; |
| | 591 | const offset = pos % ms.block_size; |
| | 592 | |
| | 593 | return block * ms.block_size + offset; |
| 592 | } | 594 | } |
| 593 | }; | 595 | }; |
| 594 | | 596 | |
| 595 | fn readSparseBitVector(stream: anytype, allocator: Allocator) ![]u32 { | 597 | fn readSparseBitVector(reader: *std.Io.Reader, allocator: Allocator) ![]u32 { |
| 596 | const num_words = try stream.readInt(u32, .little); | 598 | const num_words = try reader.takeInt(u32, .little); |
| 597 | var list = std.array_list.Managed(u32).init(allocator); | 599 | var list = std.array_list.Managed(u32).init(allocator); |
| 598 | errdefer list.deinit(); | 600 | errdefer list.deinit(); |
| 599 | var word_i: u32 = 0; | 601 | var word_i: u32 = 0; |
| 600 | while (word_i != num_words) : (word_i += 1) { | 602 | while (word_i != num_words) : (word_i += 1) { |
| 601 | const word = try stream.readInt(u32, .little); | 603 | const word = try reader.takeInt(u32, .little); |
| 602 | var bit_i: u5 = 0; | 604 | var bit_i: u5 = 0; |
| 603 | while (true) : (bit_i += 1) { | 605 | while (true) : (bit_i += 1) { |
| 604 | if (word & (@as(u32, 1) << bit_i) != 0) { | 606 | if (word & (@as(u32, 1) << bit_i) != 0) { |