authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-12-02 18:35:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-12-02 18:36:18-05:00
loga40d160a5ca7fe50680578541c40038e280272ce
treeb00fa8b07250e734b19c764bcc0ce7d0161a0373
parent6f5e7ee09c9269cfcc454cde88960987054ee031
signaturelock-open Commit is signed but in an unrecognized format.

introduce std.io.SeekableStream

Relevant #764 dwarf debug info is modified to use this instead of std.os.File directly to make it easier for bare metal projects to take advantage of debug info parsing

6 files changed, 228 insertions(+), 134 deletions(-)

CMakeLists.txt+1
...@@ -491,6 +491,7 @@ set(ZIG_STD_FILES...@@ -491,6 +491,7 @@ set(ZIG_STD_FILES
491 "heap.zig"491 "heap.zig"
492 "index.zig"492 "index.zig"
493 "io.zig"493 "io.zig"
494 "io/seekable_stream.zig"
494 "json.zig"495 "json.zig"
495 "lazy_init.zig"496 "lazy_init.zig"
496 "linked_list.zig"497 "linked_list.zig"
std/debug/index.zig+110-108
...@@ -284,7 +284,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres...@@ -284,7 +284,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres
284 const mod_index = for (di.sect_contribs) |sect_contrib| {284 const mod_index = for (di.sect_contribs) |sect_contrib| {
285 if (sect_contrib.Section > di.coff.sections.len) continue;285 if (sect_contrib.Section > di.coff.sections.len) continue;
286 // Remember that SectionContribEntry.Section is 1-based.286 // Remember that SectionContribEntry.Section is 1-based.
287 coff_section = &di.coff.sections.toSlice()[sect_contrib.Section-1];287 coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1];
288288
289 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;289 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
290 const vaddr_end = vaddr_start + sect_contrib.Size;290 const vaddr_end = vaddr_start + sect_contrib.Size;
...@@ -872,9 +872,14 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {...@@ -872,9 +872,14 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
872 return list.toOwnedSlice();872 return list.toOwnedSlice();
873}873}
874874
875fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {875pub fn openDwarfDebugInfo(
876 allocator: *mem.Allocator,
877 dwarf_seekable_stream: *DwarfSeekableStream,
878 dwarf_in_stream: *DwarfInStream,
879) !DebugInfo {
876 var di = DebugInfo{880 var di = DebugInfo{
877 .self_exe_file = undefined,881 .dwarf_seekable_stream = dwarf_seekable_stream,
882 .dwarf_in_stream = dwarf_in_stream,
878 .elf = undefined,883 .elf = undefined,
879 .debug_info = undefined,884 .debug_info = undefined,
880 .debug_abbrev = undefined,885 .debug_abbrev = undefined,
...@@ -884,10 +889,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {...@@ -884,10 +889,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
884 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),889 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
885 .compile_unit_list = ArrayList(CompileUnit).init(allocator),890 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
886 };891 };
887 di.self_exe_file = try os.openSelfExe();892 try di.elf.openStream(allocator, dwarf_seekable_stream, dwarf_in_stream);
888 errdefer di.self_exe_file.close();
889
890 try di.elf.openFile(allocator, di.self_exe_file);
891 errdefer di.elf.close();893 errdefer di.elf.close();
892894
893 di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;895 di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
...@@ -899,6 +901,27 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {...@@ -899,6 +901,27 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
899 return di;901 return di;
900}902}
901903
904fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
905 const S = struct {
906 var self_exe_file: os.File = undefined;
907 var self_exe_seekable_stream: os.File.SeekableStream = undefined;
908 var self_exe_in_stream: os.File.InStream = undefined;
909 };
910 S.self_exe_file = try os.openSelfExe();
911 errdefer S.self_exe_file.close();
912
913 S.self_exe_seekable_stream = S.self_exe_file.seekableStream();
914 S.self_exe_in_stream = S.self_exe_file.inStream();
915
916 return openDwarfDebugInfo(
917 allocator,
918 // TODO https://github.com/ziglang/zig/issues/764
919 @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream),
920 // TODO https://github.com/ziglang/zig/issues/764
921 @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream),
922 );
923}
924
902pub fn findElfSection(elf: *Elf, name: []const u8) ?*elf.Shdr {925pub fn findElfSection(elf: *Elf, name: []const u8) ?*elf.Shdr {
903 var file_stream = elf.in_file.inStream();926 var file_stream = elf.in_file.inStream();
904 const in = &file_stream.stream;927 const in = &file_stream.stream;
...@@ -1053,6 +1076,9 @@ const MachOFile = struct {...@@ -1053,6 +1076,9 @@ const MachOFile = struct {
1053 sect_debug_line: ?*const macho.section_64,1076 sect_debug_line: ?*const macho.section_64,
1054};1077};
10551078
1079pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
1080pub const DwarfInStream = io.InStream(anyerror);
1081
1056pub const DebugInfo = switch (builtin.os) {1082pub const DebugInfo = switch (builtin.os) {
1057 builtin.Os.macosx => struct {1083 builtin.Os.macosx => struct {
1058 symbols: []const MachoSymbol,1084 symbols: []const MachoSymbol,
...@@ -1077,7 +1103,8 @@ pub const DebugInfo = switch (builtin.os) {...@@ -1077,7 +1103,8 @@ pub const DebugInfo = switch (builtin.os) {
1077 modules: []Module,1103 modules: []Module,
1078 },1104 },
1079 builtin.Os.linux => struct {1105 builtin.Os.linux => struct {
1080 self_exe_file: os.File,1106 dwarf_seekable_stream: *DwarfSeekableStream,
1107 dwarf_in_stream: *DwarfInStream,
1081 elf: elf.Elf,1108 elf: elf.Elf,
1082 debug_info: *elf.SectionHeader,1109 debug_info: *elf.SectionHeader,
1083 debug_abbrev: *elf.SectionHeader,1110 debug_abbrev: *elf.SectionHeader,
...@@ -1092,13 +1119,10 @@ pub const DebugInfo = switch (builtin.os) {...@@ -1092,13 +1119,10 @@ pub const DebugInfo = switch (builtin.os) {
1092 }1119 }
10931120
1094 pub fn readString(self: *DebugInfo) ![]u8 {1121 pub fn readString(self: *DebugInfo) ![]u8 {
1095 var in_file_stream = self.self_exe_file.inStream();1122 return readStringRaw(self.allocator(), self.dwarf_in_stream);
1096 const in_stream = &in_file_stream.stream;
1097 return readStringRaw(self.allocator(), in_stream);
1098 }1123 }
10991124
1100 pub fn close(self: *DebugInfo) void {1125 pub fn close(self: *DebugInfo) void {
1101 self.self_exe_file.close();
1102 self.elf.close();1126 self.elf.close();
1103 }1127 }
1104 },1128 },
...@@ -1206,11 +1230,11 @@ const Die = struct {...@@ -1206,11 +1230,11 @@ const Die = struct {
1206 };1230 };
1207 }1231 }
12081232
1209 fn getAttrString(self: *const Die, st: *DebugInfo, id: u64) ![]u8 {1233 fn getAttrString(self: *const Die, di: *DebugInfo, id: u64) ![]u8 {
1210 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;1234 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
1211 return switch (form_value.*) {1235 return switch (form_value.*) {
1212 FormValue.String => |value| value,1236 FormValue.String => |value| value,
1213 FormValue.StrPtr => |offset| getString(st, offset),1237 FormValue.StrPtr => |offset| getString(di, offset),
1214 else => error.InvalidDebugInfo,1238 else => error.InvalidDebugInfo,
1215 };1239 };
1216 }1240 }
...@@ -1321,10 +1345,10 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {...@@ -1321,10 +1345,10 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
1321 return buf.toSlice();1345 return buf.toSlice();
1322}1346}
13231347
1324fn getString(st: *DebugInfo, offset: u64) ![]u8 {1348fn getString(di: *DebugInfo, offset: u64) ![]u8 {
1325 const pos = st.debug_str.offset + offset;1349 const pos = di.debug_str.offset + offset;
1326 try st.self_exe_file.seekTo(pos);1350 try di.dwarf_seekable_stream.seekTo(pos);
1327 return st.readString();1351 return di.readString();
1328}1352}
13291353
1330fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {1354fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
...@@ -1371,14 +1395,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type...@@ -1371,14 +1395,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type
1371 return parseFormValueRefLen(allocator, in_stream, block_len);1395 return parseFormValueRefLen(allocator, in_stream, block_len);
1372}1396}
13731397
1374const ParseFormValueError = error{1398fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
1375 EndOfStream,
1376 InvalidDebugInfo,
1377 EndOfFile,
1378 OutOfMemory,
1379} || std.os.File.ReadError;
1380
1381fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
1382 return switch (form_id) {1399 return switch (form_id) {
1383 DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },1400 DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
1384 DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),1401 DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
...@@ -1428,25 +1445,22 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64...@@ -1428,25 +1445,22 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
1428 };1445 };
1429}1446}
14301447
1431fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {1448fn parseAbbrevTable(di: *DebugInfo) !AbbrevTable {
1432 const in_file = st.self_exe_file;1449 var result = AbbrevTable.init(di.allocator());
1433 var in_file_stream = in_file.inStream();
1434 const in_stream = &in_file_stream.stream;
1435 var result = AbbrevTable.init(st.allocator());
1436 while (true) {1450 while (true) {
1437 const abbrev_code = try readULeb128(in_stream);1451 const abbrev_code = try readULeb128(di.dwarf_in_stream);
1438 if (abbrev_code == 0) return result;1452 if (abbrev_code == 0) return result;
1439 try result.append(AbbrevTableEntry{1453 try result.append(AbbrevTableEntry{
1440 .abbrev_code = abbrev_code,1454 .abbrev_code = abbrev_code,
1441 .tag_id = try readULeb128(in_stream),1455 .tag_id = try readULeb128(di.dwarf_in_stream),
1442 .has_children = (try in_stream.readByte()) == DW.CHILDREN_yes,1456 .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes,
1443 .attrs = ArrayList(AbbrevAttr).init(st.allocator()),1457 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
1444 });1458 });
1445 const attrs = &result.items[result.len - 1].attrs;1459 const attrs = &result.items[result.len - 1].attrs;
14461460
1447 while (true) {1461 while (true) {
1448 const attr_id = try readULeb128(in_stream);1462 const attr_id = try readULeb128(di.dwarf_in_stream);
1449 const form_id = try readULeb128(in_stream);1463 const form_id = try readULeb128(di.dwarf_in_stream);
1450 if (attr_id == 0 and form_id == 0) break;1464 if (attr_id == 0 and form_id == 0) break;
1451 try attrs.append(AbbrevAttr{1465 try attrs.append(AbbrevAttr{
1452 .attr_id = attr_id,1466 .attr_id = attr_id,
...@@ -1458,18 +1472,18 @@ fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {...@@ -1458,18 +1472,18 @@ fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {
14581472
1459/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,1473/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
1460/// seeks in the stream and parses it.1474/// seeks in the stream and parses it.
1461fn getAbbrevTable(st: *DebugInfo, abbrev_offset: u64) !*const AbbrevTable {1475fn getAbbrevTable(di: *DebugInfo, abbrev_offset: u64) !*const AbbrevTable {
1462 for (st.abbrev_table_list.toSlice()) |*header| {1476 for (di.abbrev_table_list.toSlice()) |*header| {
1463 if (header.offset == abbrev_offset) {1477 if (header.offset == abbrev_offset) {
1464 return &header.table;1478 return &header.table;
1465 }1479 }
1466 }1480 }
1467 try st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset);1481 try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset);
1468 try st.abbrev_table_list.append(AbbrevTableHeader{1482 try di.abbrev_table_list.append(AbbrevTableHeader{
1469 .offset = abbrev_offset,1483 .offset = abbrev_offset,
1470 .table = try parseAbbrevTable(st),1484 .table = try parseAbbrevTable(di),
1471 });1485 });
1472 return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table;1486 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
1473}1487}
14741488
1475fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {1489fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
...@@ -1479,23 +1493,20 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con...@@ -1479,23 +1493,20 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
1479 return null;1493 return null;
1480}1494}
14811495
1482fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {1496fn parseDie(di: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
1483 const in_file = st.self_exe_file;1497 const abbrev_code = try readULeb128(di.dwarf_in_stream);
1484 var in_file_stream = in_file.inStream();
1485 const in_stream = &in_file_stream.stream;
1486 const abbrev_code = try readULeb128(in_stream);
1487 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;1498 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
14881499
1489 var result = Die{1500 var result = Die{
1490 .tag_id = table_entry.tag_id,1501 .tag_id = table_entry.tag_id,
1491 .has_children = table_entry.has_children,1502 .has_children = table_entry.has_children,
1492 .attrs = ArrayList(Die.Attr).init(st.allocator()),1503 .attrs = ArrayList(Die.Attr).init(di.allocator()),
1493 };1504 };
1494 try result.attrs.resize(table_entry.attrs.len);1505 try result.attrs.resize(table_entry.attrs.len);
1495 for (table_entry.attrs.toSliceConst()) |attr, i| {1506 for (table_entry.attrs.toSliceConst()) |attr, i| {
1496 result.attrs.items[i] = Die.Attr{1507 result.attrs.items[i] = Die.Attr{
1497 .id = attr.attr_id,1508 .id = attr.attr_id,
1498 .value = try parseFormValue(st.allocator(), in_stream, attr.form_id, is_64),1509 .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64),
1499 };1510 };
1500 }1511 }
1501 return result;1512 return result;
...@@ -1702,19 +1713,15 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u...@@ -1702,19 +1713,15 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
1702fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {1713fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
1703 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);1714 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
17041715
1705 const in_file = di.self_exe_file;
1706 const debug_line_end = di.debug_line.offset + di.debug_line.size;1716 const debug_line_end = di.debug_line.offset + di.debug_line.size;
1707 var this_offset = di.debug_line.offset;1717 var this_offset = di.debug_line.offset;
1708 var this_index: usize = 0;1718 var this_index: usize = 0;
17091719
1710 var in_file_stream = in_file.inStream();
1711 const in_stream = &in_file_stream.stream;
1712
1713 while (this_offset < debug_line_end) : (this_index += 1) {1720 while (this_offset < debug_line_end) : (this_index += 1) {
1714 try in_file.seekTo(this_offset);1721 try di.dwarf_seekable_stream.seekTo(this_offset);
17151722
1716 var is_64: bool = undefined;1723 var is_64: bool = undefined;
1717 const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);1724 const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
1718 if (unit_length == 0) return error.MissingDebugInfo;1725 if (unit_length == 0) return error.MissingDebugInfo;
1719 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));1726 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
17201727
...@@ -1723,35 +1730,35 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1723,35 +1730,35 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1723 continue;1730 continue;
1724 }1731 }
17251732
1726 const version = try in_stream.readInt(di.elf.endian, u16);1733 const version = try di.dwarf_in_stream.readInt(di.elf.endian, u16);
1727 // TODO support 3 and 51734 // TODO support 3 and 5
1728 if (version != 2 and version != 4) return error.InvalidDebugInfo;1735 if (version != 2 and version != 4) return error.InvalidDebugInfo;
17291736
1730 const prologue_length = if (is_64) try in_stream.readInt(di.elf.endian, u64) else try in_stream.readInt(di.elf.endian, u32);1737 const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(di.elf.endian, u64) else try di.dwarf_in_stream.readInt(di.elf.endian, u32);
1731 const prog_start_offset = (try in_file.getPos()) + prologue_length;1738 const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
17321739
1733 const minimum_instruction_length = try in_stream.readByte();1740 const minimum_instruction_length = try di.dwarf_in_stream.readByte();
1734 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;1741 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
17351742
1736 if (version >= 4) {1743 if (version >= 4) {
1737 // maximum_operations_per_instruction1744 // maximum_operations_per_instruction
1738 _ = try in_stream.readByte();1745 _ = try di.dwarf_in_stream.readByte();
1739 }1746 }
17401747
1741 const default_is_stmt = (try in_stream.readByte()) != 0;1748 const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;
1742 const line_base = try in_stream.readByteSigned();1749 const line_base = try di.dwarf_in_stream.readByteSigned();
17431750
1744 const line_range = try in_stream.readByte();1751 const line_range = try di.dwarf_in_stream.readByte();
1745 if (line_range == 0) return error.InvalidDebugInfo;1752 if (line_range == 0) return error.InvalidDebugInfo;
17461753
1747 const opcode_base = try in_stream.readByte();1754 const opcode_base = try di.dwarf_in_stream.readByte();
17481755
1749 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);1756 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
17501757
1751 {1758 {
1752 var i: usize = 0;1759 var i: usize = 0;
1753 while (i < opcode_base - 1) : (i += 1) {1760 while (i < opcode_base - 1) : (i += 1) {
1754 standard_opcode_lengths[i] = try in_stream.readByte();1761 standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte();
1755 }1762 }
1756 }1763 }
17571764
...@@ -1769,9 +1776,9 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1769,9 +1776,9 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1769 while (true) {1776 while (true) {
1770 const file_name = try di.readString();1777 const file_name = try di.readString();
1771 if (file_name.len == 0) break;1778 if (file_name.len == 0) break;
1772 const dir_index = try readULeb128(in_stream);1779 const dir_index = try readULeb128(di.dwarf_in_stream);
1773 const mtime = try readULeb128(in_stream);1780 const mtime = try readULeb128(di.dwarf_in_stream);
1774 const len_bytes = try readULeb128(in_stream);1781 const len_bytes = try readULeb128(di.dwarf_in_stream);
1775 try file_entries.append(FileEntry{1782 try file_entries.append(FileEntry{
1776 .file_name = file_name,1783 .file_name = file_name,
1777 .dir_index = dir_index,1784 .dir_index = dir_index,
...@@ -1780,15 +1787,15 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1780,15 +1787,15 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1780 });1787 });
1781 }1788 }
17821789
1783 try in_file.seekTo(prog_start_offset);1790 try di.dwarf_seekable_stream.seekTo(prog_start_offset);
17841791
1785 while (true) {1792 while (true) {
1786 const opcode = try in_stream.readByte();1793 const opcode = try di.dwarf_in_stream.readByte();
17871794
1788 if (opcode == DW.LNS_extended_op) {1795 if (opcode == DW.LNS_extended_op) {
1789 const op_size = try readULeb128(in_stream);1796 const op_size = try readULeb128(di.dwarf_in_stream);
1790 if (op_size < 1) return error.InvalidDebugInfo;1797 if (op_size < 1) return error.InvalidDebugInfo;
1791 var sub_op = try in_stream.readByte();1798 var sub_op = try di.dwarf_in_stream.readByte();
1792 switch (sub_op) {1799 switch (sub_op) {
1793 DW.LNE_end_sequence => {1800 DW.LNE_end_sequence => {
1794 prog.end_sequence = true;1801 prog.end_sequence = true;
...@@ -1796,14 +1803,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1796,14 +1803,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1796 return error.MissingDebugInfo;1803 return error.MissingDebugInfo;
1797 },1804 },
1798 DW.LNE_set_address => {1805 DW.LNE_set_address => {
1799 const addr = try in_stream.readInt(di.elf.endian, usize);1806 const addr = try di.dwarf_in_stream.readInt(di.elf.endian, usize);
1800 prog.address = addr;1807 prog.address = addr;
1801 },1808 },
1802 DW.LNE_define_file => {1809 DW.LNE_define_file => {
1803 const file_name = try di.readString();1810 const file_name = try di.readString();
1804 const dir_index = try readULeb128(in_stream);1811 const dir_index = try readULeb128(di.dwarf_in_stream);
1805 const mtime = try readULeb128(in_stream);1812 const mtime = try readULeb128(di.dwarf_in_stream);
1806 const len_bytes = try readULeb128(in_stream);1813 const len_bytes = try readULeb128(di.dwarf_in_stream);
1807 try file_entries.append(FileEntry{1814 try file_entries.append(FileEntry{
1808 .file_name = file_name,1815 .file_name = file_name,
1809 .dir_index = dir_index,1816 .dir_index = dir_index,
...@@ -1813,7 +1820,7 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1813,7 +1820,7 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1813 },1820 },
1814 else => {1821 else => {
1815 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;1822 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
1816 try in_file.seekForward(fwd_amt);1823 try di.dwarf_seekable_stream.seekForward(fwd_amt);
1817 },1824 },
1818 }1825 }
1819 } else if (opcode >= opcode_base) {1826 } else if (opcode >= opcode_base) {
...@@ -1832,19 +1839,19 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1832,19 +1839,19 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1832 prog.basic_block = false;1839 prog.basic_block = false;
1833 },1840 },
1834 DW.LNS_advance_pc => {1841 DW.LNS_advance_pc => {
1835 const arg = try readULeb128(in_stream);1842 const arg = try readULeb128(di.dwarf_in_stream);
1836 prog.address += arg * minimum_instruction_length;1843 prog.address += arg * minimum_instruction_length;
1837 },1844 },
1838 DW.LNS_advance_line => {1845 DW.LNS_advance_line => {
1839 const arg = try readILeb128(in_stream);1846 const arg = try readILeb128(di.dwarf_in_stream);
1840 prog.line += arg;1847 prog.line += arg;
1841 },1848 },
1842 DW.LNS_set_file => {1849 DW.LNS_set_file => {
1843 const arg = try readULeb128(in_stream);1850 const arg = try readULeb128(di.dwarf_in_stream);
1844 prog.file = arg;1851 prog.file = arg;
1845 },1852 },
1846 DW.LNS_set_column => {1853 DW.LNS_set_column => {
1847 const arg = try readULeb128(in_stream);1854 const arg = try readULeb128(di.dwarf_in_stream);
1848 prog.column = arg;1855 prog.column = arg;
1849 },1856 },
1850 DW.LNS_negate_stmt => {1857 DW.LNS_negate_stmt => {
...@@ -1858,14 +1865,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1858,14 +1865,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1858 prog.address += inc_addr;1865 prog.address += inc_addr;
1859 },1866 },
1860 DW.LNS_fixed_advance_pc => {1867 DW.LNS_fixed_advance_pc => {
1861 const arg = try in_stream.readInt(di.elf.endian, u16);1868 const arg = try di.dwarf_in_stream.readInt(di.elf.endian, u16);
1862 prog.address += arg;1869 prog.address += arg;
1863 },1870 },
1864 DW.LNS_set_prologue_end => {},1871 DW.LNS_set_prologue_end => {},
1865 else => {1872 else => {
1866 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;1873 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
1867 const len_bytes = standard_opcode_lengths[opcode - 1];1874 const len_bytes = standard_opcode_lengths[opcode - 1];
1868 try in_file.seekForward(len_bytes);1875 try di.dwarf_seekable_stream.seekForward(len_bytes);
1869 },1876 },
1870 }1877 }
1871 }1878 }
...@@ -1877,36 +1884,33 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1877,36 +1884,33 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1877 return error.MissingDebugInfo;1884 return error.MissingDebugInfo;
1878}1885}
18791886
1880fn scanAllCompileUnits(st: *DebugInfo) !void {1887fn scanAllCompileUnits(di: *DebugInfo) !void {
1881 const debug_info_end = st.debug_info.offset + st.debug_info.size;1888 const debug_info_end = di.debug_info.offset + di.debug_info.size;
1882 var this_unit_offset = st.debug_info.offset;1889 var this_unit_offset = di.debug_info.offset;
1883 var cu_index: usize = 0;1890 var cu_index: usize = 0;
18841891
1885 var in_file_stream = st.self_exe_file.inStream();
1886 const in_stream = &in_file_stream.stream;
1887
1888 while (this_unit_offset < debug_info_end) {1892 while (this_unit_offset < debug_info_end) {
1889 try st.self_exe_file.seekTo(this_unit_offset);1893 try di.dwarf_seekable_stream.seekTo(this_unit_offset);
18901894
1891 var is_64: bool = undefined;1895 var is_64: bool = undefined;
1892 const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);1896 const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
1893 if (unit_length == 0) return;1897 if (unit_length == 0) return;
1894 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));1898 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
18951899
1896 const version = try in_stream.readInt(st.elf.endian, u16);1900 const version = try di.dwarf_in_stream.readInt(di.elf.endian, u16);
1897 if (version < 2 or version > 5) return error.InvalidDebugInfo;1901 if (version < 2 or version > 5) return error.InvalidDebugInfo;
18981902
1899 const debug_abbrev_offset = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32);1903 const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(di.elf.endian, u64) else try di.dwarf_in_stream.readInt(di.elf.endian, u32);
19001904
1901 const address_size = try in_stream.readByte();1905 const address_size = try di.dwarf_in_stream.readByte();
1902 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;1906 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
19031907
1904 const compile_unit_pos = try st.self_exe_file.getPos();1908 const compile_unit_pos = try di.dwarf_seekable_stream.getPos();
1905 const abbrev_table = try getAbbrevTable(st, debug_abbrev_offset);1909 const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset);
19061910
1907 try st.self_exe_file.seekTo(compile_unit_pos);1911 try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
19081912
1909 const compile_unit_die = try st.allocator().create(try parseDie(st, abbrev_table, is_64));1913 const compile_unit_die = try di.allocator().create(try parseDie(di, abbrev_table, is_64));
19101914
1911 if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;1915 if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
19121916
...@@ -1934,7 +1938,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {...@@ -1934,7 +1938,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
1934 }1938 }
1935 };1939 };
19361940
1937 try st.compile_unit_list.append(CompileUnit{1941 try di.compile_unit_list.append(CompileUnit{
1938 .version = version,1942 .version = version,
1939 .is_64 = is_64,1943 .is_64 = is_64,
1940 .pc_range = pc_range,1944 .pc_range = pc_range,
...@@ -1947,20 +1951,18 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {...@@ -1947,20 +1951,18 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
1947 }1951 }
1948}1952}
19491953
1950fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {1954fn findCompileUnit(di: *DebugInfo, target_address: u64) !*const CompileUnit {
1951 var in_file_stream = st.self_exe_file.inStream();1955 for (di.compile_unit_list.toSlice()) |*compile_unit| {
1952 const in_stream = &in_file_stream.stream;
1953 for (st.compile_unit_list.toSlice()) |*compile_unit| {
1954 if (compile_unit.pc_range) |range| {1956 if (compile_unit.pc_range) |range| {
1955 if (target_address >= range.start and target_address < range.end) return compile_unit;1957 if (target_address >= range.start and target_address < range.end) return compile_unit;
1956 }1958 }
1957 if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {1959 if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
1958 var base_address: usize = 0;1960 var base_address: usize = 0;
1959 if (st.debug_ranges) |debug_ranges| {1961 if (di.debug_ranges) |debug_ranges| {
1960 try st.self_exe_file.seekTo(debug_ranges.offset + ranges_offset);1962 try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset);
1961 while (true) {1963 while (true) {
1962 const begin_addr = try in_stream.readIntLe(usize);1964 const begin_addr = try di.dwarf_in_stream.readIntLe(usize);
1963 const end_addr = try in_stream.readIntLe(usize);1965 const end_addr = try di.dwarf_in_stream.readIntLe(usize);
1964 if (begin_addr == 0 and end_addr == 0) {1966 if (begin_addr == 0 and end_addr == 0) {
1965 break;1967 break;
1966 }1968 }
std/elf.zig+24-21
...@@ -353,7 +353,8 @@ pub const SectionHeader = struct {...@@ -353,7 +353,8 @@ pub const SectionHeader = struct {
353};353};
354354
355pub const Elf = struct {355pub const Elf = struct {
356 in_file: os.File,356 seekable_stream: *io.SeekableStream(anyerror, anyerror),
357 in_stream: *io.InStream(anyerror),
357 auto_close_stream: bool,358 auto_close_stream: bool,
358 is_64: bool,359 is_64: bool,
359 endian: builtin.Endian,360 endian: builtin.Endian,
...@@ -370,19 +371,24 @@ pub const Elf = struct {...@@ -370,19 +371,24 @@ pub const Elf = struct {
370371
371 /// Call close when done.372 /// Call close when done.
372 pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void {373 pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void {
373 try elf.prealloc_file.open(path);374 @compileError("TODO implement");
374 try elf.openFile(allocator, *elf.prealloc_file);
375 elf.auto_close_stream = true;
376 }375 }
377376
378 /// Call close when done.377 /// Call close when done.
379 pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void {378 pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void {
380 elf.allocator = allocator;379 @compileError("TODO implement");
381 elf.in_file = file;380 }
382 elf.auto_close_stream = false;
383381
384 var file_stream = elf.in_file.inStream();382 pub fn openStream(
385 const in = &file_stream.stream;383 elf: *Elf,
384 allocator: *mem.Allocator,
385 seekable_stream: *io.SeekableStream(anyerror, anyerror),
386 in: *io.InStream(anyerror),
387 ) !void {
388 elf.auto_close_stream = false;
389 elf.allocator = allocator;
390 elf.seekable_stream = seekable_stream;
391 elf.in_stream = in;
386392
387 var magic: [4]u8 = undefined;393 var magic: [4]u8 = undefined;
388 try in.readNoEof(magic[0..]);394 try in.readNoEof(magic[0..]);
...@@ -404,7 +410,7 @@ pub const Elf = struct {...@@ -404,7 +410,7 @@ pub const Elf = struct {
404 if (version_byte != 1) return error.InvalidFormat;410 if (version_byte != 1) return error.InvalidFormat;
405411
406 // skip over padding412 // skip over padding
407 try elf.in_file.seekForward(9);413 try seekable_stream.seekForward(9);
408414
409 elf.file_type = switch (try in.readInt(elf.endian, u16)) {415 elf.file_type = switch (try in.readInt(elf.endian, u16)) {
410 1 => FileType.Relocatable,416 1 => FileType.Relocatable,
...@@ -441,7 +447,7 @@ pub const Elf = struct {...@@ -441,7 +447,7 @@ pub const Elf = struct {
441 }447 }
442448
443 // skip over flags449 // skip over flags
444 try elf.in_file.seekForward(4);450 try seekable_stream.seekForward(4);
445451
446 const header_size = try in.readInt(elf.endian, u16);452 const header_size = try in.readInt(elf.endian, u16);
447 if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) {453 if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) {
...@@ -461,12 +467,12 @@ pub const Elf = struct {...@@ -461,12 +467,12 @@ pub const Elf = struct {
461 const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);467 const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);
462 const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count);468 const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count);
463469
464 const stream_end = try elf.in_file.getEndPos();470 const stream_end = try seekable_stream.getEndPos();
465 if (stream_end < end_sh or stream_end < end_ph) {471 if (stream_end < end_sh or stream_end < end_ph) {
466 return error.InvalidFormat;472 return error.InvalidFormat;
467 }473 }
468474
469 try elf.in_file.seekTo(elf.section_header_offset);475 try seekable_stream.seekTo(elf.section_header_offset);
470476
471 elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count);477 elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count);
472 errdefer elf.allocator.free(elf.section_headers);478 errdefer elf.allocator.free(elf.section_headers);
...@@ -521,26 +527,23 @@ pub const Elf = struct {...@@ -521,26 +527,23 @@ pub const Elf = struct {
521 pub fn close(elf: *Elf) void {527 pub fn close(elf: *Elf) void {
522 elf.allocator.free(elf.section_headers);528 elf.allocator.free(elf.section_headers);
523529
524 if (elf.auto_close_stream) elf.in_file.close();530 if (elf.auto_close_stream) elf.prealloc_file.close();
525 }531 }
526532
527 pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {533 pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
528 var file_stream = elf.in_file.inStream();
529 const in = &file_stream.stream;
530
531 section_loop: for (elf.section_headers) |*elf_section| {534 section_loop: for (elf.section_headers) |*elf_section| {
532 if (elf_section.sh_type == SHT_NULL) continue;535 if (elf_section.sh_type == SHT_NULL) continue;
533536
534 const name_offset = elf.string_section.offset + elf_section.name;537 const name_offset = elf.string_section.offset + elf_section.name;
535 try elf.in_file.seekTo(name_offset);538 try elf.seekable_stream.seekTo(name_offset);
536539
537 for (name) |expected_c| {540 for (name) |expected_c| {
538 const target_c = try in.readByte();541 const target_c = try elf.in_stream.readByte();
539 if (target_c == 0 or expected_c != target_c) continue :section_loop;542 if (target_c == 0 or expected_c != target_c) continue :section_loop;
540 }543 }
541544
542 {545 {
543 const null_byte = try in.readByte();546 const null_byte = try elf.in_stream.readByte();
544 if (null_byte == 0) return elf_section;547 if (null_byte == 0) return elf_section;
545 }548 }
546 }549 }
...@@ -549,7 +552,7 @@ pub const Elf = struct {...@@ -549,7 +552,7 @@ pub const Elf = struct {
549 }552 }
550553
551 pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void {554 pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void {
552 try elf.in_file.seekTo(elf_section.offset);555 try elf.seekable_stream.seekTo(elf_section.offset);
553 }556 }
554};557};
555558
std/io.zig+2
...@@ -32,6 +32,8 @@ pub fn getStdIn() GetStdIoErrs!File {...@@ -32,6 +32,8 @@ pub fn getStdIn() GetStdIoErrs!File {
32 return File.openHandle(handle);32 return File.openHandle(handle);
33}33}
3434
35pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
36
35pub fn InStream(comptime ReadError: type) type {37pub fn InStream(comptime ReadError: type) type {
36 return struct {38 return struct {
37 const Self = @This();39 const Self = @This();
std/io/seekable_stream.zig created+32
...@@ -0,0 +1,32 @@
1const std = @import("../index.zig");
2const InStream = std.io.InStream;
3
4pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type {
5 return struct {
6 const Self = @This();
7 pub const SeekError = SeekErrorType;
8 pub const GetSeekPosError = GetSeekPosErrorType;
9
10 seekToFn: fn (self: *Self, pos: usize) SeekError!void,
11 seekForwardFn: fn (self: *Self, pos: isize) SeekError!void,
12
13 getPosFn: fn (self: *Self) GetSeekPosError!usize,
14 getEndPosFn: fn (self: *Self) GetSeekPosError!usize,
15
16 pub fn seekTo(self: *Self, pos: usize) SeekError!void {
17 return self.seekToFn(self, pos);
18 }
19
20 pub fn seekForward(self: *Self, amt: isize) SeekError!void {
21 return self.seekForwardFn(self, amt);
22 }
23
24 pub fn getEndPos(self: *Self) GetSeekPosError!usize {
25 return self.getEndPosFn(self);
26 }
27
28 pub fn getPos(self: *Self) GetSeekPosError!usize {
29 return self.getPosFn(self);
30 }
31 };
32}
std/os/file.zig+59-5
...@@ -228,7 +228,14 @@ pub const File = struct {...@@ -228,7 +228,14 @@ pub const File = struct {
228 return os.isTty(self.handle);228 return os.isTty(self.handle);
229 }229 }
230230
231 pub fn seekForward(self: File, amount: isize) !void {231 pub const SeekError = error{
232 /// TODO make this error impossible to get
233 Overflow,
234 Unseekable,
235 Unexpected,
236 };
237
238 pub fn seekForward(self: File, amount: isize) SeekError!void {
232 switch (builtin.os) {239 switch (builtin.os) {
233 Os.linux, Os.macosx, Os.ios, Os.freebsd => {240 Os.linux, Os.macosx, Os.ios, Os.freebsd => {
234 const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);241 const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
...@@ -259,7 +266,7 @@ pub const File = struct {...@@ -259,7 +266,7 @@ pub const File = struct {
259 }266 }
260 }267 }
261268
262 pub fn seekTo(self: File, pos: usize) !void {269 pub fn seekTo(self: File, pos: usize) SeekError!void {
263 switch (builtin.os) {270 switch (builtin.os) {
264 Os.linux, Os.macosx, Os.ios, Os.freebsd => {271 Os.linux, Os.macosx, Os.ios, Os.freebsd => {
265 const ipos = try math.cast(isize, pos);272 const ipos = try math.cast(isize, pos);
...@@ -293,7 +300,14 @@ pub const File = struct {...@@ -293,7 +300,14 @@ pub const File = struct {
293 }300 }
294 }301 }
295302
296 pub fn getPos(self: File) !usize {303 pub const GetSeekPosError = error{
304 Overflow,
305 SystemResources,
306 Unseekable,
307 Unexpected,
308 };
309
310 pub fn getPos(self: File) GetSeekPosError!usize {
297 switch (builtin.os) {311 switch (builtin.os) {
298 Os.linux, Os.macosx, Os.ios, Os.freebsd => {312 Os.linux, Os.macosx, Os.ios, Os.freebsd => {
299 const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);313 const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
...@@ -323,13 +337,13 @@ pub const File = struct {...@@ -323,13 +337,13 @@ pub const File = struct {
323 }337 }
324338
325 assert(pos >= 0);339 assert(pos >= 0);
326 return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange;340 return math.cast(usize, pos);
327 },341 },
328 else => @compileError("unsupported OS"),342 else => @compileError("unsupported OS"),
329 }343 }
330 }344 }
331345
332 pub fn getEndPos(self: File) !usize {346 pub fn getEndPos(self: File) GetSeekPosError!usize {
333 if (is_posix) {347 if (is_posix) {
334 const stat = try os.posixFStat(self.handle);348 const stat = try os.posixFStat(self.handle);
335 return @intCast(usize, stat.size);349 return @intCast(usize, stat.size);
...@@ -431,6 +445,18 @@ pub const File = struct {...@@ -431,6 +445,18 @@ pub const File = struct {
431 };445 };
432 }446 }
433447
448 pub fn seekableStream(file: File) SeekableStream {
449 return SeekableStream{
450 .file = file,
451 .stream = SeekableStream.Stream{
452 .seekToFn = SeekableStream.seekToFn,
453 .seekForwardFn = SeekableStream.seekForwardFn,
454 .getPosFn = SeekableStream.getPosFn,
455 .getEndPosFn = SeekableStream.getEndPosFn,
456 },
457 };
458 }
459
434 /// Implementation of io.InStream trait for File460 /// Implementation of io.InStream trait for File
435 pub const InStream = struct {461 pub const InStream = struct {
436 file: File,462 file: File,
...@@ -458,4 +484,32 @@ pub const File = struct {...@@ -458,4 +484,32 @@ pub const File = struct {
458 return self.file.write(bytes);484 return self.file.write(bytes);
459 }485 }
460 };486 };
487
488 /// Implementation of io.SeekableStream trait for File
489 pub const SeekableStream = struct {
490 file: File,
491 stream: Stream,
492
493 pub const Stream = io.SeekableStream(SeekError, GetSeekPosError);
494
495 pub fn seekToFn(seekable_stream: *Stream, pos: usize) SeekError!void {
496 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
497 return self.file.seekTo(pos);
498 }
499
500 pub fn seekForwardFn(seekable_stream: *Stream, amt: isize) SeekError!void {
501 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
502 return self.file.seekForward(amt);
503 }
504
505 pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!usize {
506 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
507 return self.file.getEndPos();
508 }
509
510 pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!usize {
511 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
512 return self.file.getPos();
513 }
514 };
461};515};