authorgravatar for sahnvour@gmail.comSahnvour <sahnvour@gmail.com> 2018-07-21 20:30:11+02:00
committergravatar for sahnvour@gmail.comSahnvour <sahnvour@gmail.com> 2018-07-21 20:30:11+02:00
log2ec9a11646c792a046b4601e0b99f8e182416a6c
treef7fa33a35d73ce246ca4423508e35c8012946293
parentf47655eb6d42590a89f177e7354c998a8d88582d

Very much WIP base implementation for #721.

Currently does: - read COFF executable file - locate and load corresponding .pdb file - expose .pdb content as streams (PDB format)

7 files changed, 563 insertions(+), 3 deletions(-)

CMakeLists.txt+2
......@@ -427,6 +427,7 @@ set(ZIG_STD_FILES
427427 "c/index.zig"
428428 "c/linux.zig"
429429 "c/windows.zig"
430 "coff.zig"
430431 "crypto/blake2.zig"
431432 "crypto/hmac.zig"
432433 "crypto/index.zig"
......@@ -544,6 +545,7 @@ set(ZIG_STD_FILES
544545 "os/windows/index.zig"
545546 "os/windows/util.zig"
546547 "os/zen.zig"
548 "pdb.zig"
547549 "rand/index.zig"
548550 "rand/ziggurat.zig"
549551 "segmented_list.zig"
std/coff.zig created+238
......@@ -0,0 +1,238 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const mem = std.mem;
5const os = std.os;
6
7const ArrayList = std.ArrayList;
8
9// CoffHeader.machine values
10// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
11const IMAGE_FILE_MACHINE_I386 = 0x014c;
12const IMAGE_FILE_MACHINE_IA64 = 0x0200;
13const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
14
15// OptionalHeader.magic values
16// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
17const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
18const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
19
20const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
21const DEBUG_DIRECTORY = 6;
22
23pub const CoffError = error {
24 InvalidPEMagic,
25 InvalidPEHeader,
26 InvalidMachine,
27 MissingCoffSection,
28};
29
30pub const Coff = struct {
31 in_file: os.File,
32 allocator: *mem.Allocator,
33
34 coff_header: CoffHeader,
35 pe_header: OptionalHeader,
36 sections: ArrayList(Section),
37
38 guid: [16]u8,
39 age: u32,
40
41 pub fn loadHeader(self: *Coff) !void {
42 const pe_pointer_offset = 0x3C;
43
44 var file_stream = io.FileInStream.init(&self.in_file);
45 const in = &file_stream.stream;
46
47 var magic: [2]u8 = undefined;
48 try in.readNoEof(magic[0..]);
49 if (!mem.eql(u8, magic, "MZ"))
50 return error.InvalidPEMagic;
51
52 // Seek to PE File Header (coff header)
53 try self.in_file.seekTo(pe_pointer_offset);
54 const pe_magic_offset = try in.readIntLe(u32);
55 try self.in_file.seekTo(pe_magic_offset);
56
57 var pe_header_magic: [4]u8 = undefined;
58 try in.readNoEof(pe_header_magic[0..]);
59 if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0}))
60 return error.InvalidPEHeader;
61
62 self.coff_header = CoffHeader {
63 .machine = try in.readIntLe(u16),
64 .number_of_sections = try in.readIntLe(u16),
65 .timedate_stamp = try in.readIntLe(u32),
66 .pointer_to_symbol_table = try in.readIntLe(u32),
67 .number_of_symbols = try in.readIntLe(u32),
68 .size_of_optional_header = try in.readIntLe(u16),
69 .characteristics = try in.readIntLe(u16),
70 };
71
72 switch (self.coff_header.machine) {
73 IMAGE_FILE_MACHINE_I386,
74 IMAGE_FILE_MACHINE_AMD64,
75 IMAGE_FILE_MACHINE_IA64
76 => {},
77 else => return error.InvalidMachine,
78 }
79
80 try self.loadOptionalHeader(&file_stream);
81 }
82
83 fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void {
84 const in = &file_stream.stream;
85 self.pe_header.magic = try in.readIntLe(u16);
86 std.debug.warn("reading pe optional\n");
87 // For now we're only interested in finding the reference to the .pdb,
88 // so we'll skip most of this header, which size is different in 32
89 // 64 bits by the way.
90 var skip_size: u16 = undefined;
91 if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
92 skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
93 }
94 else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
95 skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
96 }
97 else
98 return error.InvalidPEMagic;
99
100 std.debug.warn("skipping {}\n", skip_size);
101 try self.in_file.seekForward(skip_size);
102
103 const number_of_rva_and_sizes = try in.readIntLe(u32);
104 //std.debug.warn("indicating {} data dirs\n", number_of_rva_and_sizes);
105 if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
106 return error.InvalidPEHeader;
107
108 for (self.pe_header.data_directory) |*data_dir| {
109 data_dir.* = OptionalHeader.DataDirectory {
110 .virtual_address = try in.readIntLe(u32),
111 .size = try in.readIntLe(u32),
112 };
113 //std.debug.warn("data_dir @ {x}, size {}\n", data_dir.virtual_address, data_dir.size);
114 }
115 std.debug.warn("loaded data directories\n");
116 }
117
118 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
119 try self.loadSections();
120 const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header;
121
122 // The linker puts a chunk that contains the .pdb path right after the
123 // debug_directory.
124 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
125 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
126 std.debug.warn("file offset {x}\n", file_offset);
127 try self.in_file.seekTo(file_offset + debug_dir.size);
128
129 var file_stream = io.FileInStream.init(&self.in_file);
130 const in = &file_stream.stream;
131
132 var cv_signature: [4]u8 = undefined; // CodeView signature
133 try in.readNoEof(cv_signature[0..]);
134 // 'RSDS' indicates PDB70 format, used by lld.
135 if (!mem.eql(u8, cv_signature, "RSDS"))
136 return error.InvalidPEMagic;
137 std.debug.warn("cv_signature {}\n", cv_signature);
138 try in.readNoEof(self.guid[0..]);
139 self.age = try in.readIntLe(u32);
140
141 // Finally read the null-terminated string.
142 var byte = try in.readByte();
143 var i: usize = 0;
144 while (byte != 0 and i < buffer.len) : (i += 1) {
145 buffer[i] = byte;
146 byte = try in.readByte();
147 }
148
149 if (byte != 0 and i == buffer.len)
150 return error.NameTooLong;
151
152 return i;
153 }
154
155 pub fn loadSections(self: *Coff) !void {
156 if (self.sections.len != 0)
157 return;
158
159 self.sections = ArrayList(Section).init(self.allocator);
160
161 var file_stream = io.FileInStream.init(&self.in_file);
162 const in = &file_stream.stream;
163
164 var name: [8]u8 = undefined;
165
166 var i: u16 = 0;
167 while (i < self.coff_header.number_of_sections) : (i += 1) {
168 try in.readNoEof(name[0..]);
169 try self.sections.append(Section {
170 .header = SectionHeader {
171 .name = name,
172 .misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) },
173 .virtual_address = try in.readIntLe(u32),
174 .size_of_raw_data = try in.readIntLe(u32),
175 .pointer_to_raw_data = try in.readIntLe(u32),
176 .pointer_to_relocations = try in.readIntLe(u32),
177 .pointer_to_line_numbers = try in.readIntLe(u32),
178 .number_of_relocations = try in.readIntLe(u16),
179 .number_of_line_numbers = try in.readIntLe(u16),
180 .characteristics = try in.readIntLe(u32),
181 },
182 });
183 }
184 std.debug.warn("loaded {} sections\n", self.coff_header.number_of_sections);
185 }
186
187 pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
188 for (self.sections.toSlice()) |*sec| {
189 if (mem.eql(u8, sec.header.name[0..name.len], name)) {
190 return sec;
191 }
192 }
193 return null;
194 }
195
196};
197
198const CoffHeader = struct {
199 machine: u16,
200 number_of_sections: u16,
201 timedate_stamp: u32,
202 pointer_to_symbol_table: u32,
203 number_of_symbols: u32,
204 size_of_optional_header: u16,
205 characteristics: u16
206};
207
208const OptionalHeader = struct {
209 const DataDirectory = struct {
210 virtual_address: u32,
211 size: u32
212 };
213
214 magic: u16,
215 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
216};
217
218const Section = struct {
219 header: SectionHeader,
220};
221
222const SectionHeader = struct {
223 const Misc = union {
224 physical_address: u32,
225 virtual_size: u32
226 };
227
228 name: [8]u8,
229 misc: Misc,
230 virtual_address: u32,
231 size_of_raw_data: u32,
232 pointer_to_raw_data: u32,
233 pointer_to_relocations: u32,
234 pointer_to_line_numbers: u32,
235 number_of_relocations: u16,
236 number_of_line_numbers: u16,
237 characteristics: u32,
238};
\ No newline at end of file
std/debug/index.zig+45-2
......@@ -6,6 +6,9 @@ const os = std.os;
66const elf = std.elf;
77const DW = std.dwarf;
88const macho = std.macho;
9const coff = std.coff;
10const pdb = std.pdb;
11const windows = os.windows;
912const ArrayList = std.ArrayList;
1013const builtin = @import("builtin");
1114
......@@ -197,7 +200,13 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us
197200 const ptr_hex = "0x{x}";
198201
199202 switch (builtin.os) {
200 builtin.Os.windows => return error.UnsupportedDebugInfo,
203 builtin.Os.windows => {
204 const base_address = @ptrToInt(windows.GetModuleHandleA(null)); // returned HMODULE points to our executable file in memory
205 const relative_address = address - base_address;
206 std.debug.warn("{x} - {x} => {x}\n", address, base_address, relative_address);
207 try debug_info.pdb.getSourceLine(relative_address);
208 return error.UnsupportedDebugInfo;
209 },
201210 builtin.Os.macosx => {
202211 // TODO(bnoordhuis) It's theoretically possible to obtain the
203212 // compilation unit from the symbtab but it's not that useful
......@@ -288,7 +297,38 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
288297 return st;
289298 },
290299 builtin.ObjectFormat.coff => {
291 return error.TodoSupportCoffDebugInfo;
300 var coff_file: coff.Coff = undefined;
301 coff_file.in_file = try os.openSelfExe();
302 coff_file.allocator = allocator;
303 defer coff_file.in_file.close();
304
305 try coff_file.loadHeader();
306
307 var path: [windows.MAX_PATH]u8 = undefined;
308 const len = try coff_file.getPdbPath(path[0..]);
309 std.debug.warn("pdb path {}\n", path[0..len]);
310
311 const st = try allocator.create(ElfStackTrace);
312 errdefer allocator.destroy(st);
313 st.* = ElfStackTrace {
314 .pdb = undefined,
315 };
316
317 try st.pdb.openFile(allocator, path[0..len]);
318
319 var pdb_stream = st.pdb.getStream(pdb.StreamType.Pdb) orelse return error.CorruptedFile;
320 std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos());
321 const version = try pdb_stream.stream.readIntLe(u32);
322 const signature = try pdb_stream.stream.readIntLe(u32);
323 const age = try pdb_stream.stream.readIntLe(u32);
324 var guid: [16]u8 = undefined;
325 try pdb_stream.stream.readNoEof(guid[0..]);
326 if (!mem.eql(u8, coff_file.guid, guid) or coff_file.age != age)
327 return error.CorruptedFile;
328 std.debug.warn("v {} s {} a {}\n", version, signature, age);
329 // We validated the executable and pdb match.
330
331 return st;
292332 },
293333 builtin.ObjectFormat.wasm => {
294334 return error.TodoSupportCOFFDebugInfo;
......@@ -339,6 +379,9 @@ pub const ElfStackTrace = switch (builtin.os) {
339379 self.symbol_table.deinit();
340380 }
341381 },
382 builtin.Os.windows => struct {
383 pdb: pdb.Pdb,
384 },
342385 else => struct {
343386 self_exe_file: os.File,
344387 elf: elf.Elf,
std/index.zig+4
......@@ -13,6 +13,7 @@ pub const atomic = @import("atomic/index.zig");
1313pub const base64 = @import("base64.zig");
1414pub const build = @import("build.zig");
1515pub const c = @import("c/index.zig");
16pub const coff = @import("coff.zig");
1617pub const crypto = @import("crypto/index.zig");
1718pub const cstr = @import("cstr.zig");
1819pub const debug = @import("debug/index.zig");
......@@ -30,6 +31,7 @@ pub const math = @import("math/index.zig");
3031pub const mem = @import("mem.zig");
3132pub const net = @import("net.zig");
3233pub const os = @import("os/index.zig");
34pub const pdb = @import("pdb.zig");
3335pub const rand = @import("rand/index.zig");
3436pub const sort = @import("sort.zig");
3537pub const unicode = @import("unicode.zig");
......@@ -49,6 +51,7 @@ test "std" {
4951 _ = @import("base64.zig");
5052 _ = @import("build.zig");
5153 _ = @import("c/index.zig");
54 _ = @import("coff.zig");
5255 _ = @import("crypto/index.zig");
5356 _ = @import("cstr.zig");
5457 _ = @import("debug/index.zig");
......@@ -67,6 +70,7 @@ test "std" {
6770 _ = @import("heap.zig");
6871 _ = @import("os/index.zig");
6972 _ = @import("rand/index.zig");
73 _ = @import("pdb.zig");
7074 _ = @import("sort.zig");
7175 _ = @import("unicode.zig");
7276 _ = @import("zig/index.zig");
std/os/index.zig+7-1
......@@ -1896,13 +1896,19 @@ pub fn openSelfExe() !os.File {
18961896 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
18971897 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
18981898 },
1899 Os.windows => {
1900 var fixed_buffer_mem: [windows.MAX_PATH * 2]u8 = undefined;
1901 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1902 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
1903 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
1904 },
18991905 else => @compileError("Unsupported OS"),
19001906 }
19011907}
19021908
19031909test "openSelfExe" {
19041910 switch (builtin.os) {
1905 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
1911 Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(),
19061912 else => return, // Unsupported OS.
19071913 }
19081914}
std/os/windows/index.zig+2
......@@ -105,6 +105,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
105105 dwFlags: DWORD,
106106) DWORD;
107107
108pub extern "kernel32" stdcallcc fn GetModuleHandleA(lpModuleName: ?LPCSTR) HMODULE;
109
108110pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
109111
110112pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
std/pdb.zig created+265
......@@ -0,0 +1,265 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const math = std.math;
5const mem = std.mem;
6const os = std.os;
7const warn = std.debug.warn;
8
9const ArrayList = std.ArrayList;
10
11pub const PdbError = error {
12 InvalidPdbMagic,
13 CorruptedFile,
14};
15
16pub const StreamType = enum(u16) {
17 Pdb = 1,
18 Tpi = 2,
19 Dbi = 3,
20 Ipi = 4,
21};
22
23pub const Pdb = struct {
24 in_file: os.File,
25 allocator: *mem.Allocator,
26
27 msf: Msf,
28
29 pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void {
30 self.in_file = try os.File.openRead(allocator, file_name[0..]);
31 self.allocator = allocator;
32
33 try self.msf.openFile(allocator, &self.in_file);
34 }
35
36 pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream {
37 const id = u16(stream);
38 if (id < self.msf.streams.len)
39 return &self.msf.streams.items[id];
40 return null;
41 }
42
43 pub fn getSourceLine(self: *Pdb, address: usize) !void {
44 const dbi = self.getStream(StreamType.Dbi) orelse return error.CorruptedFile;
45
46 // Dbi Header
47 try dbi.seekForward(@sizeOf(u32) * 3 + @sizeOf(u16) * 6);
48 warn("dbi stream at {} (file offset)\n", dbi.getFilePos());
49 const module_info_size = try dbi.stream.readIntLe(u32);
50 const section_contribution_size = try dbi.stream.readIntLe(u32);
51 const section_map_size = try dbi.stream.readIntLe(u32);
52 const source_info_size = try dbi.stream.readIntLe(u32);
53 warn("module_info_size: {}\n", module_info_size);
54 warn("section_contribution_size: {}\n", section_contribution_size);
55 warn("section_map_size: {}\n", section_map_size);
56 warn("source_info_size: {}\n", source_info_size);
57 try dbi.seekForward(@sizeOf(u32) * 5 + @sizeOf(u16) * 2);
58 warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos());
59
60 // Module Info Substream
61 try dbi.seekForward(@sizeOf(u32) + @sizeOf(u16) + @sizeOf(u8) * 2);
62 const offset = try dbi.stream.readIntLe(u32);
63 const size = try dbi.stream.readIntLe(u32);
64 try dbi.seekForward(@sizeOf(u32));
65 const module_index = try dbi.stream.readIntLe(u16);
66 warn("module {} of size {} at {}\n", module_index, size, offset);
67
68 // TODO: locate corresponding source line information
69 }
70};
71
72// see https://llvm.org/docs/PDB/MsfFile.html
73const Msf = struct {
74 superblock: SuperBlock,
75 directory: MsfStream,
76 streams: ArrayList(MsfStream),
77
78 fn openFile(self: *Msf, allocator: *mem.Allocator, file: *os.File) !void {
79 var file_stream = io.FileInStream.init(file);
80 const in = &file_stream.stream;
81
82 var magic: SuperBlock.FileMagicBuffer = undefined;
83 try in.readNoEof(magic[0..]);
84 warn("magic: '{}'\n", magic);
85
86 if (!mem.eql(u8, magic, SuperBlock.FileMagic))
87 return error.InvalidPdbMagic;
88
89 self.superblock = SuperBlock {
90 .block_size = try in.readIntLe(u32),
91 .free_block_map_block = try in.readIntLe(u32),
92 .num_blocks = try in.readIntLe(u32),
93 .num_directory_bytes = try in.readIntLe(u32),
94 .unknown = try in.readIntLe(u32),
95 .block_map_addr = try in.readIntLe(u32),
96 };
97
98 switch (self.superblock.block_size) {
99 512, 1024, 2048, 4096 => {}, // llvm only uses 4096
100 else => return error.InvalidPdbMagic
101 }
102
103 if (self.superblock.fileSize() != try file.getEndPos())
104 return error.CorruptedFile; // Should always stand.
105
106 self.directory = try MsfStream.init(
107 self.superblock.block_size,
108 self.superblock.blocksOccupiedByDirectoryStream(),
109 self.superblock.blockMapAddr(),
110 file,
111 allocator
112 );
113
114 const stream_count = try self.directory.stream.readIntLe(u32);
115 warn("stream count {}\n", stream_count);
116
117 var stream_sizes = ArrayList(u32).init(allocator);
118 try stream_sizes.resize(stream_count);
119 for (stream_sizes.toSlice()) |*s| {
120 const size = try self.directory.stream.readIntLe(u32);
121 s.* = blockCountFromSize(size, self.superblock.block_size);
122 warn("stream {}B {} blocks\n", size, s.*);
123 }
124
125 self.streams = ArrayList(MsfStream).init(allocator);
126 try self.streams.resize(stream_count);
127 for (self.streams.toSlice()) |*ss, i| {
128 ss.* = try MsfStream.init(
129 self.superblock.block_size,
130 stream_sizes.items[i],
131 try file.getPos(), // We're reading the jagged array of block indices when creating streams so the file is always at the right position.
132 file,
133 allocator
134 );
135 }
136 }
137};
138
139fn blockCountFromSize(size: u32, block_size: u32) u32 {
140 return (size + block_size - 1) / block_size;
141}
142
143const SuperBlock = struct {
144 const FileMagic = "Microsoft C/C++ MSF 7.00\r\n" ++ []u8 { 0x1A, 'D', 'S', 0, 0, 0};
145 const FileMagicBuffer = @typeOf(FileMagic);
146
147 block_size: u32,
148 free_block_map_block: u32,
149 num_blocks: u32,
150 num_directory_bytes: u32,
151 unknown: u32,
152 block_map_addr: u32,
153
154 fn fileSize(self: *const SuperBlock) usize {
155 return self.num_blocks * self.block_size;
156 }
157
158 fn blockMapAddr(self: *const SuperBlock) usize {
159 return self.block_size * self.block_map_addr;
160 }
161
162 fn blocksOccupiedByDirectoryStream(self: *const SuperBlock) u32 {
163 return blockCountFromSize(self.num_directory_bytes, self.block_size);
164 }
165};
166
167const MsfStream = struct {
168 in_file: *os.File,
169 pos: usize,
170 blocks: ArrayList(u32),
171 block_size: u32,
172
173 fn init(block_size: u32, block_count: u32, pos: usize, file: *os.File, allocator: *mem.Allocator) !MsfStream {
174 var stream = MsfStream {
175 .in_file = file,
176 .pos = 0,
177 .blocks = ArrayList(u32).init(allocator),
178 .block_size = block_size,
179 .stream = Stream {
180 .readFn = readFn,
181 },
182 };
183
184 try stream.blocks.resize(block_count);
185
186 var file_stream = io.FileInStream.init(file);
187 const in = &file_stream.stream;
188 try file.seekTo(pos);
189
190 warn("stream with blocks");
191 var i: u32 = 0;
192 while (i < block_count) : (i += 1) {
193 stream.blocks.items[i] = try in.readIntLe(u32);
194 warn(" {}", stream.blocks.items[i]);
195 }
196 warn("\n");
197
198 return stream;
199 }
200
201 fn read(self: *MsfStream, buffer: []u8) !usize {
202 var block_id = self.pos / self.block_size;
203 var block = self.blocks.items[block_id];
204 var offset = self.pos % self.block_size;
205
206 try self.in_file.seekTo(block * self.block_size + offset);
207 var file_stream = io.FileInStream.init(self.in_file);
208 const in = &file_stream.stream;
209
210 var size: usize = 0;
211 for (buffer) |*byte| {
212 byte.* = try in.readByte();
213
214 offset += 1;
215 size += 1;
216
217 // If we're at the end of a block, go to the next one.
218 if (offset == self.block_size)
219 {
220 offset = 0;
221 block_id += 1;
222 block = self.blocks.items[block_id];
223 try self.in_file.seekTo(block * self.block_size);
224 }
225 }
226
227 self.pos += size;
228 return size;
229 }
230
231 fn seekForward(self: *MsfStream, len: usize) !void {
232 self.pos += len;
233 if (self.pos >= self.blocks.len * self.block_size)
234 return error.EOF;
235 }
236
237 fn seekTo(self: *MsfStream, len: usize) !void {
238 self.pos = len;
239 if (self.pos >= self.blocks.len * self.block_size)
240 return error.EOF;
241 }
242
243 fn getSize(self: *const MsfStream) usize {
244 return self.blocks.len * self.block_size;
245 }
246
247 fn getFilePos(self: *const MsfStream) usize {
248 const block_id = self.pos / self.block_size;
249 const block = self.blocks.items[block_id];
250 const offset = self.pos % self.block_size;
251
252 return block * self.block_size + offset;
253 }
254
255 /// Implementation of InStream trait for Pdb.MsfStream
256 pub const Error = @typeOf(read).ReturnType.ErrorSet;
257 pub const Stream = io.InStream(Error);
258
259 stream: Stream,
260
261 fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
262 const self = @fieldParentPtr(MsfStream, "stream", in_stream);
263 return self.read(buffer);
264 }
265};
\ No newline at end of file