authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-10 15:27:45-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-10 15:32:32-04:00
logba0e3be5cfa2f60f2f9d2a4eb319408f972796c2
treeda35c17d0067a463b521163f624f3d109b8050f4
parent1ad831a0ef22f354d4e1dd5073456a0683249846
signature Commit is signed but in an unrecognized format.

(breaking) rework stream abstractions

The main goal here is to make the function pointers comptime, so that we don't have to do the crazy stuff with async function frames. Since InStream, OutStream, and SeekableStream are already generic across error sets, it's not really worse to make them generic across the vtable as well. See #764 for the open issue acknowledging that using generics for these abstractions is a design flaw. See #130 for the efforts to make these abstractions non-generic. This commit also changes the OutStream API so that `write` returns number of bytes written, and `writeAll` is the one that loops until the whole buffer is written.

24 files changed, 752 insertions(+), 776 deletions(-)

lib/std/buffer.zig+11
......@@ -157,6 +157,17 @@ pub const Buffer = struct {
157157 pub fn print(self: *Buffer, comptime fmt: []const u8, args: var) !void {
158158 return std.fmt.format(self, error{OutOfMemory}, Buffer.append, fmt, args);
159159 }
160
161 pub fn outStream(self: *Buffer) std.io.OutStream(*Buffer, error{OutOfMemory}, appendWrite) {
162 return .{ .context = self };
163 }
164
165 /// Same as `append` except it returns the number of bytes written, which is always the same
166 /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
167 pub fn appendWrite(self: *Buffer, m: []const u8) !usize {
168 try self.append(m);
169 return m.len;
170 }
160171};
161172
162173test "simple Buffer" {
lib/std/child_process.zig+4-6
......@@ -221,9 +221,9 @@ pub const ChildProcess = struct {
221221 var stderr_file_in_stream = child.stderr.?.inStream();
222222
223223 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
224 const stdout = try stdout_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
224 const stdout = try stdout_file_in_stream.readAllAlloc(args.allocator, args.max_output_bytes);
225225 errdefer args.allocator.free(stdout);
226 const stderr = try stderr_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
226 const stderr = try stderr_file_in_stream.readAllAlloc(args.allocator, args.max_output_bytes);
227227 errdefer args.allocator.free(stderr);
228228
229229 return ExecResult{
......@@ -857,8 +857,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
857857 .io_mode = .blocking,
858858 .async_block_allowed = File.async_block_allowed_yes,
859859 };
860 const stream = &file.outStream().stream;
861 stream.writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
860 file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
862861}
863862
864863fn readIntFd(fd: i32) !ErrInt {
......@@ -867,8 +866,7 @@ fn readIntFd(fd: i32) !ErrInt {
867866 .io_mode = .blocking,
868867 .async_block_allowed = File.async_block_allowed_yes,
869868 };
870 const stream = &file.inStream().stream;
871 return @intCast(ErrInt, stream.readIntNative(u64) catch return error.SystemResources);
869 return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);
872870}
873871
874872/// Caller must free result.
lib/std/debug.zig+128-105
......@@ -55,7 +55,7 @@ pub const LineInfo = struct {
5555var stderr_file: File = undefined;
5656var stderr_file_out_stream: File.OutStream = undefined;
5757
58var stderr_stream: ?*io.OutStream(File.WriteError) = null;
58var stderr_stream: ?*File.OutStream = null;
5959var stderr_mutex = std.Mutex.init();
6060
6161pub fn warn(comptime fmt: []const u8, args: var) void {
......@@ -65,13 +65,13 @@ pub fn warn(comptime fmt: []const u8, args: var) void {
6565 noasync stderr.print(fmt, args) catch return;
6666}
6767
68pub fn getStderrStream() *io.OutStream(File.WriteError) {
68pub fn getStderrStream() *File.OutStream {
6969 if (stderr_stream) |st| {
7070 return st;
7171 } else {
7272 stderr_file = io.getStdErr();
7373 stderr_file_out_stream = stderr_file.outStream();
74 const st = &stderr_file_out_stream.stream;
74 const st = &stderr_file_out_stream;
7575 stderr_stream = st;
7676 return st;
7777 }
......@@ -408,15 +408,15 @@ pub const TTY = struct {
408408 windows_api,
409409
410410 fn setColor(conf: Config, out_stream: var, color: Color) void {
411 switch (conf) {
411 noasync switch (conf) {
412412 .no_color => return,
413413 .escape_codes => switch (color) {
414 .Red => noasync out_stream.write(RED) catch return,
415 .Green => noasync out_stream.write(GREEN) catch return,
416 .Cyan => noasync out_stream.write(CYAN) catch return,
417 .White, .Bold => noasync out_stream.write(WHITE) catch return,
418 .Dim => noasync out_stream.write(DIM) catch return,
419 .Reset => noasync out_stream.write(RESET) catch return,
414 .Red => out_stream.writeAll(RED) catch return,
415 .Green => out_stream.writeAll(GREEN) catch return,
416 .Cyan => out_stream.writeAll(CYAN) catch return,
417 .White, .Bold => out_stream.writeAll(WHITE) catch return,
418 .Dim => out_stream.writeAll(DIM) catch return,
419 .Reset => out_stream.writeAll(RESET) catch return,
420420 },
421421 .windows_api => if (builtin.os.tag == .windows) {
422422 const S = struct {
......@@ -455,7 +455,7 @@ pub const TTY = struct {
455455 } else {
456456 unreachable;
457457 },
458 }
458 };
459459 }
460460 };
461461};
......@@ -565,38 +565,40 @@ fn printLineInfo(
565565 tty_config: TTY.Config,
566566 comptime printLineFromFile: var,
567567) !void {
568 tty_config.setColor(out_stream, .White);
568 noasync {
569 tty_config.setColor(out_stream, .White);
569570
570 if (line_info) |*li| {
571 try noasync out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
572 } else {
573 try noasync out_stream.write("???:?:?");
574 }
571 if (line_info) |*li| {
572 try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
573 } else {
574 try out_stream.writeAll("???:?:?");
575 }
575576
576 tty_config.setColor(out_stream, .Reset);
577 try noasync out_stream.write(": ");
578 tty_config.setColor(out_stream, .Dim);
579 try noasync out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
580 tty_config.setColor(out_stream, .Reset);
581 try noasync out_stream.write("\n");
582
583 // Show the matching source code line if possible
584 if (line_info) |li| {
585 if (noasync printLineFromFile(out_stream, li)) {
586 if (li.column > 0) {
587 // The caret already takes one char
588 const space_needed = @intCast(usize, li.column - 1);
589
590 try noasync out_stream.writeByteNTimes(' ', space_needed);
591 tty_config.setColor(out_stream, .Green);
592 try noasync out_stream.write("^");
593 tty_config.setColor(out_stream, .Reset);
577 tty_config.setColor(out_stream, .Reset);
578 try out_stream.writeAll(": ");
579 tty_config.setColor(out_stream, .Dim);
580 try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
581 tty_config.setColor(out_stream, .Reset);
582 try out_stream.writeAll("\n");
583
584 // Show the matching source code line if possible
585 if (line_info) |li| {
586 if (printLineFromFile(out_stream, li)) {
587 if (li.column > 0) {
588 // The caret already takes one char
589 const space_needed = @intCast(usize, li.column - 1);
590
591 try out_stream.writeByteNTimes(' ', space_needed);
592 tty_config.setColor(out_stream, .Green);
593 try out_stream.writeAll("^");
594 tty_config.setColor(out_stream, .Reset);
595 }
596 try out_stream.writeAll("\n");
597 } else |err| switch (err) {
598 error.EndOfFile, error.FileNotFound => {},
599 error.BadPathName => {},
600 else => return err,
594601 }
595 try noasync out_stream.write("\n");
596 } else |err| switch (err) {
597 error.EndOfFile, error.FileNotFound => {},
598 error.BadPathName => {},
599 else => return err,
600602 }
601603 }
602604}
......@@ -609,21 +611,21 @@ pub const OpenSelfDebugInfoError = error{
609611};
610612
611613/// TODO resources https://github.com/ziglang/zig/issues/4353
612/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
613/// make this `noasync fn` and remove the individual noasync calls.
614614pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
615 if (builtin.strip_debug_info)
616 return error.MissingDebugInfo;
617 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
618 return noasync root.os.debug.openSelfDebugInfo(allocator);
619 }
620 switch (builtin.os.tag) {
621 .linux,
622 .freebsd,
623 .macosx,
624 .windows,
625 => return DebugInfo.init(allocator),
626 else => @compileError("openSelfDebugInfo unsupported for this platform"),
615 noasync {
616 if (builtin.strip_debug_info)
617 return error.MissingDebugInfo;
618 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
619 return root.os.debug.openSelfDebugInfo(allocator);
620 }
621 switch (builtin.os.tag) {
622 .linux,
623 .freebsd,
624 .macosx,
625 .windows,
626 => return DebugInfo.init(allocator),
627 else => @compileError("openSelfDebugInfo unsupported for this platform"),
628 }
627629 }
628630}
629631
......@@ -808,45 +810,64 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
808810
809811/// TODO resources https://github.com/ziglang/zig/issues/4353
810812pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
811 const mapped_mem = try mapWholeFile(elf_file_path);
812
813 var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
814 var efile = try noasync elf.Elf.openStream(
815 allocator,
816 @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
817 @ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
818 );
819 defer noasync efile.close();
813 noasync {
814 const mapped_mem = try mapWholeFile(elf_file_path);
815 const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
816 if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
817 if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
818
819 const endian: builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
820 elf.ELFDATA2LSB => .Little,
821 elf.ELFDATA2MSB => .Big,
822 else => return error.InvalidElfEndian,
823 };
824 assert(endian == std.builtin.endian); // this is our own debug info
825
826 const shoff = hdr.e_shoff;
827 const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
828 const header_strings = mapped_mem[str_section_off..str_section_off + hdr.e_shentsize];
829 const shdrs = @ptrCast([*]const elf.Shdr, @alignCast(@alignOf(elf.Shdr), &mapped_mem[shoff]))[0..hdr.e_shnum];
830
831 var opt_debug_info: ?[]const u8 = null;
832 var opt_debug_abbrev: ?[]const u8 = null;
833 var opt_debug_str: ?[]const u8 = null;
834 var opt_debug_line: ?[]const u8 = null;
835 var opt_debug_ranges: ?[]const u8 = null;
836
837 for (shdrs) |*shdr| {
838 if (shdr.sh_type == elf.SHT_NULL) continue;
839
840 const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr));
841 if (mem.eql(u8, name, ".debug_info")) {
842 opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
843 } else if (mem.eql(u8, name, ".debug_abbrev")) {
844 opt_debug_abbrev = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
845 } else if (mem.eql(u8, name, ".debug_str")) {
846 opt_debug_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
847 } else if (mem.eql(u8, name, ".debug_line")) {
848 opt_debug_line = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
849 } else if (mem.eql(u8, name, ".debug_ranges")) {
850 opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
851 }
852 }
820853
821 const debug_info = (try noasync efile.findSection(".debug_info")) orelse
822 return error.MissingDebugInfo;
823 const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse
824 return error.MissingDebugInfo;
825 const debug_str = (try noasync efile.findSection(".debug_str")) orelse
826 return error.MissingDebugInfo;
827 const debug_line = (try noasync efile.findSection(".debug_line")) orelse
828 return error.MissingDebugInfo;
829 const opt_debug_ranges = try noasync efile.findSection(".debug_ranges");
830
831 var di = DW.DwarfInfo{
832 .endian = efile.endian,
833 .debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
834 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
835 .debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
836 .debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
837 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
838 try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size)
839 else
840 null,
841 };
854 var di = DW.DwarfInfo{
855 .endian = endian,
856 .debug_info = opt_debug_info orelse return error.MissingDebugInfo,
857 .debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
858 .debug_str = opt_debug_str orelse return error.MissingDebugInfo,
859 .debug_line = opt_debug_line orelse return error.MissingDebugInfo,
860 .debug_ranges = opt_debug_ranges,
861 };
842862
843 try noasync DW.openDwarfDebugInfo(&di, allocator);
863 try DW.openDwarfDebugInfo(&di, allocator);
844864
845 return ModuleDebugInfo{
846 .base_address = undefined,
847 .dwarf = di,
848 .mapped_memory = mapped_mem,
849 };
865 return ModuleDebugInfo{
866 .base_address = undefined,
867 .dwarf = di,
868 .mapped_memory = mapped_mem,
869 };
870 }
850871}
851872
852873/// TODO resources https://github.com/ziglang/zig/issues/4353
......@@ -982,22 +1003,24 @@ const MachoSymbol = struct {
9821003 }
9831004};
9841005
985fn mapWholeFile(path: []const u8) ![]const u8 {
986 const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true });
987 defer noasync file.close();
988
989 const file_len = try math.cast(usize, try file.getEndPos());
990 const mapped_mem = try os.mmap(
991 null,
992 file_len,
993 os.PROT_READ,
994 os.MAP_SHARED,
995 file.handle,
996 0,
997 );
998 errdefer os.munmap(mapped_mem);
1006fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
1007 noasync {
1008 const file = try fs.openFileAbsolute(path, .{ .always_blocking = true });
1009 defer file.close();
9991010
1000 return mapped_mem;
1011 const file_len = try math.cast(usize, try file.getEndPos());
1012 const mapped_mem = try os.mmap(
1013 null,
1014 file_len,
1015 os.PROT_READ,
1016 os.MAP_SHARED,
1017 file.handle,
1018 0,
1019 );
1020 errdefer os.munmap(mapped_mem);
1021
1022 return mapped_mem;
1023 }
10011024}
10021025
10031026pub const DebugInfo = struct {
lib/std/dwarf.zig+84-77
......@@ -11,9 +11,6 @@ const ArrayList = std.ArrayList;
1111
1212usingnamespace @import("dwarf_bits.zig");
1313
14pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
15pub const DwarfInStream = io.InStream(anyerror);
16
1714const PcRange = struct {
1815 start: u64,
1916 end: u64,
......@@ -239,7 +236,7 @@ const LineNumberProgram = struct {
239236 }
240237};
241238
242fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
239fn readInitialLength(in_stream: var, is_64: *bool) !u64 {
243240 const first_32_bits = try in_stream.readIntLittle(u32);
244241 is_64.* = (first_32_bits == 0xffffffff);
245242 if (is_64.*) {
......@@ -414,40 +411,42 @@ pub const DwarfInfo = struct {
414411 }
415412
416413 fn scanAllFunctions(di: *DwarfInfo) !void {
417 var s = io.SliceSeekableInStream.init(di.debug_info);
414 var stream = io.fixedBufferStream(di.debug_info);
415 const in = &stream.inStream();
416 const seekable = &stream.seekableStream();
418417 var this_unit_offset: u64 = 0;
419418
420 while (this_unit_offset < try s.seekable_stream.getEndPos()) {
421 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
419 while (this_unit_offset < try seekable.getEndPos()) {
420 seekable.seekTo(this_unit_offset) catch |err| switch (err) {
422421 error.EndOfStream => unreachable,
423422 else => return err,
424423 };
425424
426425 var is_64: bool = undefined;
427 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
426 const unit_length = try readInitialLength(in, &is_64);
428427 if (unit_length == 0) return;
429428 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
430429
431 const version = try s.stream.readInt(u16, di.endian);
430 const version = try in.readInt(u16, di.endian);
432431 if (version < 2 or version > 5) return error.InvalidDebugInfo;
433432
434 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
433 const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
435434
436 const address_size = try s.stream.readByte();
435 const address_size = try in.readByte();
437436 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
438437
439 const compile_unit_pos = try s.seekable_stream.getPos();
438 const compile_unit_pos = try seekable.getPos();
440439 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
441440
442 try s.seekable_stream.seekTo(compile_unit_pos);
441 try seekable.seekTo(compile_unit_pos);
443442
444443 const next_unit_pos = this_unit_offset + next_offset;
445444
446 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
447 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
445 while ((try seekable.getPos()) < next_unit_pos) {
446 const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue;
448447 defer die_obj.attrs.deinit();
449448
450 const after_die_offset = try s.seekable_stream.getPos();
449 const after_die_offset = try seekable.getPos();
451450
452451 switch (die_obj.tag_id) {
453452 TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
......@@ -463,14 +462,14 @@ pub const DwarfInfo = struct {
463462 // Follow the DIE it points to and repeat
464463 const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
465464 if (ref_offset > next_offset) return error.InvalidDebugInfo;
466 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
467 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
465 try seekable.seekTo(this_unit_offset + ref_offset);
466 this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
468467 } else if (this_die_obj.getAttr(AT_specification)) |ref| {
469468 // Follow the DIE it points to and repeat
470469 const ref_offset = try this_die_obj.getAttrRef(AT_specification);
471470 if (ref_offset > next_offset) return error.InvalidDebugInfo;
472 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
473 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
471 try seekable.seekTo(this_unit_offset + ref_offset);
472 this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
474473 } else {
475474 break :x null;
476475 }
......@@ -511,7 +510,7 @@ pub const DwarfInfo = struct {
511510 else => {},
512511 }
513512
514 try s.seekable_stream.seekTo(after_die_offset);
513 try seekable.seekTo(after_die_offset);
515514 }
516515
517516 this_unit_offset += next_offset;
......@@ -519,35 +518,37 @@ pub const DwarfInfo = struct {
519518 }
520519
521520 fn scanAllCompileUnits(di: *DwarfInfo) !void {
522 var s = io.SliceSeekableInStream.init(di.debug_info);
521 var stream = io.fixedBufferStream(di.debug_info);
522 const in = &stream.inStream();
523 const seekable = &stream.seekableStream();
523524 var this_unit_offset: u64 = 0;
524525
525 while (this_unit_offset < try s.seekable_stream.getEndPos()) {
526 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
526 while (this_unit_offset < try seekable.getEndPos()) {
527 seekable.seekTo(this_unit_offset) catch |err| switch (err) {
527528 error.EndOfStream => unreachable,
528529 else => return err,
529530 };
530531
531532 var is_64: bool = undefined;
532 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
533 const unit_length = try readInitialLength(in, &is_64);
533534 if (unit_length == 0) return;
534535 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
535536
536 const version = try s.stream.readInt(u16, di.endian);
537 const version = try in.readInt(u16, di.endian);
537538 if (version < 2 or version > 5) return error.InvalidDebugInfo;
538539
539 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
540 const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
540541
541 const address_size = try s.stream.readByte();
542 const address_size = try in.readByte();
542543 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
543544
544 const compile_unit_pos = try s.seekable_stream.getPos();
545 const compile_unit_pos = try seekable.getPos();
545546 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
546547
547 try s.seekable_stream.seekTo(compile_unit_pos);
548 try seekable.seekTo(compile_unit_pos);
548549
549550 const compile_unit_die = try di.allocator().create(Die);
550 compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
551 compile_unit_die.* = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
551552
552553 if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
553554
......@@ -593,7 +594,9 @@ pub const DwarfInfo = struct {
593594 }
594595 if (di.debug_ranges) |debug_ranges| {
595596 if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
596 var s = io.SliceSeekableInStream.init(debug_ranges);
597 var stream = io.fixedBufferStream(debug_ranges);
598 const in = &stream.inStream();
599 const seekable = &stream.seekableStream();
597600
598601 // All the addresses in the list are relative to the value
599602 // specified by DW_AT_low_pc or to some other value encoded
......@@ -604,11 +607,11 @@ pub const DwarfInfo = struct {
604607 else => return err,
605608 };
606609
607 try s.seekable_stream.seekTo(ranges_offset);
610 try seekable.seekTo(ranges_offset);
608611
609612 while (true) {
610 const begin_addr = try s.stream.readIntLittle(usize);
611 const end_addr = try s.stream.readIntLittle(usize);
613 const begin_addr = try in.readIntLittle(usize);
614 const end_addr = try in.readIntLittle(usize);
612615 if (begin_addr == 0 and end_addr == 0) {
613616 break;
614617 }
......@@ -646,25 +649,27 @@ pub const DwarfInfo = struct {
646649 }
647650
648651 fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
649 var s = io.SliceSeekableInStream.init(di.debug_abbrev);
652 var stream = io.fixedBufferStream(di.debug_abbrev);
653 const in = &stream.inStream();
654 const seekable = &stream.seekableStream();
650655
651 try s.seekable_stream.seekTo(offset);
656 try seekable.seekTo(offset);
652657 var result = AbbrevTable.init(di.allocator());
653658 errdefer result.deinit();
654659 while (true) {
655 const abbrev_code = try leb.readULEB128(u64, &s.stream);
660 const abbrev_code = try leb.readULEB128(u64, in);
656661 if (abbrev_code == 0) return result;
657662 try result.append(AbbrevTableEntry{
658663 .abbrev_code = abbrev_code,
659 .tag_id = try leb.readULEB128(u64, &s.stream),
660 .has_children = (try s.stream.readByte()) == CHILDREN_yes,
664 .tag_id = try leb.readULEB128(u64, in),
665 .has_children = (try in.readByte()) == CHILDREN_yes,
661666 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
662667 });
663668 const attrs = &result.items[result.len - 1].attrs;
664669
665670 while (true) {
666 const attr_id = try leb.readULEB128(u64, &s.stream);
667 const form_id = try leb.readULEB128(u64, &s.stream);
671 const attr_id = try leb.readULEB128(u64, in);
672 const form_id = try leb.readULEB128(u64, in);
668673 if (attr_id == 0 and form_id == 0) break;
669674 try attrs.append(AbbrevAttr{
670675 .attr_id = attr_id,
......@@ -695,42 +700,44 @@ pub const DwarfInfo = struct {
695700 }
696701
697702 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
698 var s = io.SliceSeekableInStream.init(di.debug_line);
703 var stream = io.fixedBufferStream(di.debug_line);
704 const in = &stream.inStream();
705 const seekable = &stream.seekableStream();
699706
700707 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
701708 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list);
702709
703 try s.seekable_stream.seekTo(line_info_offset);
710 try seekable.seekTo(line_info_offset);
704711
705712 var is_64: bool = undefined;
706 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
713 const unit_length = try readInitialLength(in, &is_64);
707714 if (unit_length == 0) {
708715 return error.MissingDebugInfo;
709716 }
710717 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
711718
712 const version = try s.stream.readInt(u16, di.endian);
719 const version = try in.readInt(u16, di.endian);
713720 // TODO support 3 and 5
714721 if (version != 2 and version != 4) return error.InvalidDebugInfo;
715722
716 const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
717 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
723 const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
724 const prog_start_offset = (try seekable.getPos()) + prologue_length;
718725
719 const minimum_instruction_length = try s.stream.readByte();
726 const minimum_instruction_length = try in.readByte();
720727 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
721728
722729 if (version >= 4) {
723730 // maximum_operations_per_instruction
724 _ = try s.stream.readByte();
731 _ = try in.readByte();
725732 }
726733
727 const default_is_stmt = (try s.stream.readByte()) != 0;
728 const line_base = try s.stream.readByteSigned();
734 const default_is_stmt = (try in.readByte()) != 0;
735 const line_base = try in.readByteSigned();
729736
730 const line_range = try s.stream.readByte();
737 const line_range = try in.readByte();
731738 if (line_range == 0) return error.InvalidDebugInfo;
732739
733 const opcode_base = try s.stream.readByte();
740 const opcode_base = try in.readByte();
734741
735742 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
736743 defer di.allocator().free(standard_opcode_lengths);
......@@ -738,14 +745,14 @@ pub const DwarfInfo = struct {
738745 {
739746 var i: usize = 0;
740747 while (i < opcode_base - 1) : (i += 1) {
741 standard_opcode_lengths[i] = try s.stream.readByte();
748 standard_opcode_lengths[i] = try in.readByte();
742749 }
743750 }
744751
745752 var include_directories = ArrayList([]const u8).init(di.allocator());
746753 try include_directories.append(compile_unit_cwd);
747754 while (true) {
748 const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
755 const dir = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
749756 if (dir.len == 0) break;
750757 try include_directories.append(dir);
751758 }
......@@ -754,11 +761,11 @@ pub const DwarfInfo = struct {
754761 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
755762
756763 while (true) {
757 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
764 const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
758765 if (file_name.len == 0) break;
759 const dir_index = try leb.readULEB128(usize, &s.stream);
760 const mtime = try leb.readULEB128(usize, &s.stream);
761 const len_bytes = try leb.readULEB128(usize, &s.stream);
766 const dir_index = try leb.readULEB128(usize, in);
767 const mtime = try leb.readULEB128(usize, in);
768 const len_bytes = try leb.readULEB128(usize, in);
762769 try file_entries.append(FileEntry{
763770 .file_name = file_name,
764771 .dir_index = dir_index,
......@@ -767,17 +774,17 @@ pub const DwarfInfo = struct {
767774 });
768775 }
769776
770 try s.seekable_stream.seekTo(prog_start_offset);
777 try seekable.seekTo(prog_start_offset);
771778
772779 const next_unit_pos = line_info_offset + next_offset;
773780
774 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
775 const opcode = try s.stream.readByte();
781 while ((try seekable.getPos()) < next_unit_pos) {
782 const opcode = try in.readByte();
776783
777784 if (opcode == LNS_extended_op) {
778 const op_size = try leb.readULEB128(u64, &s.stream);
785 const op_size = try leb.readULEB128(u64, in);
779786 if (op_size < 1) return error.InvalidDebugInfo;
780 var sub_op = try s.stream.readByte();
787 var sub_op = try in.readByte();
781788 switch (sub_op) {
782789 LNE_end_sequence => {
783790 prog.end_sequence = true;
......@@ -785,14 +792,14 @@ pub const DwarfInfo = struct {
785792 prog.reset();
786793 },
787794 LNE_set_address => {
788 const addr = try s.stream.readInt(usize, di.endian);
795 const addr = try in.readInt(usize, di.endian);
789796 prog.address = addr;
790797 },
791798 LNE_define_file => {
792 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
793 const dir_index = try leb.readULEB128(usize, &s.stream);
794 const mtime = try leb.readULEB128(usize, &s.stream);
795 const len_bytes = try leb.readULEB128(usize, &s.stream);
799 const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
800 const dir_index = try leb.readULEB128(usize, in);
801 const mtime = try leb.readULEB128(usize, in);
802 const len_bytes = try leb.readULEB128(usize, in);
796803 try file_entries.append(FileEntry{
797804 .file_name = file_name,
798805 .dir_index = dir_index,
......@@ -802,7 +809,7 @@ pub const DwarfInfo = struct {
802809 },
803810 else => {
804811 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
805 try s.seekable_stream.seekBy(fwd_amt);
812 try seekable.seekBy(fwd_amt);
806813 },
807814 }
808815 } else if (opcode >= opcode_base) {
......@@ -821,19 +828,19 @@ pub const DwarfInfo = struct {
821828 prog.basic_block = false;
822829 },
823830 LNS_advance_pc => {
824 const arg = try leb.readULEB128(usize, &s.stream);
831 const arg = try leb.readULEB128(usize, in);
825832 prog.address += arg * minimum_instruction_length;
826833 },
827834 LNS_advance_line => {
828 const arg = try leb.readILEB128(i64, &s.stream);
835 const arg = try leb.readILEB128(i64, in);
829836 prog.line += arg;
830837 },
831838 LNS_set_file => {
832 const arg = try leb.readULEB128(usize, &s.stream);
839 const arg = try leb.readULEB128(usize, in);
833840 prog.file = arg;
834841 },
835842 LNS_set_column => {
836 const arg = try leb.readULEB128(u64, &s.stream);
843 const arg = try leb.readULEB128(u64, in);
837844 prog.column = arg;
838845 },
839846 LNS_negate_stmt => {
......@@ -847,14 +854,14 @@ pub const DwarfInfo = struct {
847854 prog.address += inc_addr;
848855 },
849856 LNS_fixed_advance_pc => {
850 const arg = try s.stream.readInt(u16, di.endian);
857 const arg = try in.readInt(u16, di.endian);
851858 prog.address += arg;
852859 },
853860 LNS_set_prologue_end => {},
854861 else => {
855862 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
856863 const len_bytes = standard_opcode_lengths[opcode - 1];
857 try s.seekable_stream.seekBy(len_bytes);
864 try seekable.seekBy(len_bytes);
858865 },
859866 }
860867 }
lib/std/elf.zig+48
......@@ -333,6 +333,54 @@ pub const ET = extern enum(u16) {
333333pub const SectionHeader = Elf64_Shdr;
334334pub const ProgramHeader = Elf64_Phdr;
335335
336const Header = struct {
337 endian: builtin.Endian,
338 is_64: bool,
339 entry: u64,
340 phoff: u64,
341 shoff: u64,
342 phentsize: u16,
343 phnum: u16,
344 shentsize: u16,
345 shnum: u16,
346 shstrndx: u16,
347};
348
349pub fn readHeader(in_stream: var) !Header {
350 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
351 try in_stream.readAll(&hdr_buf);
352 const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
353 const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
354 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
355 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
356
357 const endian = switch (hdr32.e_ident[elf.EI_DATA]) {
358 ELFDATA2LSB => .Little,
359 ELFDATA2MSB => .Big,
360 else => return error.InvalidElfEndian,
361 };
362 const need_bswap = endian != std.builtin.endian;
363
364 const is_64 = switch (hdr32.e_ident[EI_CLASS]) {
365 ELFCLASS32 => false,
366 ELFCLASS64 => true,
367 else => return error.InvalidElfClass,
368 };
369
370 return @as(Header, .{
371 .endian = endian,
372 .is_64 = is_64,
373 .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),
374 .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),
375 .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),
376 .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),
377 .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),
378 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
379 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
380 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
381 });
382}
383
336384pub const Elf = struct {
337385 seekable_stream: *io.SeekableStream(anyerror, anyerror),
338386 in_stream: *io.InStream(anyerror),
lib/std/fs.zig+1-1
......@@ -1150,7 +1150,7 @@ pub const Dir = struct {
11501150 const buf = try allocator.alignedAlloc(u8, A, size);
11511151 errdefer allocator.free(buf);
11521152
1153 try file.inStream().stream.readNoEof(buf);
1153 try file.inStream().readNoEof(buf);
11541154 return buf;
11551155 }
11561156
lib/std/fs/file.zig+19-75
......@@ -71,7 +71,7 @@ pub const File = struct {
7171 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
7272 std.event.Loop.instance.?.close(self.handle);
7373 } else {
74 return os.close(self.handle);
74 os.close(self.handle);
7575 }
7676 }
7777
......@@ -496,85 +496,29 @@ pub const File = struct {
496496 }
497497 }
498498
499 pub fn inStream(file: File) InStream {
500 return InStream{
501 .file = file,
502 .stream = InStream.Stream{ .readFn = InStream.readFn },
503 };
499 pub const InStream = io.InStream(File, ReadError, read);
500
501 pub fn inStream(file: File) io.InStream(File, ReadError, read) {
502 return .{ .context = file };
504503 }
505504
505 pub const OutStream = io.OutStream(File, WriteError, write);
506
506507 pub fn outStream(file: File) OutStream {
507 return OutStream{
508 .file = file,
509 .stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
510 };
508 return .{ .context = file };
511509 }
512510
511 pub const SeekableStream = io.SeekableStream(
512 File,
513 SeekError,
514 GetPosError,
515 seekTo,
516 seekBy,
517 getPos,
518 getEndPos,
519 );
520
513521 pub fn seekableStream(file: File) SeekableStream {
514 return SeekableStream{
515 .file = file,
516 .stream = SeekableStream.Stream{
517 .seekToFn = SeekableStream.seekToFn,
518 .seekByFn = SeekableStream.seekByFn,
519 .getPosFn = SeekableStream.getPosFn,
520 .getEndPosFn = SeekableStream.getEndPosFn,
521 },
522 };
522 return .{ .context = file };
523523 }
524
525 /// Implementation of io.InStream trait for File
526 pub const InStream = struct {
527 file: File,
528 stream: Stream,
529
530 pub const Error = ReadError;
531 pub const Stream = io.InStream(Error);
532
533 fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
534 const self = @fieldParentPtr(InStream, "stream", in_stream);
535 return self.file.read(buffer);
536 }
537 };
538
539 /// Implementation of io.OutStream trait for File
540 pub const OutStream = struct {
541 file: File,
542 stream: Stream,
543
544 pub const Error = WriteError;
545 pub const Stream = io.OutStream(Error);
546
547 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
548 const self = @fieldParentPtr(OutStream, "stream", out_stream);
549 return self.file.write(bytes);
550 }
551 };
552
553 /// Implementation of io.SeekableStream trait for File
554 pub const SeekableStream = struct {
555 file: File,
556 stream: Stream,
557
558 pub const Stream = io.SeekableStream(SeekError, GetPosError);
559
560 pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void {
561 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
562 return self.file.seekTo(pos);
563 }
564
565 pub fn seekByFn(seekable_stream: *Stream, amt: i64) SeekError!void {
566 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
567 return self.file.seekBy(amt);
568 }
569
570 pub fn getEndPosFn(seekable_stream: *Stream) GetPosError!u64 {
571 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
572 return self.file.getEndPos();
573 }
574
575 pub fn getPosFn(seekable_stream: *Stream) GetPosError!u64 {
576 const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
577 return self.file.getPos();
578 }
579 };
580524};
lib/std/io.zig+47-172
......@@ -93,10 +93,46 @@ pub fn getStdIn() File {
9393}
9494
9595pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
96pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream;
97pub const COutStream = @import("io/c_out_stream.zig").COutStream;
9896pub const InStream = @import("io/in_stream.zig").InStream;
9997pub const OutStream = @import("io/out_stream.zig").OutStream;
98pub const FixedBufferInStream = @import("io/fixed_buffer_stream.zig").FixedBufferInStream;
99pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
100
101pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream;
102pub const BufferedOutStreamCustom = @import("io/buffered_out_stream.zig").BufferedOutStreamCustom;
103pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream;
104
105pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream;
106
107pub fn fixedBufferStream(bytes: []const u8) FixedBufferInStream {
108 return (FixedBufferInStream{ .bytes = bytes, .pos = 0 });
109}
110
111pub fn cOutStream(c_file: *std.c.FILE) COutStream {
112 return .{ .context = c_file };
113}
114
115pub const COutStream = OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite);
116
117pub fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
118 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
119 if (amt_written >= 0) return amt_written;
120 switch (std.c._errno().*) {
121 0 => unreachable,
122 os.EINVAL => unreachable,
123 os.EFAULT => unreachable,
124 os.EAGAIN => unreachable, // this is a blocking API
125 os.EBADF => unreachable, // always a race condition
126 os.EDESTADDRREQ => unreachable, // connect was never called
127 os.EDQUOT => return error.DiskQuota,
128 os.EFBIG => return error.FileTooBig,
129 os.EIO => return error.InputOutput,
130 os.ENOSPC => return error.NoSpaceLeft,
131 os.EPERM => return error.AccessDenied,
132 os.EPIPE => return error.BrokenPipe,
133 else => |err| return os.unexpectedErrno(@intCast(usize, err)),
134 }
135}
100136
101137/// Deprecated; use `std.fs.Dir.writeFile`.
102138pub fn writeFile(path: []const u8, data: []const u8) !void {
......@@ -495,139 +531,18 @@ test "io.SliceOutStream" {
495531 testing.expectEqualSlices(u8, "HelloWorld!", slice_stream.getWritten());
496532}
497533
498var null_out_stream_state = NullOutStream.init();
499pub const null_out_stream = &null_out_stream_state.stream;
500
501534/// An OutStream that doesn't write to anything.
502pub const NullOutStream = struct {
503 pub const Error = error{};
504 pub const Stream = OutStream(Error);
505
506 stream: Stream,
507
508 pub fn init() NullOutStream {
509 return NullOutStream{
510 .stream = Stream{ .writeFn = writeFn },
511 };
512 }
513
514 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
515 return bytes.len;
516 }
517};
535pub const null_out_stream = @as(NullOutStream, .{ .context = {} });
518536
519test "io.NullOutStream" {
520 var null_stream = NullOutStream.init();
521 const stream = &null_stream.stream;
522 stream.write("yay" ** 10000) catch unreachable;
537const NullOutStream = OutStream(void, error{}, dummyWrite);
538fn dummyWrite(context: void, data: []const u8) error{}!usize {
539 return data.len;
523540}
524541
525/// An OutStream that counts how many bytes has been written to it.
526pub fn CountingOutStream(comptime OutStreamError: type) type {
527 return struct {
528 const Self = @This();
529 pub const Stream = OutStream(Error);
530 pub const Error = OutStreamError;
531
532 stream: Stream,
533 bytes_written: u64,
534 child_stream: *Stream,
535
536 pub fn init(child_stream: *Stream) Self {
537 return Self{
538 .stream = Stream{ .writeFn = writeFn },
539 .bytes_written = 0,
540 .child_stream = child_stream,
541 };
542 }
543
544 fn writeFn(out_stream: *Stream, bytes: []const u8) OutStreamError!usize {
545 const self = @fieldParentPtr(Self, "stream", out_stream);
546 try self.child_stream.write(bytes);
547 self.bytes_written += bytes.len;
548 return bytes.len;
549 }
550 };
551}
552
553test "io.CountingOutStream" {
554 var null_stream = NullOutStream.init();
555 var counting_stream = CountingOutStream(NullOutStream.Error).init(&null_stream.stream);
556 const stream = &counting_stream.stream;
557
558 const bytes = "yay" ** 10000;
559 stream.write(bytes) catch unreachable;
560 testing.expect(counting_stream.bytes_written == bytes.len);
561}
562
563pub fn BufferedOutStream(comptime Error: type) type {
564 return BufferedOutStreamCustom(mem.page_size, Error);
565}
566
567pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type {
568 return struct {
569 const Self = @This();
570 pub const Stream = OutStream(Error);
571 pub const Error = OutStreamError;
572
573 stream: Stream,
574
575 unbuffered_out_stream: *Stream,
576
577 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
578 fifo: FifoType,
579
580 pub fn init(unbuffered_out_stream: *Stream) Self {
581 return Self{
582 .unbuffered_out_stream = unbuffered_out_stream,
583 .fifo = FifoType.init(),
584 .stream = Stream{ .writeFn = writeFn },
585 };
586 }
587
588 pub fn flush(self: *Self) !void {
589 while (true) {
590 const slice = self.fifo.readableSlice(0);
591 if (slice.len == 0) break;
592 try self.unbuffered_out_stream.write(slice);
593 self.fifo.discard(slice.len);
594 }
595 }
596
597 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
598 const self = @fieldParentPtr(Self, "stream", out_stream);
599 if (bytes.len >= self.fifo.writableLength()) {
600 try self.flush();
601 return self.unbuffered_out_stream.writeOnce(bytes);
602 }
603 self.fifo.writeAssumeCapacity(bytes);
604 return bytes.len;
605 }
606 };
542test "null_out_stream" {
543 null_out_stream.writeAll("yay" ** 1000) catch |err| switch (err) {};
607544}
608545
609/// Implementation of OutStream trait for Buffer
610pub const BufferOutStream = struct {
611 buffer: *Buffer,
612 stream: Stream,
613
614 pub const Error = error{OutOfMemory};
615 pub const Stream = OutStream(Error);
616
617 pub fn init(buffer: *Buffer) BufferOutStream {
618 return BufferOutStream{
619 .buffer = buffer,
620 .stream = Stream{ .writeFn = writeFn },
621 };
622 }
623
624 fn writeFn(out_stream: *Stream, bytes: []const u8) !usize {
625 const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
626 try self.buffer.append(bytes);
627 return bytes.len;
628 }
629};
630
631546/// Creates a stream which allows for writing bit fields to another stream
632547pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
633548 return struct {
......@@ -752,52 +667,11 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
752667 return buffer.len;
753668 }
754669
755 return self.out_stream.writeOnce(buffer);
670 return self.out_stream.write(buffer);
756671 }
757672 };
758673}
759674
760pub const BufferedAtomicFile = struct {
761 atomic_file: fs.AtomicFile,
762 file_stream: File.OutStream,
763 buffered_stream: BufferedOutStream(File.WriteError),
764 allocator: *mem.Allocator,
765
766 pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
767 // TODO with well defined copy elision we don't need this allocation
768 var self = try allocator.create(BufferedAtomicFile);
769 self.* = BufferedAtomicFile{
770 .atomic_file = undefined,
771 .file_stream = undefined,
772 .buffered_stream = undefined,
773 .allocator = allocator,
774 };
775 errdefer allocator.destroy(self);
776
777 self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode);
778 errdefer self.atomic_file.deinit();
779
780 self.file_stream = self.atomic_file.file.outStream();
781 self.buffered_stream = BufferedOutStream(File.WriteError).init(&self.file_stream.stream);
782 return self;
783 }
784
785 /// always call destroy, even after successful finish()
786 pub fn destroy(self: *BufferedAtomicFile) void {
787 self.atomic_file.deinit();
788 self.allocator.destroy(self);
789 }
790
791 pub fn finish(self: *BufferedAtomicFile) !void {
792 try self.buffered_stream.flush();
793 try self.atomic_file.finish();
794 }
795
796 pub fn stream(self: *BufferedAtomicFile) *OutStream(File.WriteError) {
797 return &self.buffered_stream.stream;
798 }
799};
800
801675pub const Packing = enum {
802676 /// Pack data to byte alignment
803677 Byte,
......@@ -1129,8 +1003,9 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
11291003 };
11301004}
11311005
1132test "import io tests" {
1006test "" {
11331007 comptime {
11341008 _ = @import("io/test.zig");
11351009 }
1010 std.meta.refAllDecls(@This());
11361011}
lib/std/io/buffered_atomic_file.zig created+50
......@@ -0,0 +1,50 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const File = std.fs.File;
5
6pub const BufferedAtomicFile = struct {
7 atomic_file: fs.AtomicFile,
8 file_stream: File.OutStream,
9 buffered_stream: BufferedOutStream,
10 allocator: *mem.Allocator,
11
12 pub const buffer_size = 4096;
13 pub const BufferedOutStream = std.io.BufferedOutStreamCustom(buffer_size, File.OutStream);
14 pub const OutStream = std.io.OutStream(*BufferedOutStream, BufferedOutStream.Error, BufferedOutStream.write);
15
16 /// TODO when https://github.com/ziglang/zig/issues/2761 is solved
17 /// this API will not need an allocator
18 pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
19 var self = try allocator.create(BufferedAtomicFile);
20 self.* = BufferedAtomicFile{
21 .atomic_file = undefined,
22 .file_stream = undefined,
23 .buffered_stream = undefined,
24 .allocator = allocator,
25 };
26 errdefer allocator.destroy(self);
27
28 self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode);
29 errdefer self.atomic_file.deinit();
30
31 self.file_stream = self.atomic_file.file.outStream();
32 self.buffered_stream = std.io.bufferedOutStream(buffer_size, self.file_stream);
33 return self;
34 }
35
36 /// always call destroy, even after successful finish()
37 pub fn destroy(self: *BufferedAtomicFile) void {
38 self.atomic_file.deinit();
39 self.allocator.destroy(self);
40 }
41
42 pub fn finish(self: *BufferedAtomicFile) !void {
43 try self.buffered_stream.flush();
44 try self.atomic_file.finish();
45 }
46
47 pub fn stream(self: *BufferedAtomicFile) OutStream {
48 return .{ .context = &self.buffered_stream };
49 }
50};
lib/std/io/buffered_out_stream.zig created+56
......@@ -0,0 +1,56 @@
1const std = @import("../std.zig");
2const io = std.io;
3
4pub fn BufferedOutStream(comptime OutStreamType: type) type {
5 return BufferedOutStreamCustom(4096, OutStreamType);
6}
7
8pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamType: type) type {
9 return struct {
10 unbuffered_out_stream: OutStreamType,
11 fifo: FifoType,
12
13 pub const Error = OutStreamType.Error;
14 pub const OutStream = io.OutStream(*Self, Error, write);
15
16 const Self = @This();
17 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
18
19 pub fn init(unbuffered_out_stream: OutStreamType) Self {
20 return Self{
21 .unbuffered_out_stream = unbuffered_out_stream,
22 .fifo = FifoType.init(),
23 };
24 }
25
26 pub fn flush(self: *Self) !void {
27 while (true) {
28 const slice = self.fifo.readableSlice(0);
29 if (slice.len == 0) break;
30 try self.unbuffered_out_stream.writeAll(slice);
31 self.fifo.discard(slice.len);
32 }
33 }
34
35 pub fn outStream(self: *Self) OutStream {
36 return .{ .context = self };
37 }
38
39 pub fn write(self: *Self, bytes: []const u8) Error!usize {
40 if (bytes.len >= self.fifo.writableLength()) {
41 try self.flush();
42 return self.unbuffered_out_stream.write(bytes);
43 }
44 self.fifo.writeAssumeCapacity(bytes);
45 return bytes.len;
46 }
47 };
48}
49
50pub fn bufferedOutStream(
51 comptime buffer_size: usize,
52 underlying_stream: var,
53) BufferedOutStreamCustom(buffer_size, @TypeOf(underlying_stream)) {
54 return BufferedOutStreamCustom(buffer_size, @TypeOf(underlying_stream)).init(underlying_stream);
55}
56
lib/std/io/c_out_stream.zig deleted-43
......@@ -1,43 +0,0 @@
1const std = @import("../std.zig");
2const os = std.os;
3const OutStream = std.io.OutStream;
4const builtin = @import("builtin");
5
6/// TODO make a proposal to make `std.fs.File` use *FILE when linking libc and this just becomes
7/// std.io.FileOutStream because std.fs.File.write would do this when linking
8/// libc.
9pub const COutStream = struct {
10 pub const Error = std.fs.File.WriteError;
11 pub const Stream = OutStream(Error);
12
13 stream: Stream,
14 c_file: *std.c.FILE,
15
16 pub fn init(c_file: *std.c.FILE) COutStream {
17 return COutStream{
18 .c_file = c_file,
19 .stream = Stream{ .writeFn = writeFn },
20 };
21 }
22
23 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
24 const self = @fieldParentPtr(COutStream, "stream", out_stream);
25 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file);
26 if (amt_written >= 0) return amt_written;
27 switch (std.c._errno().*) {
28 0 => unreachable,
29 os.EINVAL => unreachable,
30 os.EFAULT => unreachable,
31 os.EAGAIN => unreachable, // this is a blocking API
32 os.EBADF => unreachable, // always a race condition
33 os.EDESTADDRREQ => unreachable, // connect was never called
34 os.EDQUOT => return error.DiskQuota,
35 os.EFBIG => return error.FileTooBig,
36 os.EIO => return error.InputOutput,
37 os.ENOSPC => return error.NoSpaceLeft,
38 os.EPERM => return error.AccessDenied,
39 os.EPIPE => return error.BrokenPipe,
40 else => |err| return os.unexpectedErrno(@intCast(usize, err)),
41 }
42 }
43};
lib/std/io/counting_out_stream.zig created+42
......@@ -0,0 +1,42 @@
1const std = @import("../std.zig");
2const io = std.io;
3
4/// An OutStream that counts how many bytes has been written to it.
5pub fn CountingOutStream(comptime OutStreamType: type) type {
6 return struct {
7 bytes_written: u64,
8 child_stream: OutStreamType,
9
10 pub const Error = OutStreamType.Error;
11 pub const OutStream = io.OutStream(*Self, Error, write);
12
13 const Self = @This();
14
15 pub fn init(child_stream: OutStreamType) Self {
16 return Self{
17 .bytes_written = 0,
18 .child_stream = child_stream,
19 };
20 }
21
22 pub fn write(self: *Self, bytes: []const u8) Error!usize {
23 const amt = try self.child_stream.write(bytes);
24 self.bytes_written += amt;
25 return amt;
26 }
27
28 pub fn outStream(self: *Self) OutStream {
29 return .{ .context = self };
30 }
31 };
32}
33
34test "io.CountingOutStream" {
35 var counting_stream = CountingOutStream(NullOutStream.Error).init(std.io.null_out_stream);
36 const stream = &counting_stream.stream;
37
38 const bytes = "yay" ** 10000;
39 stream.write(bytes) catch unreachable;
40 testing.expect(counting_stream.bytes_written == bytes.len);
41}
42
lib/std/io/fixed_buffer_stream.zig created+66
......@@ -0,0 +1,66 @@
1const std = @import("../std.zig");
2const io = std.io;
3
4pub const FixedBufferInStream = struct {
5 bytes: []const u8,
6 pos: usize,
7
8 pub const SeekError = error{EndOfStream};
9 pub const GetSeekPosError = error{};
10
11 pub const InStream = io.InStream(*FixedBufferInStream, error{}, read);
12
13 pub fn inStream(self: *FixedBufferInStream) InStream {
14 return .{ .context = self };
15 }
16
17 pub const SeekableStream = io.SeekableStream(
18 *FixedBufferInStream,
19 SeekError,
20 GetSeekPosError,
21 seekTo,
22 seekBy,
23 getPos,
24 getEndPos,
25 );
26
27 pub fn seekableStream(self: *FixedBufferInStream) SeekableStream {
28 return .{ .context = self };
29 }
30
31 pub fn read(self: *FixedBufferInStream, dest: []u8) error{}!usize {
32 const size = std.math.min(dest.len, self.bytes.len - self.pos);
33 const end = self.pos + size;
34
35 std.mem.copy(u8, dest[0..size], self.bytes[self.pos..end]);
36 self.pos = end;
37
38 return size;
39 }
40
41 pub fn seekTo(self: *FixedBufferInStream, pos: u64) SeekError!void {
42 const usize_pos = std.math.cast(usize, pos) catch return error.EndOfStream;
43 if (usize_pos > self.bytes.len) return error.EndOfStream;
44 self.pos = usize_pos;
45 }
46
47 pub fn seekBy(self: *FixedBufferInStream, amt: i64) SeekError!void {
48 if (amt < 0) {
49 const abs_amt = std.math.cast(usize, -amt) catch return error.EndOfStream;
50 if (abs_amt > self.pos) return error.EndOfStream;
51 self.pos -= abs_amt;
52 } else {
53 const usize_amt = std.math.cast(usize, amt) catch return error.EndOfStream;
54 if (self.pos + usize_amt > self.bytes.len) return error.EndOfStream;
55 self.pos += usize_amt;
56 }
57 }
58
59 pub fn getEndPos(self: *FixedBufferInStream) GetSeekPosError!u64 {
60 return self.bytes.len;
61 }
62
63 pub fn getPos(self: *FixedBufferInStream) GetSeekPosError!u64 {
64 return self.pos;
65 }
66};
lib/std/io/in_stream.zig+34-47
......@@ -1,44 +1,31 @@
11const std = @import("../std.zig");
2const builtin = @import("builtin");
3const root = @import("root");
2const builtin = std.builtin;
43const math = std.math;
54const assert = std.debug.assert;
65const mem = std.mem;
76const Buffer = std.Buffer;
87const testing = std.testing;
98
10pub const default_stack_size = 1 * 1024 * 1024;
11pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))
12 root.stack_size_std_io_InStream
13else
14 default_stack_size;
15
16pub fn InStream(comptime ReadError: type) type {
9pub fn InStream(
10 comptime Context: type,
11 comptime ReadError: type,
12 /// Returns the number of bytes read. It may be less than buffer.len.
13 /// If the number of bytes read is 0, it means end of stream.
14 /// End of stream is not an error condition.
15 comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
16) type {
1717 return struct {
18 const Self = @This();
1918 pub const Error = ReadError;
20 pub const ReadFn = if (std.io.is_async)
21 async fn (self: *Self, buffer: []u8) Error!usize
22 else
23 fn (self: *Self, buffer: []u8) Error!usize;
2419
25 /// Returns the number of bytes read. It may be less than buffer.len.
26 /// If the number of bytes read is 0, it means end of stream.
27 /// End of stream is not an error condition.
28 readFn: ReadFn,
20 context: Context,
21
22 const Self = @This();
2923
3024 /// Returns the number of bytes read. It may be less than buffer.len.
3125 /// If the number of bytes read is 0, it means end of stream.
3226 /// End of stream is not an error condition.
33 pub fn read(self: *Self, buffer: []u8) Error!usize {
34 if (std.io.is_async) {
35 // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read.
36 @setRuntimeSafety(false);
37 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
38 return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer);
39 } else {
40 return self.readFn(self, buffer);
41 }
27 pub fn read(self: Self, buffer: []u8) Error!usize {
28 return readFn(self.context, buffer);
4229 }
4330
4431 /// Deprecated: use `readAll`.
......@@ -47,7 +34,7 @@ pub fn InStream(comptime ReadError: type) type {
4734 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
4835 /// means the stream reached the end. Reaching the end of a stream is not an error
4936 /// condition.
50 pub fn readAll(self: *Self, buffer: []u8) Error!usize {
37 pub fn readAll(self: Self, buffer: []u8) Error!usize {
5138 var index: usize = 0;
5239 while (index != buffer.len) {
5340 const amt = try self.read(buffer[index..]);
......@@ -59,13 +46,13 @@ pub fn InStream(comptime ReadError: type) type {
5946
6047 /// Returns the number of bytes read. If the number read would be smaller than buf.len,
6148 /// error.EndOfStream is returned instead.
62 pub fn readNoEof(self: *Self, buf: []u8) !void {
49 pub fn readNoEof(self: Self, buf: []u8) !void {
6350 const amt_read = try self.readAll(buf);
6451 if (amt_read < buf.len) return error.EndOfStream;
6552 }
6653
6754 /// Deprecated: use `readAllArrayList`.
68 pub fn readAllBuffer(self: *Self, buffer: *Buffer, max_size: usize) !void {
55 pub fn readAllBuffer(self: Self, buffer: *Buffer, max_size: usize) !void {
6956 buffer.list.shrink(0);
7057 try self.readAllArrayList(&buffer.list, max_size);
7158 errdefer buffer.shrink(0);
......@@ -75,7 +62,7 @@ pub fn InStream(comptime ReadError: type) type {
7562 /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found.
7663 /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned
7764 /// and the `std.ArrayList` has exactly `max_append_size` bytes appended.
78 pub fn readAllArrayList(self: *Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void {
65 pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void {
7966 try array_list.ensureCapacity(math.min(max_append_size, 4096));
8067 const original_len = array_list.len;
8168 var start_index: usize = original_len;
......@@ -104,7 +91,7 @@ pub fn InStream(comptime ReadError: type) type {
10491 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
10592 /// Caller owns returned memory.
10693 /// If this function returns an error, the contents from the stream read so far are lost.
107 pub fn readAllAlloc(self: *Self, allocator: *mem.Allocator, max_size: usize) ![]u8 {
94 pub fn readAllAlloc(self: Self, allocator: *mem.Allocator, max_size: usize) ![]u8 {
10895 var array_list = std.ArrayList(u8).init(allocator);
10996 defer array_list.deinit();
11097 try self.readAllArrayList(&array_list, max_size);
......@@ -116,7 +103,7 @@ pub fn InStream(comptime ReadError: type) type {
116103 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
117104 /// `std.ArrayList` is populated with `max_size` bytes from the stream.
118105 pub fn readUntilDelimiterArrayList(
119 self: *Self,
106 self: Self,
120107 array_list: *std.ArrayList(u8),
121108 delimiter: u8,
122109 max_size: usize,
......@@ -142,7 +129,7 @@ pub fn InStream(comptime ReadError: type) type {
142129 /// Caller owns returned memory.
143130 /// If this function returns an error, the contents from the stream read so far are lost.
144131 pub fn readUntilDelimiterAlloc(
145 self: *Self,
132 self: Self,
146133 allocator: *mem.Allocator,
147134 delimiter: u8,
148135 max_size: usize,
......@@ -159,7 +146,7 @@ pub fn InStream(comptime ReadError: type) type {
159146 /// function is called again after that, returns null.
160147 /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
161148 /// delimiter byte is not included in the returned slice.
162 pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 {
149 pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {
163150 var index: usize = 0;
164151 while (true) {
165152 const byte = self.readByte() catch |err| switch (err) {
......@@ -184,7 +171,7 @@ pub fn InStream(comptime ReadError: type) type {
184171 /// Reads from the stream until specified byte is found, discarding all data,
185172 /// including the delimiter.
186173 /// If end-of-stream is found, this function succeeds.
187 pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void {
174 pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void {
188175 while (true) {
189176 const byte = self.readByte() catch |err| switch (err) {
190177 error.EndOfStream => return,
......@@ -195,7 +182,7 @@ pub fn InStream(comptime ReadError: type) type {
195182 }
196183
197184 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
198 pub fn readByte(self: *Self) !u8 {
185 pub fn readByte(self: Self) !u8 {
199186 var result: [1]u8 = undefined;
200187 const amt_read = try self.read(result[0..]);
201188 if (amt_read < 1) return error.EndOfStream;
......@@ -203,43 +190,43 @@ pub fn InStream(comptime ReadError: type) type {
203190 }
204191
205192 /// Same as `readByte` except the returned byte is signed.
206 pub fn readByteSigned(self: *Self) !i8 {
193 pub fn readByteSigned(self: Self) !i8 {
207194 return @bitCast(i8, try self.readByte());
208195 }
209196
210197 /// Reads a native-endian integer
211 pub fn readIntNative(self: *Self, comptime T: type) !T {
198 pub fn readIntNative(self: Self, comptime T: type) !T {
212199 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
213200 try self.readNoEof(bytes[0..]);
214201 return mem.readIntNative(T, &bytes);
215202 }
216203
217204 /// Reads a foreign-endian integer
218 pub fn readIntForeign(self: *Self, comptime T: type) !T {
205 pub fn readIntForeign(self: Self, comptime T: type) !T {
219206 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
220207 try self.readNoEof(bytes[0..]);
221208 return mem.readIntForeign(T, &bytes);
222209 }
223210
224 pub fn readIntLittle(self: *Self, comptime T: type) !T {
211 pub fn readIntLittle(self: Self, comptime T: type) !T {
225212 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
226213 try self.readNoEof(bytes[0..]);
227214 return mem.readIntLittle(T, &bytes);
228215 }
229216
230 pub fn readIntBig(self: *Self, comptime T: type) !T {
217 pub fn readIntBig(self: Self, comptime T: type) !T {
231218 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
232219 try self.readNoEof(bytes[0..]);
233220 return mem.readIntBig(T, &bytes);
234221 }
235222
236 pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
223 pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T {
237224 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
238225 try self.readNoEof(bytes[0..]);
239226 return mem.readInt(T, &bytes, endian);
240227 }
241228
242 pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
229 pub fn readVarInt(self: Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
243230 assert(size <= @sizeOf(ReturnType));
244231 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
245232 const bytes = bytes_buf[0..size];
......@@ -247,14 +234,14 @@ pub fn InStream(comptime ReadError: type) type {
247234 return mem.readVarInt(ReturnType, bytes, endian);
248235 }
249236
250 pub fn skipBytes(self: *Self, num_bytes: u64) !void {
237 pub fn skipBytes(self: Self, num_bytes: u64) !void {
251238 var i: u64 = 0;
252239 while (i < num_bytes) : (i += 1) {
253240 _ = try self.readByte();
254241 }
255242 }
256243
257 pub fn readStruct(self: *Self, comptime T: type) !T {
244 pub fn readStruct(self: Self, comptime T: type) !T {
258245 // Only extern and packed structs have defined in-memory layout.
259246 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
260247 var res: [1]T = undefined;
......@@ -265,7 +252,7 @@ pub fn InStream(comptime ReadError: type) type {
265252 /// Reads an integer with the same size as the given enum's tag type. If the integer matches
266253 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error.
267254 /// TODO optimization taking advantage of most fields being in order
268 pub fn readEnum(self: *Self, comptime Enum: type, endian: builtin.Endian) !Enum {
255 pub fn readEnum(self: Self, comptime Enum: type, endian: builtin.Endian) !Enum {
269256 const E = error{
270257 /// An integer was read, but it did not match any of the tags in the supplied enum.
271258 InvalidValue,
lib/std/io/out_stream.zig+33-42
......@@ -1,94 +1,85 @@
11const std = @import("../std.zig");
2const builtin = @import("builtin");
3const root = @import("root");
2const builtin = std.builtin;
43const mem = std.mem;
54
6pub const default_stack_size = 1 * 1024 * 1024;
7pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))
8 root.stack_size_std_io_OutStream
9else
10 default_stack_size;
11
12pub fn OutStream(comptime WriteError: type) type {
5pub fn OutStream(
6 comptime Context: type,
7 comptime WriteError: type,
8 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
9) type {
1310 return struct {
11 context: Context,
12
1413 const Self = @This();
1514 pub const Error = WriteError;
16 pub const WriteFn = if (std.io.is_async)
17 async fn (self: *Self, bytes: []const u8) Error!usize
18 else
19 fn (self: *Self, bytes: []const u8) Error!usize;
2015
21 writeFn: WriteFn,
22
23 pub fn writeOnce(self: *Self, bytes: []const u8) Error!usize {
24 if (std.io.is_async) {
25 // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write.
26 @setRuntimeSafety(false);
27 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
28 return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes);
29 } else {
30 return self.writeFn(self, bytes);
31 }
16 pub fn write(self: Self, bytes: []const u8) Error!usize {
17 return writeFn(self.context, bytes);
3218 }
3319
34 pub fn write(self: *Self, bytes: []const u8) Error!void {
20 pub fn writeAll(self: Self, bytes: []const u8) Error!void {
3521 var index: usize = 0;
3622 while (index != bytes.len) {
37 index += try self.writeOnce(bytes[index..]);
23 index += try self.write(bytes[index..]);
3824 }
3925 }
4026
41 pub fn print(self: *Self, comptime format: []const u8, args: var) Error!void {
42 return std.fmt.format(self, Error, write, format, args);
27 pub fn print(self: Self, comptime format: []const u8, args: var) Error!void {
28 return std.fmt.format(self, Error, writeAll, format, args);
4329 }
4430
45 pub fn writeByte(self: *Self, byte: u8) Error!void {
31 pub fn writeByte(self: Self, byte: u8) Error!void {
4632 const array = [1]u8{byte};
47 return self.write(&array);
33 return self.writeAll(&array);
4834 }
4935
50 pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
36 pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
5137 var bytes: [256]u8 = undefined;
5238 mem.set(u8, bytes[0..], byte);
5339
5440 var remaining: usize = n;
5541 while (remaining > 0) {
5642 const to_write = std.math.min(remaining, bytes.len);
57 try self.write(bytes[0..to_write]);
43 try self.writeAll(bytes[0..to_write]);
5844 remaining -= to_write;
5945 }
6046 }
6147
6248 /// Write a native-endian integer.
63 pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
49 /// TODO audit non-power-of-two int sizes
50 pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void {
6451 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
6552 mem.writeIntNative(T, &bytes, value);
66 return self.write(&bytes);
53 return self.writeAll(&bytes);
6754 }
6855
6956 /// Write a foreign-endian integer.
70 pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
57 /// TODO audit non-power-of-two int sizes
58 pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void {
7159 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
7260 mem.writeIntForeign(T, &bytes, value);
73 return self.write(&bytes);
61 return self.writeAll(&bytes);
7462 }
7563
76 pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
64 /// TODO audit non-power-of-two int sizes
65 pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void {
7766 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
7867 mem.writeIntLittle(T, &bytes, value);
79 return self.write(&bytes);
68 return self.writeAll(&bytes);
8069 }
8170
82 pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
71 /// TODO audit non-power-of-two int sizes
72 pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void {
8373 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
8474 mem.writeIntBig(T, &bytes, value);
85 return self.write(&bytes);
75 return self.writeAll(&bytes);
8676 }
8777
88 pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
78 /// TODO audit non-power-of-two int sizes
79 pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
8980 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
9081 mem.writeInt(T, &bytes, value, endian);
91 return self.write(&bytes);
82 return self.writeAll(&bytes);
9283 }
9384 };
9485}
lib/std/io/seekable_stream.zig+19-86
......@@ -1,103 +1,36 @@
11const std = @import("../std.zig");
22const InStream = std.io.InStream;
33
4pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type {
4pub fn SeekableStream(
5 comptime Context: type,
6 comptime SeekErrorType: type,
7 comptime GetSeekPosErrorType: type,
8 comptime seekToFn: fn (context: Context, pos: u64) SeekErrorType!void,
9 comptime seekByFn: fn (context: Context, pos: i64) SeekErrorType!void,
10 comptime getPosFn: fn (context: Context) GetSeekPosErrorType!u64,
11 comptime getEndPosFn: fn (context: Context) GetSeekPosErrorType!u64,
12) type {
513 return struct {
14 context: Context,
15
616 const Self = @This();
717 pub const SeekError = SeekErrorType;
818 pub const GetSeekPosError = GetSeekPosErrorType;
919
10 seekToFn: fn (self: *Self, pos: u64) SeekError!void,
11 seekByFn: fn (self: *Self, pos: i64) SeekError!void,
12
13 getPosFn: fn (self: *Self) GetSeekPosError!u64,
14 getEndPosFn: fn (self: *Self) GetSeekPosError!u64,
15
16 pub fn seekTo(self: *Self, pos: u64) SeekError!void {
17 return self.seekToFn(self, pos);
20 pub fn seekTo(self: Self, pos: u64) SeekError!void {
21 return seekToFn(self.context, pos);
1822 }
1923
20 pub fn seekBy(self: *Self, amt: i64) SeekError!void {
21 return self.seekByFn(self, amt);
24 pub fn seekBy(self: Self, amt: i64) SeekError!void {
25 return seekByFn(self.context, amt);
2226 }
2327
24 pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
25 return self.getEndPosFn(self);
28 pub fn getEndPos(self: Self) GetSeekPosError!u64 {
29 return getEndPosFn(self.context);
2630 }
2731
28 pub fn getPos(self: *Self) GetSeekPosError!u64 {
29 return self.getPosFn(self);
32 pub fn getPos(self: Self) GetSeekPosError!u64 {
33 return getPosFn(self.context);
3034 }
3135 };
3236}
33
34pub const SliceSeekableInStream = struct {
35 const Self = @This();
36 pub const Error = error{};
37 pub const SeekError = error{EndOfStream};
38 pub const GetSeekPosError = error{};
39 pub const Stream = InStream(Error);
40 pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError);
41
42 stream: Stream,
43 seekable_stream: SeekableInStream,
44
45 pos: usize,
46 slice: []const u8,
47
48 pub fn init(slice: []const u8) Self {
49 return Self{
50 .slice = slice,
51 .pos = 0,
52 .stream = Stream{ .readFn = readFn },
53 .seekable_stream = SeekableInStream{
54 .seekToFn = seekToFn,
55 .seekByFn = seekByFn,
56 .getEndPosFn = getEndPosFn,
57 .getPosFn = getPosFn,
58 },
59 };
60 }
61
62 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
63 const self = @fieldParentPtr(Self, "stream", in_stream);
64 const size = std.math.min(dest.len, self.slice.len - self.pos);
65 const end = self.pos + size;
66
67 std.mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
68 self.pos = end;
69
70 return size;
71 }
72
73 fn seekToFn(in_stream: *SeekableInStream, pos: u64) SeekError!void {
74 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
75 const usize_pos = @intCast(usize, pos);
76 if (usize_pos > self.slice.len) return error.EndOfStream;
77 self.pos = usize_pos;
78 }
79
80 fn seekByFn(in_stream: *SeekableInStream, amt: i64) SeekError!void {
81 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
82
83 if (amt < 0) {
84 const abs_amt = @intCast(usize, -amt);
85 if (abs_amt > self.pos) return error.EndOfStream;
86 self.pos -= abs_amt;
87 } else {
88 const usize_amt = @intCast(usize, amt);
89 if (self.pos + usize_amt > self.slice.len) return error.EndOfStream;
90 self.pos += usize_amt;
91 }
92 }
93
94 fn getEndPosFn(in_stream: *SeekableInStream) GetSeekPosError!u64 {
95 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
96 return @intCast(u64, self.slice.len);
97 }
98
99 fn getPosFn(in_stream: *SeekableInStream) GetSeekPosError!u64 {
100 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
101 return @intCast(u64, self.pos);
102 }
103};
lib/std/json/write_stream.zig+16-16
......@@ -30,11 +30,11 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
3030 /// The string used as spacing.
3131 space: []const u8 = " ",
3232
33 stream: *OutStream,
33 stream: OutStream,
3434 state_index: usize,
3535 state: [max_depth]State,
3636
37 pub fn init(stream: *OutStream) Self {
37 pub fn init(stream: OutStream) Self {
3838 var self = Self{
3939 .stream = stream,
4040 .state_index = 1,
......@@ -90,8 +90,8 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
9090 self.pushState(.Value);
9191 try self.indent();
9292 try self.writeEscapedString(name);
93 try self.stream.write(":");
94 try self.stream.write(self.space);
93 try self.stream.writeAll(":");
94 try self.stream.writeAll(self.space);
9595 },
9696 }
9797 }
......@@ -134,16 +134,16 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
134134
135135 pub fn emitNull(self: *Self) !void {
136136 assert(self.state[self.state_index] == State.Value);
137 try self.stream.write("null");
137 try self.stream.writeAll("null");
138138 self.popState();
139139 }
140140
141141 pub fn emitBool(self: *Self, value: bool) !void {
142142 assert(self.state[self.state_index] == State.Value);
143143 if (value) {
144 try self.stream.write("true");
144 try self.stream.writeAll("true");
145145 } else {
146 try self.stream.write("false");
146 try self.stream.writeAll("false");
147147 }
148148 self.popState();
149149 }
......@@ -188,13 +188,13 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
188188 try self.stream.writeByte('"');
189189 for (string) |s| {
190190 switch (s) {
191 '"' => try self.stream.write("\\\""),
192 '\t' => try self.stream.write("\\t"),
193 '\r' => try self.stream.write("\\r"),
194 '\n' => try self.stream.write("\\n"),
195 8 => try self.stream.write("\\b"),
196 12 => try self.stream.write("\\f"),
197 '\\' => try self.stream.write("\\\\"),
191 '"' => try self.stream.writeAll("\\\""),
192 '\t' => try self.stream.writeAll("\\t"),
193 '\r' => try self.stream.writeAll("\\r"),
194 '\n' => try self.stream.writeAll("\\n"),
195 8 => try self.stream.writeAll("\\b"),
196 12 => try self.stream.writeAll("\\f"),
197 '\\' => try self.stream.writeAll("\\\\"),
198198 else => try self.stream.writeByte(s),
199199 }
200200 }
......@@ -231,10 +231,10 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
231231
232232 fn indent(self: *Self) !void {
233233 assert(self.state_index >= 1);
234 try self.stream.write(self.newline);
234 try self.stream.writeAll(self.newline);
235235 var i: usize = 0;
236236 while (i < self.state_index - 1) : (i += 1) {
237 try self.stream.write(self.one_indent);
237 try self.stream.writeAll(self.one_indent);
238238 }
239239 }
240240
lib/std/os.zig+1-1
......@@ -176,7 +176,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
176176 .io_mode = .blocking,
177177 .async_block_allowed = std.fs.File.async_block_allowed_yes,
178178 };
179 const stream = &file.inStream().stream;
179 const stream = file.inStream();
180180 stream.readNoEof(buf) catch return error.Unexpected;
181181}
182182
lib/std/pdb.zig+2-8
......@@ -632,11 +632,7 @@ const MsfStream = struct {
632632 blocks: []u32 = undefined,
633633 block_size: u32 = undefined,
634634
635 /// Implementation of InStream trait for Pdb.MsfStream
636 stream: Stream = undefined,
637
638635 pub const Error = @TypeOf(read).ReturnType.ErrorSet;
639 pub const Stream = io.InStream(Error);
640636
641637 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
642638 const stream = MsfStream{
......@@ -644,7 +640,6 @@ const MsfStream = struct {
644640 .pos = 0,
645641 .blocks = blocks,
646642 .block_size = block_size,
647 .stream = Stream{ .readFn = readFn },
648643 };
649644
650645 return stream;
......@@ -715,8 +710,7 @@ const MsfStream = struct {
715710 return block * self.block_size + offset;
716711 }
717712
718 fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
719 const self = @fieldParentPtr(MsfStream, "stream", in_stream);
720 return self.read(buffer);
713 fn inStream(self: *MsfStream) std.io.InStream(*MsfStream, Error, read) {
714 return .{ .context = self };
721715 }
722716};
lib/std/zig/ast.zig+1-1
......@@ -375,7 +375,7 @@ pub const Error = union(enum) {
375375 token: TokenIndex,
376376
377377 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
378 return stream.write(msg);
378 return stream.writeAll(msg);
379379 }
380380 };
381381 }
lib/std/zig/render.zig+66-73
......@@ -12,64 +12,58 @@ pub const Error = error{
1212};
1313
1414/// Returns whether anything changed
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Child.Error || Error)!bool {
16 comptime assert(@typeInfo(@TypeOf(stream)) == .Pointer);
17
18 var anything_changed: bool = false;
19
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
2016 // make a passthrough stream that checks whether something changed
2117 const MyStream = struct {
2218 const MyStream = @This();
23 const StreamError = @TypeOf(stream).Child.Error;
24 const Stream = std.io.OutStream(StreamError);
19 const StreamError = @TypeOf(stream).Error;
2520
26 anything_changed_ptr: *bool,
2721 child_stream: @TypeOf(stream),
28 stream: Stream,
22 anything_changed: bool,
2923 source_index: usize,
3024 source: []const u8,
3125
32 fn write(iface_stream: *Stream, bytes: []const u8) StreamError!usize {
33 const self = @fieldParentPtr(MyStream, "stream", iface_stream);
34
35 if (!self.anything_changed_ptr.*) {
26 fn write(self: *MyStream, bytes: []const u8) StreamError!usize {
27 if (!self.anything_changed) {
3628 const end = self.source_index + bytes.len;
3729 if (end > self.source.len) {
38 self.anything_changed_ptr.* = true;
30 self.anything_changed = true;
3931 } else {
4032 const src_slice = self.source[self.source_index..end];
4133 self.source_index += bytes.len;
4234 if (!mem.eql(u8, bytes, src_slice)) {
43 self.anything_changed_ptr.* = true;
35 self.anything_changed = true;
4436 }
4537 }
4638 }
4739
48 return self.child_stream.writeOnce(bytes);
40 return self.child_stream.write(bytes);
4941 }
5042 };
5143 var my_stream = MyStream{
52 .stream = MyStream.Stream{ .writeFn = MyStream.write },
5344 .child_stream = stream,
54 .anything_changed_ptr = &anything_changed,
45 .anything_changed = false,
5546 .source_index = 0,
5647 .source = tree.source,
5748 };
49 const my_stream_stream: std.io.OutStream(*MyStream, MyStream.StreamError, MyStream.write) = .{
50 .context = &my_stream,
51 };
5852
59 try renderRoot(allocator, &my_stream.stream, tree);
53 try renderRoot(allocator, my_stream_stream, tree);
6054
61 if (!anything_changed and my_stream.source_index != my_stream.source.len) {
62 anything_changed = true;
55 if (my_stream.source_index != my_stream.source.len) {
56 my_stream.anything_changed = true;
6357 }
6458
65 return anything_changed;
59 return my_stream.anything_changed;
6660}
6761
6862fn renderRoot(
6963 allocator: *mem.Allocator,
7064 stream: var,
7165 tree: *ast.Tree,
72) (@TypeOf(stream).Child.Error || Error)!void {
66) (@TypeOf(stream).Error || Error)!void {
7367 var tok_it = tree.tokens.iterator(0);
7468
7569 // render all the line comments at the beginning of the file
......@@ -189,7 +183,7 @@ fn renderRoot(
189183 }
190184}
191185
192fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Child.Error!void {
186fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
193187 const first_token = node.firstToken();
194188 var prev_token = first_token;
195189 if (prev_token == 0) return;
......@@ -204,11 +198,11 @@ fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *as
204198 }
205199}
206200
207fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Child.Error || Error)!void {
201fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
208202 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
209203}
210204
211fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Child.Error || Error)!void {
205fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
212206 switch (decl.id) {
213207 .FnProto => {
214208 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
......@@ -343,7 +337,7 @@ fn renderExpression(
343337 start_col: *usize,
344338 base: *ast.Node,
345339 space: Space,
346) (@TypeOf(stream).Child.Error || Error)!void {
340) (@TypeOf(stream).Error || Error)!void {
347341 switch (base.id) {
348342 .Identifier => {
349343 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
......@@ -449,9 +443,9 @@ fn renderExpression(
449443 switch (op_tok_id) {
450444 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
451445 .LBracket => if (tree.tokens.at(prefix_op_node.op_token + 2).id == .Identifier)
452 try stream.write("[*c")
446 try stream.writeAll("[*c")
453447 else
454 try stream.write("[*"),
448 try stream.writeAll("[*"),
455449 else => unreachable,
456450 }
457451 if (ptr_info.sentinel) |sentinel| {
......@@ -757,7 +751,7 @@ fn renderExpression(
757751 while (it.next()) |field_init| {
758752 var find_stream = FindByteOutStream.init('\n');
759753 var dummy_col: usize = 0;
760 try renderExpression(allocator, &find_stream.stream, tree, 0, &dummy_col, field_init.*, Space.None);
754 try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init.*, Space.None);
761755 if (find_stream.byte_found) break :blk false;
762756 }
763757 break :blk true;
......@@ -909,8 +903,7 @@ fn renderExpression(
909903 var column_widths = widths[widths.len - row_size ..];
910904
911905 // Null stream for counting the printed length of each expression
912 var null_stream = std.io.NullOutStream.init();
913 var counting_stream = std.io.CountingOutStream(std.io.NullOutStream.Error).init(&null_stream.stream);
906 var counting_stream = std.io.CountingOutStream(@TypeOf(std.io.null_out_stream)).init(std.io.null_out_stream);
914907
915908 var it = exprs.iterator(0);
916909 var i: usize = 0;
......@@ -918,7 +911,7 @@ fn renderExpression(
918911 while (it.next()) |expr| : (i += 1) {
919912 counting_stream.bytes_written = 0;
920913 var dummy_col: usize = 0;
921 try renderExpression(allocator, &counting_stream.stream, tree, indent, &dummy_col, expr.*, Space.None);
914 try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr.*, Space.None);
922915 const width = @intCast(usize, counting_stream.bytes_written);
923916 const col = i % row_size;
924917 column_widths[col] = std.math.max(column_widths[col], width);
......@@ -1336,7 +1329,7 @@ fn renderExpression(
13361329
13371330 // TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348
13381331 if (mem.eql(u8, tree.tokenSlicePtr(tree.tokens.at(builtin_call.builtin_token)), "@typeOf")) {
1339 try stream.write("@TypeOf");
1332 try stream.writeAll("@TypeOf");
13401333 } else {
13411334 try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name
13421335 }
......@@ -1505,9 +1498,9 @@ fn renderExpression(
15051498 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
15061499 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
15071500 } else if (cc_rewrite_str) |str| {
1508 try stream.write("callconv(");
1509 try stream.write(mem.toSliceConst(u8, str));
1510 try stream.write(") ");
1501 try stream.writeAll("callconv(");
1502 try stream.writeAll(mem.toSliceConst(u8, str));
1503 try stream.writeAll(") ");
15111504 }
15121505
15131506 switch (fn_proto.return_type) {
......@@ -1997,11 +1990,11 @@ fn renderExpression(
19971990 .AsmInput => {
19981991 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
19991992
2000 try stream.write("[");
1993 try stream.writeAll("[");
20011994 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);
2002 try stream.write("] ");
1995 try stream.writeAll("] ");
20031996 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);
2004 try stream.write(" (");
1997 try stream.writeAll(" (");
20051998 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);
20061999 return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )
20072000 },
......@@ -2009,18 +2002,18 @@ fn renderExpression(
20092002 .AsmOutput => {
20102003 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
20112004
2012 try stream.write("[");
2005 try stream.writeAll("[");
20132006 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);
2014 try stream.write("] ");
2007 try stream.writeAll("] ");
20152008 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);
2016 try stream.write(" (");
2009 try stream.writeAll(" (");
20172010
20182011 switch (asm_output.kind) {
20192012 ast.Node.AsmOutput.Kind.Variable => |variable_name| {
20202013 try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None);
20212014 },
20222015 ast.Node.AsmOutput.Kind.Return => |return_type| {
2023 try stream.write("-> ");
2016 try stream.writeAll("-> ");
20242017 try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);
20252018 },
20262019 }
......@@ -2052,7 +2045,7 @@ fn renderVarDecl(
20522045 indent: usize,
20532046 start_col: *usize,
20542047 var_decl: *ast.Node.VarDecl,
2055) (@TypeOf(stream).Child.Error || Error)!void {
2048) (@TypeOf(stream).Error || Error)!void {
20562049 if (var_decl.visib_token) |visib_token| {
20572050 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
20582051 }
......@@ -2125,7 +2118,7 @@ fn renderParamDecl(
21252118 start_col: *usize,
21262119 base: *ast.Node,
21272120 space: Space,
2128) (@TypeOf(stream).Child.Error || Error)!void {
2121) (@TypeOf(stream).Error || Error)!void {
21292122 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
21302123
21312124 try renderDocComments(tree, stream, param_decl, indent, start_col);
......@@ -2154,7 +2147,7 @@ fn renderStatement(
21542147 indent: usize,
21552148 start_col: *usize,
21562149 base: *ast.Node,
2157) (@TypeOf(stream).Child.Error || Error)!void {
2150) (@TypeOf(stream).Error || Error)!void {
21582151 switch (base.id) {
21592152 .VarDecl => {
21602153 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
......@@ -2193,7 +2186,7 @@ fn renderTokenOffset(
21932186 start_col: *usize,
21942187 space: Space,
21952188 token_skip_bytes: usize,
2196) (@TypeOf(stream).Child.Error || Error)!void {
2189) (@TypeOf(stream).Error || Error)!void {
21972190 if (space == Space.BlockStart) {
21982191 if (start_col.* < indent + indent_delta)
21992192 return renderToken(tree, stream, token_index, indent, start_col, Space.Space);
......@@ -2204,7 +2197,7 @@ fn renderTokenOffset(
22042197 }
22052198
22062199 var token = tree.tokens.at(token_index);
2207 try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
2200 try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
22082201
22092202 if (space == Space.NoComment)
22102203 return;
......@@ -2214,15 +2207,15 @@ fn renderTokenOffset(
22142207 if (space == Space.Comma) switch (next_token.id) {
22152208 .Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),
22162209 .LineComment => {
2217 try stream.write(", ");
2210 try stream.writeAll(", ");
22182211 return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);
22192212 },
22202213 else => {
22212214 if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == .MultilineStringLiteralLine) {
2222 try stream.write(",");
2215 try stream.writeAll(",");
22232216 return;
22242217 } else {
2225 try stream.write(",\n");
2218 try stream.writeAll(",\n");
22262219 start_col.* = 0;
22272220 return;
22282221 }
......@@ -2246,7 +2239,7 @@ fn renderTokenOffset(
22462239 if (next_token.id == .MultilineStringLiteralLine) {
22472240 return;
22482241 } else {
2249 try stream.write("\n");
2242 try stream.writeAll("\n");
22502243 start_col.* = 0;
22512244 return;
22522245 }
......@@ -2309,7 +2302,7 @@ fn renderTokenOffset(
23092302 if (next_token.id == .MultilineStringLiteralLine) {
23102303 return;
23112304 } else {
2312 try stream.write("\n");
2305 try stream.writeAll("\n");
23132306 start_col.* = 0;
23142307 return;
23152308 }
......@@ -2327,7 +2320,7 @@ fn renderTokenOffset(
23272320 const newline_count = if (loc.line == 1) @as(u8, 1) else @as(u8, 2);
23282321 try stream.writeByteNTimes('\n', newline_count);
23292322 try stream.writeByteNTimes(' ', indent);
2330 try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
2323 try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
23312324
23322325 offset += 1;
23332326 token = next_token;
......@@ -2338,7 +2331,7 @@ fn renderTokenOffset(
23382331 if (next_token.id == .MultilineStringLiteralLine) {
23392332 return;
23402333 } else {
2341 try stream.write("\n");
2334 try stream.writeAll("\n");
23422335 start_col.* = 0;
23432336 return;
23442337 }
......@@ -2381,7 +2374,7 @@ fn renderToken(
23812374 indent: usize,
23822375 start_col: *usize,
23832376 space: Space,
2384) (@TypeOf(stream).Child.Error || Error)!void {
2377) (@TypeOf(stream).Error || Error)!void {
23852378 return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);
23862379}
23872380
......@@ -2391,7 +2384,7 @@ fn renderDocComments(
23912384 node: var,
23922385 indent: usize,
23932386 start_col: *usize,
2394) (@TypeOf(stream).Child.Error || Error)!void {
2387) (@TypeOf(stream).Error || Error)!void {
23952388 const comment = node.doc_comments orelse return;
23962389 var it = comment.lines.iterator(0);
23972390 const first_token = node.firstToken();
......@@ -2401,7 +2394,7 @@ fn renderDocComments(
24012394 try stream.writeByteNTimes(' ', indent);
24022395 } else {
24032396 try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.NoComment);
2404 try stream.write("\n");
2397 try stream.writeAll("\n");
24052398 try stream.writeByteNTimes(' ', indent);
24062399 }
24072400 }
......@@ -2427,27 +2420,23 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
24272420 };
24282421}
24292422
2430// An OutStream that returns whether the given character has been written to it.
2431// The contents are not written to anything.
2423/// A `std.io.OutStream` that returns whether the given character has been written to it.
2424/// The contents are not written to anything.
24322425const FindByteOutStream = struct {
2433 const Self = FindByteOutStream;
2434 pub const Error = error{};
2435 pub const Stream = std.io.OutStream(Error);
2436
2437 stream: Stream,
24382426 byte_found: bool,
24392427 byte: u8,
24402428
2441 pub fn init(byte: u8) Self {
2442 return Self{
2443 .stream = Stream{ .writeFn = writeFn },
2429 pub const Error = error{};
2430 pub const OutStream = std.io.OutStream(*FindByteOutStream, Error, write);
2431
2432 pub fn init(byte: u8) FindByteOutStream {
2433 return FindByteOutStream{
24442434 .byte = byte,
24452435 .byte_found = false,
24462436 };
24472437 }
24482438
2449 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
2450 const self = @fieldParentPtr(Self, "stream", out_stream);
2439 pub fn write(self: *FindByteOutStream, bytes: []const u8) Error!usize {
24512440 if (self.byte_found) return bytes.len;
24522441 self.byte_found = blk: {
24532442 for (bytes) |b|
......@@ -2456,11 +2445,15 @@ const FindByteOutStream = struct {
24562445 };
24572446 return bytes.len;
24582447 }
2448
2449 pub fn outStream(self: *FindByteOutStream) OutStream {
2450 return .{ .context = self };
2451 }
24592452};
24602453
2461fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Child.Error!void {
2454fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void {
24622455 for (slice) |byte| switch (byte) {
2463 '\t' => try stream.write(" "),
2456 '\t' => try stream.writeAll(" "),
24642457 '\r' => {},
24652458 else => try stream.writeByte(byte),
24662459 };
src-self-hosted/libc_installation.zig+2-2
......@@ -38,7 +38,7 @@ pub const LibCInstallation = struct {
3838 pub fn parse(
3939 allocator: *Allocator,
4040 libc_file: []const u8,
41 stderr: *std.io.OutStream(fs.File.WriteError),
41 stderr: var,
4242 ) !LibCInstallation {
4343 var self: LibCInstallation = .{};
4444
......@@ -123,7 +123,7 @@ pub const LibCInstallation = struct {
123123 return self;
124124 }
125125
126 pub fn render(self: LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void {
126 pub fn render(self: LibCInstallation, out: var) !void {
127127 @setEvalBranchQuota(4000);
128128 const include_dir = self.include_dir orelse "";
129129 const sys_include_dir = self.sys_include_dir orelse "";
src-self-hosted/print_targets.zig+7-6
......@@ -52,7 +52,7 @@ const available_libcs = [_][]const u8{
5252 "sparc-linux-gnu",
5353 "sparcv9-linux-gnu",
5454 "wasm32-freestanding-musl",
55 "x86_64-linux-gnu (native)",
55 "x86_64-linux-gnu",
5656 "x86_64-linux-gnux32",
5757 "x86_64-linux-musl",
5858 "x86_64-windows-gnu",
......@@ -61,7 +61,8 @@ const available_libcs = [_][]const u8{
6161pub fn cmdTargets(
6262 allocator: *Allocator,
6363 args: []const []const u8,
64 stdout: *io.OutStream(fs.File.WriteError),
64 /// Output stream
65 stdout: var,
6566 native_target: Target,
6667) !void {
6768 const available_glibcs = blk: {
......@@ -92,9 +93,9 @@ pub fn cmdTargets(
9293 };
9394 defer allocator.free(available_glibcs);
9495
95 const BOS = io.BufferedOutStream(fs.File.WriteError);
96 var bos = BOS.init(stdout);
97 var jws = std.json.WriteStream(BOS.Stream, 6).init(&bos.stream);
96 var bos = io.bufferedOutStream(4096, stdout);
97 const bos_stream = bos.outStream();
98 var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
9899
99100 try jws.beginObject();
100101
......@@ -219,6 +220,6 @@ pub fn cmdTargets(
219220
220221 try jws.endObject();
221222
222 try bos.stream.writeByte('\n');
223 try bos_stream.writeByte('\n');
223224 return bos.flush();
224225}
src-self-hosted/stage2.zig+15-15
......@@ -18,8 +18,8 @@ const assert = std.debug.assert;
1818const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1919
2020var stderr_file: fs.File = undefined;
21var stderr: *io.OutStream(fs.File.WriteError) = undefined;
22var stdout: *io.OutStream(fs.File.WriteError) = undefined;
21var stderr: fs.File.OutStream = undefined;
22var stdout: fs.File.OutStream = undefined;
2323
2424comptime {
2525 _ = @import("dep_tokenizer.zig");
......@@ -146,7 +146,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error
146146}
147147
148148export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
149 const c_out_stream = &std.io.COutStream.init(output_file).stream;
149 const c_out_stream = std.io.cOutStream(output_file);
150150 _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
151151 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
152152 error.SystemResources => return .SystemResources,
......@@ -186,9 +186,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
186186 try args_list.append(mem.toSliceConst(u8, argv[arg_i]));
187187 }
188188
189 stdout = &std.io.getStdOut().outStream().stream;
189 stdout = std.io.getStdOut().outStream();
190190 stderr_file = std.io.getStdErr();
191 stderr = &stderr_file.outStream().stream;
191 stderr = stderr_file.outStream();
192192
193193 const args = args_list.toSliceConst()[2..];
194194
......@@ -203,11 +203,11 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
203203 const arg = args[i];
204204 if (mem.startsWith(u8, arg, "-")) {
205205 if (mem.eql(u8, arg, "--help")) {
206 try stdout.write(self_hosted_main.usage_fmt);
206 try stdout.writeAll(self_hosted_main.usage_fmt);
207207 process.exit(0);
208208 } else if (mem.eql(u8, arg, "--color")) {
209209 if (i + 1 >= args.len) {
210 try stderr.write("expected [auto|on|off] after --color\n");
210 try stderr.writeAll("expected [auto|on|off] after --color\n");
211211 process.exit(1);
212212 }
213213 i += 1;
......@@ -238,14 +238,14 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
238238
239239 if (stdin_flag) {
240240 if (input_files.len != 0) {
241 try stderr.write("cannot use --stdin with positional arguments\n");
241 try stderr.writeAll("cannot use --stdin with positional arguments\n");
242242 process.exit(1);
243243 }
244244
245245 const stdin_file = io.getStdIn();
246246 var stdin = stdin_file.inStream();
247247
248 const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size);
248 const source_code = try stdin.readAllAlloc(allocator, self_hosted_main.max_src_size);
249249 defer allocator.free(source_code);
250250
251251 const tree = std.zig.parse(allocator, source_code) catch |err| {
......@@ -272,7 +272,7 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
272272 }
273273
274274 if (input_files.len == 0) {
275 try stderr.write("expected at least one source file argument\n");
275 try stderr.writeAll("expected at least one source file argument\n");
276276 process.exit(1);
277277 }
278278
......@@ -409,11 +409,11 @@ fn printErrMsgToFile(
409409 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
410410
411411 var text_buf = try std.Buffer.initSize(allocator, 0);
412 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
412 const out_stream = &text_buf.outStream();
413413 try parse_error.render(&tree.tokens, out_stream);
414414 const text = text_buf.toOwnedSlice();
415415
416 const stream = &file.outStream().stream;
416 const stream = &file.outStream();
417417 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
418418
419419 if (!color_on) return;
......@@ -641,7 +641,7 @@ fn cmdTargets(zig_triple: [*:0]const u8) !void {
641641 return @import("print_targets.zig").cmdTargets(
642642 std.heap.c_allocator,
643643 &[0][]u8{},
644 &std.io.getStdOut().outStream().stream,
644 std.io.getStdOut().outStream(),
645645 target,
646646 );
647647}
......@@ -808,7 +808,7 @@ const Stage2LibCInstallation = extern struct {
808808// ABI warning
809809export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
810810 stderr_file = std.io.getStdErr();
811 stderr = &stderr_file.outStream().stream;
811 stderr = stderr_file.outStream();
812812 const libc_file = mem.toSliceConst(u8, libc_file_z);
813813 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
814814 error.ParseError => return .SemanticAnalyzeFail,
......@@ -870,7 +870,7 @@ export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
870870// ABI warning
871871export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {
872872 var libc = stage1_libc.toStage2();
873 const c_out_stream = &std.io.COutStream.init(output_file).stream;
873 const c_out_stream = std.io.cOutStream(output_file);
874874 libc.render(c_out_stream) catch |err| switch (err) {
875875 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
876876 error.SystemResources => return .SystemResources,