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