authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-11 18:54:52-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-03-11 18:54:52-04:00
log895f67cc6dfe3ade4b635c4c2168843b022edee7
tree78f817084e76780c1b3eaab961657b168736e4d3
parent571f3ed161455074be5f296b39b24cba554da8e0
parent06d2f53ece7328e6beedd5c846a5b25798ba74e3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4710 from ziglang/io-stream-iface

rework I/O stream abstractions

56 files changed, 2740 insertions(+), 2695 deletions(-)

doc/docgen.zig+44-51
...@@ -40,12 +40,9 @@ pub fn main() !void {...@@ -40,12 +40,9 @@ pub fn main() !void {
40 var out_file = try fs.cwd().createFile(out_file_name, .{});40 var out_file = try fs.cwd().createFile(out_file_name, .{});
41 defer out_file.close();41 defer out_file.close();
4242
43 var file_in_stream = in_file.inStream();43 const input_file_bytes = try in_file.inStream().readAllAlloc(allocator, max_doc_file_size);
4444
45 const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);45 var buffered_out_stream = io.bufferedOutStream(out_file.outStream());
46
47 var file_out_stream = out_file.outStream();
48 var buffered_out_stream = io.BufferedOutStream(fs.File.WriteError).init(&file_out_stream.stream);
4946
50 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);47 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
51 var toc = try genToc(allocator, &tokenizer);48 var toc = try genToc(allocator, &tokenizer);
...@@ -53,7 +50,7 @@ pub fn main() !void {...@@ -53,7 +50,7 @@ pub fn main() !void {
53 try fs.cwd().makePath(tmp_dir_name);50 try fs.cwd().makePath(tmp_dir_name);
54 defer fs.deleteTree(tmp_dir_name) catch {};51 defer fs.deleteTree(tmp_dir_name) catch {};
5552
56 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);53 try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);
57 try buffered_out_stream.flush();54 try buffered_out_stream.flush();
58}55}
5956
...@@ -327,8 +324,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -327,8 +324,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
327 var toc_buf = try std.Buffer.initSize(allocator, 0);324 var toc_buf = try std.Buffer.initSize(allocator, 0);
328 defer toc_buf.deinit();325 defer toc_buf.deinit();
329326
330 var toc_buf_adapter = io.BufferOutStream.init(&toc_buf);327 var toc = toc_buf.outStream();
331 var toc = &toc_buf_adapter.stream;
332328
333 var nodes = std.ArrayList(Node).init(allocator);329 var nodes = std.ArrayList(Node).init(allocator);
334 defer nodes.deinit();330 defer nodes.deinit();
...@@ -342,7 +338,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -342,7 +338,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
342 if (header_stack_size != 0) {338 if (header_stack_size != 0) {
343 return parseError(tokenizer, token, "unbalanced headers", .{});339 return parseError(tokenizer, token, "unbalanced headers", .{});
344 }340 }
345 try toc.write(" </ul>\n");341 try toc.writeAll(" </ul>\n");
346 break;342 break;
347 },343 },
348 Token.Id.Content => {344 Token.Id.Content => {
...@@ -407,7 +403,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -407,7 +403,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
407 if (last_columns) |n| {403 if (last_columns) |n| {
408 try toc.print("<ul style=\"columns: {}\">\n", .{n});404 try toc.print("<ul style=\"columns: {}\">\n", .{n});
409 } else {405 } else {
410 try toc.write("<ul>\n");406 try toc.writeAll("<ul>\n");
411 }407 }
412 } else {408 } else {
413 last_action = Action.Open;409 last_action = Action.Open;
...@@ -424,9 +420,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -424,9 +420,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
424420
425 if (last_action == Action.Close) {421 if (last_action == Action.Close) {
426 try toc.writeByteNTimes(' ', 8 + header_stack_size * 4);422 try toc.writeByteNTimes(' ', 8 + header_stack_size * 4);
427 try toc.write("</ul></li>\n");423 try toc.writeAll("</ul></li>\n");
428 } else {424 } else {
429 try toc.write("</li>\n");425 try toc.writeAll("</li>\n");
430 last_action = Action.Close;426 last_action = Action.Close;
431 }427 }
432 } else if (mem.eql(u8, tag_name, "see_also")) {428 } else if (mem.eql(u8, tag_name, "see_also")) {
...@@ -614,8 +610,7 @@ fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -614,8 +610,7 @@ fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
614 var buf = try std.Buffer.initSize(allocator, 0);610 var buf = try std.Buffer.initSize(allocator, 0);
615 defer buf.deinit();611 defer buf.deinit();
616612
617 var buf_adapter = io.BufferOutStream.init(&buf);613 const out = buf.outStream();
618 var out = &buf_adapter.stream;
619 for (input) |c| {614 for (input) |c| {
620 switch (c) {615 switch (c) {
621 'a'...'z', 'A'...'Z', '_', '-', '0'...'9' => {616 'a'...'z', 'A'...'Z', '_', '-', '0'...'9' => {
...@@ -634,8 +629,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -634,8 +629,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
634 var buf = try std.Buffer.initSize(allocator, 0);629 var buf = try std.Buffer.initSize(allocator, 0);
635 defer buf.deinit();630 defer buf.deinit();
636631
637 var buf_adapter = io.BufferOutStream.init(&buf);632 const out = buf.outStream();
638 var out = &buf_adapter.stream;
639 try writeEscaped(out, input);633 try writeEscaped(out, input);
640 return buf.toOwnedSlice();634 return buf.toOwnedSlice();
641}635}
...@@ -643,10 +637,10 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -643,10 +637,10 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
643fn writeEscaped(out: var, input: []const u8) !void {637fn writeEscaped(out: var, input: []const u8) !void {
644 for (input) |c| {638 for (input) |c| {
645 try switch (c) {639 try switch (c) {
646 '&' => out.write("&amp;"),640 '&' => out.writeAll("&amp;"),
647 '<' => out.write("&lt;"),641 '<' => out.writeAll("&lt;"),
648 '>' => out.write("&gt;"),642 '>' => out.writeAll("&gt;"),
649 '"' => out.write("&quot;"),643 '"' => out.writeAll("&quot;"),
650 else => out.writeByte(c),644 else => out.writeByte(c),
651 };645 };
652 }646 }
...@@ -681,8 +675,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -681,8 +675,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
681 var buf = try std.Buffer.initSize(allocator, 0);675 var buf = try std.Buffer.initSize(allocator, 0);
682 defer buf.deinit();676 defer buf.deinit();
683677
684 var buf_adapter = io.BufferOutStream.init(&buf);678 var out = buf.outStream();
685 var out = &buf_adapter.stream;
686 var number_start_index: usize = undefined;679 var number_start_index: usize = undefined;
687 var first_number: usize = undefined;680 var first_number: usize = undefined;
688 var second_number: usize = undefined;681 var second_number: usize = undefined;
...@@ -743,7 +736,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -743,7 +736,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
743 'm' => {736 'm' => {
744 state = TermState.Start;737 state = TermState.Start;
745 while (open_span_count != 0) : (open_span_count -= 1) {738 while (open_span_count != 0) : (open_span_count -= 1) {
746 try out.write("</span>");739 try out.writeAll("</span>");
747 }740 }
748 if (first_number != 0 or second_number != 0) {741 if (first_number != 0 or second_number != 0) {
749 try out.print("<span class=\"t{}_{}\">", .{ first_number, second_number });742 try out.print("<span class=\"t{}_{}\">", .{ first_number, second_number });
...@@ -774,7 +767,7 @@ fn isType(name: []const u8) bool {...@@ -774,7 +767,7 @@ fn isType(name: []const u8) bool {
774767
775fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void {768fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void {
776 const src = mem.trim(u8, raw_src, " \n");769 const src = mem.trim(u8, raw_src, " \n");
777 try out.write("<code class=\"zig\">");770 try out.writeAll("<code class=\"zig\">");
778 var tokenizer = std.zig.Tokenizer.init(src);771 var tokenizer = std.zig.Tokenizer.init(src);
779 var index: usize = 0;772 var index: usize = 0;
780 var next_tok_is_fn = false;773 var next_tok_is_fn = false;
...@@ -835,15 +828,15 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -835,15 +828,15 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
835 .Keyword_allowzero,828 .Keyword_allowzero,
836 .Keyword_while,829 .Keyword_while,
837 => {830 => {
838 try out.write("<span class=\"tok-kw\">");831 try out.writeAll("<span class=\"tok-kw\">");
839 try writeEscaped(out, src[token.start..token.end]);832 try writeEscaped(out, src[token.start..token.end]);
840 try out.write("</span>");833 try out.writeAll("</span>");
841 },834 },
842835
843 .Keyword_fn => {836 .Keyword_fn => {
844 try out.write("<span class=\"tok-kw\">");837 try out.writeAll("<span class=\"tok-kw\">");
845 try writeEscaped(out, src[token.start..token.end]);838 try writeEscaped(out, src[token.start..token.end]);
846 try out.write("</span>");839 try out.writeAll("</span>");
847 next_tok_is_fn = true;840 next_tok_is_fn = true;
848 },841 },
849842
...@@ -852,24 +845,24 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -852,24 +845,24 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
852 .Keyword_true,845 .Keyword_true,
853 .Keyword_false,846 .Keyword_false,
854 => {847 => {
855 try out.write("<span class=\"tok-null\">");848 try out.writeAll("<span class=\"tok-null\">");
856 try writeEscaped(out, src[token.start..token.end]);849 try writeEscaped(out, src[token.start..token.end]);
857 try out.write("</span>");850 try out.writeAll("</span>");
858 },851 },
859852
860 .StringLiteral,853 .StringLiteral,
861 .MultilineStringLiteralLine,854 .MultilineStringLiteralLine,
862 .CharLiteral,855 .CharLiteral,
863 => {856 => {
864 try out.write("<span class=\"tok-str\">");857 try out.writeAll("<span class=\"tok-str\">");
865 try writeEscaped(out, src[token.start..token.end]);858 try writeEscaped(out, src[token.start..token.end]);
866 try out.write("</span>");859 try out.writeAll("</span>");
867 },860 },
868861
869 .Builtin => {862 .Builtin => {
870 try out.write("<span class=\"tok-builtin\">");863 try out.writeAll("<span class=\"tok-builtin\">");
871 try writeEscaped(out, src[token.start..token.end]);864 try writeEscaped(out, src[token.start..token.end]);
872 try out.write("</span>");865 try out.writeAll("</span>");
873 },866 },
874867
875 .LineComment,868 .LineComment,
...@@ -877,16 +870,16 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -877,16 +870,16 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
877 .ContainerDocComment,870 .ContainerDocComment,
878 .ShebangLine,871 .ShebangLine,
879 => {872 => {
880 try out.write("<span class=\"tok-comment\">");873 try out.writeAll("<span class=\"tok-comment\">");
881 try writeEscaped(out, src[token.start..token.end]);874 try writeEscaped(out, src[token.start..token.end]);
882 try out.write("</span>");875 try out.writeAll("</span>");
883 },876 },
884877
885 .Identifier => {878 .Identifier => {
886 if (prev_tok_was_fn) {879 if (prev_tok_was_fn) {
887 try out.write("<span class=\"tok-fn\">");880 try out.writeAll("<span class=\"tok-fn\">");
888 try writeEscaped(out, src[token.start..token.end]);881 try writeEscaped(out, src[token.start..token.end]);
889 try out.write("</span>");882 try out.writeAll("</span>");
890 } else {883 } else {
891 const is_int = blk: {884 const is_int = blk: {
892 if (src[token.start] != 'i' and src[token.start] != 'u')885 if (src[token.start] != 'i' and src[token.start] != 'u')
...@@ -901,9 +894,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -901,9 +894,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
901 break :blk true;894 break :blk true;
902 };895 };
903 if (is_int or isType(src[token.start..token.end])) {896 if (is_int or isType(src[token.start..token.end])) {
904 try out.write("<span class=\"tok-type\">");897 try out.writeAll("<span class=\"tok-type\">");
905 try writeEscaped(out, src[token.start..token.end]);898 try writeEscaped(out, src[token.start..token.end]);
906 try out.write("</span>");899 try out.writeAll("</span>");
907 } else {900 } else {
908 try writeEscaped(out, src[token.start..token.end]);901 try writeEscaped(out, src[token.start..token.end]);
909 }902 }
...@@ -913,9 +906,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -913,9 +906,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
913 .IntegerLiteral,906 .IntegerLiteral,
914 .FloatLiteral,907 .FloatLiteral,
915 => {908 => {
916 try out.write("<span class=\"tok-number\">");909 try out.writeAll("<span class=\"tok-number\">");
917 try writeEscaped(out, src[token.start..token.end]);910 try writeEscaped(out, src[token.start..token.end]);
918 try out.write("</span>");911 try out.writeAll("</span>");
919 },912 },
920913
921 .Bang,914 .Bang,
...@@ -983,7 +976,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -983,7 +976,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
983 }976 }
984 index = token.end;977 index = token.end;
985 }978 }
986 try out.write("</code>");979 try out.writeAll("</code>");
987}980}
988981
989fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void {982fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void {
...@@ -1002,7 +995,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1002,7 +995,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1002 for (toc.nodes) |node| {995 for (toc.nodes) |node| {
1003 switch (node) {996 switch (node) {
1004 .Content => |data| {997 .Content => |data| {
1005 try out.write(data);998 try out.writeAll(data);
1006 },999 },
1007 .Link => |info| {1000 .Link => |info| {
1008 if (!toc.urls.contains(info.url)) {1001 if (!toc.urls.contains(info.url)) {
...@@ -1011,12 +1004,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1011,12 +1004,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1011 try out.print("<a href=\"#{}\">{}</a>", .{ info.url, info.name });1004 try out.print("<a href=\"#{}\">{}</a>", .{ info.url, info.name });
1012 },1005 },
1013 .Nav => {1006 .Nav => {
1014 try out.write(toc.toc);1007 try out.writeAll(toc.toc);
1015 },1008 },
1016 .Builtin => |tok| {1009 .Builtin => |tok| {
1017 try out.write("<pre>");1010 try out.writeAll("<pre>");
1018 try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code);1011 try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code);
1019 try out.write("</pre>");1012 try out.writeAll("</pre>");
1020 },1013 },
1021 .HeaderOpen => |info| {1014 .HeaderOpen => |info| {
1022 try out.print(1015 try out.print(
...@@ -1025,7 +1018,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1025,7 +1018,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1025 );1018 );
1026 },1019 },
1027 .SeeAlso => |items| {1020 .SeeAlso => |items| {
1028 try out.write("<p>See also:</p><ul>\n");1021 try out.writeAll("<p>See also:</p><ul>\n");
1029 for (items) |item| {1022 for (items) |item| {
1030 const url = try urlize(allocator, item.name);1023 const url = try urlize(allocator, item.name);
1031 if (!toc.urls.contains(url)) {1024 if (!toc.urls.contains(url)) {
...@@ -1033,7 +1026,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1033,7 +1026,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1033 }1026 }
1034 try out.print("<li><a href=\"#{}\">{}</a></li>\n", .{ url, item.name });1027 try out.print("<li><a href=\"#{}\">{}</a></li>\n", .{ url, item.name });
1035 }1028 }
1036 try out.write("</ul>\n");1029 try out.writeAll("</ul>\n");
1037 },1030 },
1038 .Syntax => |content_tok| {1031 .Syntax => |content_tok| {
1039 try tokenizeAndPrint(tokenizer, out, content_tok);1032 try tokenizeAndPrint(tokenizer, out, content_tok);
...@@ -1047,9 +1040,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1047,9 +1040,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1047 if (!code.is_inline) {1040 if (!code.is_inline) {
1048 try out.print("<p class=\"file\">{}.zig</p>", .{code.name});1041 try out.print("<p class=\"file\">{}.zig</p>", .{code.name});
1049 }1042 }
1050 try out.write("<pre>");1043 try out.writeAll("<pre>");
1051 try tokenizeAndPrint(tokenizer, out, code.source_token);1044 try tokenizeAndPrint(tokenizer, out, code.source_token);
1052 try out.write("</pre>");1045 try out.writeAll("</pre>");
1053 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", .{code.name});1046 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", .{code.name});
1054 const tmp_source_file_name = try fs.path.join(1047 const tmp_source_file_name = try fs.path.join(
1055 allocator,1048 allocator,
doc/langref.html.in+1-1
...@@ -230,7 +230,7 @@...@@ -230,7 +230,7 @@
230const std = @import("std");230const std = @import("std");
231231
232pub fn main() !void {232pub fn main() !void {
233 const stdout = &std.io.getStdOut().outStream().stream;233 const stdout = std.io.getStdOut().outStream();
234 try stdout.print("Hello, {}!\n", .{"world"});234 try stdout.print("Hello, {}!\n", .{"world"});
235}235}
236 {#code_end#}236 {#code_end#}
lib/std/atomic/queue.zig+14-19
...@@ -104,21 +104,17 @@ pub fn Queue(comptime T: type) type {...@@ -104,21 +104,17 @@ pub fn Queue(comptime T: type) type {
104 }104 }
105105
106 pub fn dump(self: *Self) void {106 pub fn dump(self: *Self) void {
107 var stderr_file = std.io.getStdErr() catch return;107 self.dumpToStream(std.io.getStdErr().outStream()) catch return;
108 const stderr = &stderr_file.outStream().stream;
109 const Error = @typeInfo(@TypeOf(stderr)).Pointer.child.Error;
110
111 self.dumpToStream(Error, stderr) catch return;
112 }108 }
113109
114 pub fn dumpToStream(self: *Self, comptime Error: type, stream: *std.io.OutStream(Error)) Error!void {110 pub fn dumpToStream(self: *Self, stream: var) !void {
115 const S = struct {111 const S = struct {
116 fn dumpRecursive(112 fn dumpRecursive(
117 s: *std.io.OutStream(Error),113 s: var,
118 optional_node: ?*Node,114 optional_node: ?*Node,
119 indent: usize,115 indent: usize,
120 comptime depth: comptime_int,116 comptime depth: comptime_int,
121 ) Error!void {117 ) !void {
122 try s.writeByteNTimes(' ', indent);118 try s.writeByteNTimes(' ', indent);
123 if (optional_node) |node| {119 if (optional_node) |node| {
124 try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data });120 try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data });
...@@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" {...@@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" {
326322
327test "std.atomic.Queue dump" {323test "std.atomic.Queue dump" {
328 const mem = std.mem;324 const mem = std.mem;
329 const SliceOutStream = std.io.SliceOutStream;
330 var buffer: [1024]u8 = undefined;325 var buffer: [1024]u8 = undefined;
331 var expected_buffer: [1024]u8 = undefined;326 var expected_buffer: [1024]u8 = undefined;
332 var sos = SliceOutStream.init(buffer[0..]);327 var fbs = std.io.fixedBufferStream(&buffer);
333328
334 var queue = Queue(i32).init();329 var queue = Queue(i32).init();
335330
336 // Test empty stream331 // Test empty stream
337 sos.reset();332 fbs.reset();
338 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);333 try queue.dumpToStream(fbs.outStream());
339 expect(mem.eql(u8, buffer[0..sos.pos],334 expect(mem.eql(u8, buffer[0..fbs.pos],
340 \\head: (null)335 \\head: (null)
341 \\tail: (null)336 \\tail: (null)
342 \\337 \\
...@@ -350,8 +345,8 @@ test "std.atomic.Queue dump" {...@@ -350,8 +345,8 @@ test "std.atomic.Queue dump" {
350 };345 };
351 queue.put(&node_0);346 queue.put(&node_0);
352347
353 sos.reset();348 fbs.reset();
354 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);349 try queue.dumpToStream(fbs.outStream());
355350
356 var expected = try std.fmt.bufPrint(expected_buffer[0..],351 var expected = try std.fmt.bufPrint(expected_buffer[0..],
357 \\head: 0x{x}=1352 \\head: 0x{x}=1
...@@ -360,7 +355,7 @@ test "std.atomic.Queue dump" {...@@ -360,7 +355,7 @@ test "std.atomic.Queue dump" {
360 \\ (null)355 \\ (null)
361 \\356 \\
362 , .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) });357 , .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) });
363 expect(mem.eql(u8, buffer[0..sos.pos], expected));358 expect(mem.eql(u8, buffer[0..fbs.pos], expected));
364359
365 // Test a stream with two elements360 // Test a stream with two elements
366 var node_1 = Queue(i32).Node{361 var node_1 = Queue(i32).Node{
...@@ -370,8 +365,8 @@ test "std.atomic.Queue dump" {...@@ -370,8 +365,8 @@ test "std.atomic.Queue dump" {
370 };365 };
371 queue.put(&node_1);366 queue.put(&node_1);
372367
373 sos.reset();368 fbs.reset();
374 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);369 try queue.dumpToStream(fbs.outStream());
375370
376 expected = try std.fmt.bufPrint(expected_buffer[0..],371 expected = try std.fmt.bufPrint(expected_buffer[0..],
377 \\head: 0x{x}=1372 \\head: 0x{x}=1
...@@ -381,5 +376,5 @@ test "std.atomic.Queue dump" {...@@ -381,5 +376,5 @@ test "std.atomic.Queue dump" {
381 \\ (null)376 \\ (null)
382 \\377 \\
383 , .{ @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail) });378 , .{ @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail) });
384 expect(mem.eql(u8, buffer[0..sos.pos], expected));379 expect(mem.eql(u8, buffer[0..fbs.pos], expected));
385}380}
lib/std/buffer.zig+23
...@@ -157,6 +157,17 @@ pub const Buffer = struct {...@@ -157,6 +157,17 @@ pub const Buffer = struct {
157 pub fn print(self: *Buffer, comptime fmt: []const u8, args: var) !void {157 pub fn print(self: *Buffer, comptime fmt: []const u8, args: var) !void {
158 return std.fmt.format(self, error{OutOfMemory}, Buffer.append, fmt, args);158 return std.fmt.format(self, error{OutOfMemory}, Buffer.append, fmt, args);
159 }159 }
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 }
160};171};
161172
162test "simple Buffer" {173test "simple Buffer" {
...@@ -208,3 +219,15 @@ test "Buffer.print" {...@@ -208,3 +219,15 @@ test "Buffer.print" {
208 try buf.print("Hello {} the {}", .{ 2, "world" });219 try buf.print("Hello {} the {}", .{ 2, "world" });
209 testing.expect(buf.eql("Hello 2 the world"));220 testing.expect(buf.eql("Hello 2 the world"));
210}221}
222
223test "Buffer.outStream" {
224 var buffer = try Buffer.initSize(testing.allocator, 0);
225 defer buffer.deinit();
226 const buf_stream = buffer.outStream();
227
228 const x: i32 = 42;
229 const y: i32 = 1234;
230 try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
231
232 testing.expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
233}
lib/std/build.zig+1-2
...@@ -926,8 +926,7 @@ pub const Builder = struct {...@@ -926,8 +926,7 @@ pub const Builder = struct {
926926
927 try child.spawn();927 try child.spawn();
928928
929 var stdout_file_in_stream = child.stdout.?.inStream();929 const stdout = try child.stdout.?.inStream().readAllAlloc(self.allocator, max_output_size);
930 const stdout = try stdout_file_in_stream.stream.readAllAlloc(self.allocator, max_output_size);
931 errdefer self.allocator.free(stdout);930 errdefer self.allocator.free(stdout);
932931
933 const term = try child.wait();932 const term = try child.wait();
lib/std/build/emit_raw.zig+36-74
...@@ -14,11 +14,6 @@ const io = std.io;...@@ -14,11 +14,6 @@ const io = std.io;
14const sort = std.sort;14const sort = std.sort;
15const warn = std.debug.warn;15const warn = std.debug.warn;
1616
17const BinOutStream = io.OutStream(anyerror);
18const BinSeekStream = io.SeekableStream(anyerror, anyerror);
19const ElfSeekStream = io.SeekableStream(anyerror, anyerror);
20const ElfInStream = io.InStream(anyerror);
21
22const BinaryElfSection = struct {17const BinaryElfSection = struct {
23 elfOffset: u64,18 elfOffset: u64,
24 binaryOffset: u64,19 binaryOffset: u64,
...@@ -41,22 +36,19 @@ const BinaryElfOutput = struct {...@@ -41,22 +36,19 @@ const BinaryElfOutput = struct {
4136
42 const Self = @This();37 const Self = @This();
4338
44 pub fn init(allocator: *Allocator) Self {
45 return Self{
46 .segments = ArrayList(*BinaryElfSegment).init(allocator),
47 .sections = ArrayList(*BinaryElfSection).init(allocator),
48 };
49 }
50
51 pub fn deinit(self: *Self) void {39 pub fn deinit(self: *Self) void {
52 self.sections.deinit();40 self.sections.deinit();
53 self.segments.deinit();41 self.segments.deinit();
54 }42 }
5543
56 pub fn parseElf(self: *Self, elfFile: elf.Elf) !void {44 pub fn parse(allocator: *Allocator, elf_file: File) !Self {
57 const allocator = self.segments.allocator;45 var self: Self = .{
46 .segments = ArrayList(*BinaryElfSegment).init(allocator),
47 .sections = ArrayList(*BinaryElfSection).init(allocator),
48 };
49 const elf_hdrs = try std.elf.readAllHeaders(allocator, elf_file);
5850
59 for (elfFile.section_headers) |section, i| {51 for (elf_hdrs.section_headers) |section, i| {
60 if (sectionValidForOutput(section)) {52 if (sectionValidForOutput(section)) {
61 const newSection = try allocator.create(BinaryElfSection);53 const newSection = try allocator.create(BinaryElfSection);
6254
...@@ -69,19 +61,19 @@ const BinaryElfOutput = struct {...@@ -69,19 +61,19 @@ const BinaryElfOutput = struct {
69 }61 }
70 }62 }
7163
72 for (elfFile.program_headers) |programHeader, i| {64 for (elf_hdrs.program_headers) |phdr, i| {
73 if (programHeader.p_type == elf.PT_LOAD) {65 if (phdr.p_type == elf.PT_LOAD) {
74 const newSegment = try allocator.create(BinaryElfSegment);66 const newSegment = try allocator.create(BinaryElfSegment);
7567
76 newSegment.physicalAddress = if (programHeader.p_paddr != 0) programHeader.p_paddr else programHeader.p_vaddr;68 newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr;
77 newSegment.virtualAddress = programHeader.p_vaddr;69 newSegment.virtualAddress = phdr.p_vaddr;
78 newSegment.fileSize = @intCast(usize, programHeader.p_filesz);70 newSegment.fileSize = @intCast(usize, phdr.p_filesz);
79 newSegment.elfOffset = programHeader.p_offset;71 newSegment.elfOffset = phdr.p_offset;
80 newSegment.binaryOffset = 0;72 newSegment.binaryOffset = 0;
81 newSegment.firstSection = null;73 newSegment.firstSection = null;
8274
83 for (self.sections.toSlice()) |section| {75 for (self.sections.toSlice()) |section| {
84 if (sectionWithinSegment(section, programHeader)) {76 if (sectionWithinSegment(section, phdr)) {
85 if (section.segment) |sectionSegment| {77 if (section.segment) |sectionSegment| {
86 if (sectionSegment.elfOffset > newSegment.elfOffset) {78 if (sectionSegment.elfOffset > newSegment.elfOffset) {
87 section.segment = newSegment;79 section.segment = newSegment;
...@@ -126,14 +118,17 @@ const BinaryElfOutput = struct {...@@ -126,14 +118,17 @@ const BinaryElfOutput = struct {
126 }118 }
127119
128 sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare);120 sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare);
121
122 return self;
129 }123 }
130124
131 fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.ProgramHeader) bool {125 fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool {
132 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);126 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
133 }127 }
134128
135 fn sectionValidForOutput(section: elf.SectionHeader) bool {129 fn sectionValidForOutput(shdr: var) bool {
136 return section.sh_size > 0 and section.sh_type != elf.SHT_NOBITS and ((section.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);130 return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and
131 ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
137 }132 }
138133
139 fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool {134 fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool {
...@@ -151,60 +146,27 @@ const BinaryElfOutput = struct {...@@ -151,60 +146,27 @@ const BinaryElfOutput = struct {
151 }146 }
152};147};
153148
154const WriteContext = struct {149fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void {
155 inStream: *ElfInStream,150 try out_file.seekTo(section.binaryOffset);
156 inSeekStream: *ElfSeekStream,
157 outStream: *BinOutStream,
158 outSeekStream: *BinSeekStream,
159};
160
161fn writeBinaryElfSection(allocator: *Allocator, context: WriteContext, section: *BinaryElfSection) !void {
162 var readBuffer = try allocator.alloc(u8, section.fileSize);
163 defer allocator.free(readBuffer);
164
165 try context.inSeekStream.seekTo(section.elfOffset);
166 _ = try context.inStream.read(readBuffer);
167151
168 try context.outSeekStream.seekTo(section.binaryOffset);152 try out_file.writeFileAll(elf_file, .{
169 try context.outStream.write(readBuffer);153 .in_offset = section.elfOffset,
154 .in_len = section.fileSize,
155 });
170}156}
171157
172fn emit_raw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {158fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
173 var arenaAlloc = ArenaAllocator.init(allocator);159 var elf_file = try fs.cwd().openFile(elf_path, .{});
174 errdefer arenaAlloc.deinit();160 defer elf_file.close();
175 var arena_allocator = &arenaAlloc.allocator;
176
177 const currentDir = fs.cwd();
178
179 var file = try currentDir.openFile(elf_path, File.OpenFlags{});
180 defer file.close();
181
182 var fileInStream = file.inStream();
183 var fileSeekStream = file.seekableStream();
184
185 var elfFile = try elf.Elf.openStream(allocator, @ptrCast(*ElfSeekStream, &fileSeekStream.stream), @ptrCast(*ElfInStream, &fileInStream.stream));
186 defer elfFile.close();
187
188 var outFile = try currentDir.createFile(raw_path, File.CreateFlags{});
189 defer outFile.close();
190
191 var outFileOutStream = outFile.outStream();
192 var outFileSeekStream = outFile.seekableStream();
193
194 const writeContext = WriteContext{
195 .inStream = @ptrCast(*ElfInStream, &fileInStream.stream),
196 .inSeekStream = @ptrCast(*ElfSeekStream, &fileSeekStream.stream),
197 .outStream = @ptrCast(*BinOutStream, &outFileOutStream.stream),
198 .outSeekStream = @ptrCast(*BinSeekStream, &outFileSeekStream.stream),
199 };
200161
201 var binaryElfOutput = BinaryElfOutput.init(arena_allocator);162 var out_file = try fs.cwd().createFile(raw_path, .{});
202 defer binaryElfOutput.deinit();163 defer out_file.close();
203164
204 try binaryElfOutput.parseElf(elfFile);165 var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file);
166 defer binary_elf_output.deinit();
205167
206 for (binaryElfOutput.sections.toSlice()) |section| {168 for (binary_elf_output.sections.toSlice()) |section| {
207 try writeBinaryElfSection(allocator, writeContext, section);169 try writeBinaryElfSection(elf_file, out_file, section);
208 }170 }
209}171}
210172
...@@ -250,6 +212,6 @@ pub const InstallRawStep = struct {...@@ -250,6 +212,6 @@ pub const InstallRawStep = struct {
250 const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);212 const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);
251213
252 fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable;214 fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable;
253 try emit_raw(builder.allocator, full_src_path, full_dest_path);215 try emitRaw(builder.allocator, full_src_path, full_dest_path);
254 }216 }
255};217};
lib/std/build/run.zig+2-4
...@@ -175,8 +175,7 @@ pub const RunStep = struct {...@@ -175,8 +175,7 @@ pub const RunStep = struct {
175175
176 switch (self.stdout_action) {176 switch (self.stdout_action) {
177 .expect_exact, .expect_matches => {177 .expect_exact, .expect_matches => {
178 var stdout_file_in_stream = child.stdout.?.inStream();178 stdout = child.stdout.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
179 stdout = stdout_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
180 },179 },
181 .inherit, .ignore => {},180 .inherit, .ignore => {},
182 }181 }
...@@ -186,8 +185,7 @@ pub const RunStep = struct {...@@ -186,8 +185,7 @@ pub const RunStep = struct {
186185
187 switch (self.stderr_action) {186 switch (self.stderr_action) {
188 .expect_exact, .expect_matches => {187 .expect_exact, .expect_matches => {
189 var stderr_file_in_stream = child.stderr.?.inStream();188 stderr = child.stderr.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
190 stderr = stderr_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
191 },189 },
192 .inherit, .ignore => {},190 .inherit, .ignore => {},
193 }191 }
lib/std/child_process.zig+7-9
...@@ -217,13 +217,13 @@ pub const ChildProcess = struct {...@@ -217,13 +217,13 @@ pub const ChildProcess = struct {
217217
218 try child.spawn();218 try child.spawn();
219219
220 var stdout_file_in_stream = child.stdout.?.inStream();220 const stdout_in = child.stdout.?.inStream();
221 var stderr_file_in_stream = child.stderr.?.inStream();221 const stderr_in = child.stderr.?.inStream();
222222
223 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).223 // 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_in.readAllAlloc(args.allocator, args.max_output_bytes);
225 errdefer args.allocator.free(stdout);225 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_in.readAllAlloc(args.allocator, args.max_output_bytes);
227 errdefer args.allocator.free(stderr);227 errdefer args.allocator.free(stderr);
228228
229 return ExecResult{229 return ExecResult{
...@@ -780,7 +780,7 @@ fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8)...@@ -780,7 +780,7 @@ fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8)
780 var buf = try Buffer.initSize(allocator, 0);780 var buf = try Buffer.initSize(allocator, 0);
781 defer buf.deinit();781 defer buf.deinit();
782782
783 var buf_stream = &io.BufferOutStream.init(&buf).stream;783 var buf_stream = buf.outStream();
784784
785 for (argv) |arg, arg_i| {785 for (argv) |arg, arg_i| {
786 if (arg_i != 0) try buf.appendByte(' ');786 if (arg_i != 0) try buf.appendByte(' ');
...@@ -857,8 +857,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {...@@ -857,8 +857,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
857 .io_mode = .blocking,857 .io_mode = .blocking,
858 .async_block_allowed = File.async_block_allowed_yes,858 .async_block_allowed = File.async_block_allowed_yes,
859 };859 };
860 const stream = &file.outStream().stream;860 file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
861 stream.writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
862}861}
863862
864fn readIntFd(fd: i32) !ErrInt {863fn readIntFd(fd: i32) !ErrInt {
...@@ -867,8 +866,7 @@ fn readIntFd(fd: i32) !ErrInt {...@@ -867,8 +866,7 @@ fn readIntFd(fd: i32) !ErrInt {
867 .io_mode = .blocking,866 .io_mode = .blocking,
868 .async_block_allowed = File.async_block_allowed_yes,867 .async_block_allowed = File.async_block_allowed_yes,
869 };868 };
870 const stream = &file.inStream().stream;869 return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);
871 return @intCast(ErrInt, stream.readIntNative(u64) catch return error.SystemResources);
872}870}
873871
874/// Caller must free result.872/// Caller must free result.
lib/std/coff.zig+6-9
...@@ -56,8 +56,7 @@ pub const Coff = struct {...@@ -56,8 +56,7 @@ pub const Coff = struct {
56 pub fn loadHeader(self: *Coff) !void {56 pub fn loadHeader(self: *Coff) !void {
57 const pe_pointer_offset = 0x3C;57 const pe_pointer_offset = 0x3C;
5858
59 var file_stream = self.in_file.inStream();59 const in = self.in_file.inStream();
60 const in = &file_stream.stream;
6160
62 var magic: [2]u8 = undefined;61 var magic: [2]u8 = undefined;
63 try in.readNoEof(magic[0..]);62 try in.readNoEof(magic[0..]);
...@@ -89,11 +88,11 @@ pub const Coff = struct {...@@ -89,11 +88,11 @@ pub const Coff = struct {
89 else => return error.InvalidMachine,88 else => return error.InvalidMachine,
90 }89 }
9190
92 try self.loadOptionalHeader(&file_stream);91 try self.loadOptionalHeader();
93 }92 }
9493
95 fn loadOptionalHeader(self: *Coff, file_stream: *File.InStream) !void {94 fn loadOptionalHeader(self: *Coff) !void {
96 const in = &file_stream.stream;95 const in = self.in_file.inStream();
97 self.pe_header.magic = try in.readIntLittle(u16);96 self.pe_header.magic = try in.readIntLittle(u16);
98 // For now we're only interested in finding the reference to the .pdb,97 // For now we're only interested in finding the reference to the .pdb,
99 // so we'll skip most of this header, which size is different in 3298 // so we'll skip most of this header, which size is different in 32
...@@ -136,8 +135,7 @@ pub const Coff = struct {...@@ -136,8 +135,7 @@ pub const Coff = struct {
136 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];135 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
137 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;136 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
138137
139 var file_stream = self.in_file.inStream();138 const in = self.in_file.inStream();
140 const in = &file_stream.stream;
141 try self.in_file.seekTo(file_offset);139 try self.in_file.seekTo(file_offset);
142140
143 // Find the correct DebugDirectoryEntry, and where its data is stored.141 // Find the correct DebugDirectoryEntry, and where its data is stored.
...@@ -188,8 +186,7 @@ pub const Coff = struct {...@@ -188,8 +186,7 @@ pub const Coff = struct {
188186
189 try self.sections.ensureCapacity(self.coff_header.number_of_sections);187 try self.sections.ensureCapacity(self.coff_header.number_of_sections);
190188
191 var file_stream = self.in_file.inStream();189 const in = self.in_file.inStream();
192 const in = &file_stream.stream;
193190
194 var name: [8]u8 = undefined;191 var name: [8]u8 = undefined;
195192
lib/std/debug.zig+156-124
...@@ -55,7 +55,7 @@ pub const LineInfo = struct {...@@ -55,7 +55,7 @@ pub const LineInfo = struct {
55var stderr_file: File = undefined;55var stderr_file: File = undefined;
56var stderr_file_out_stream: File.OutStream = undefined;56var stderr_file_out_stream: File.OutStream = undefined;
5757
58var stderr_stream: ?*io.OutStream(File.WriteError) = null;58var stderr_stream: ?*File.OutStream = null;
59var stderr_mutex = std.Mutex.init();59var stderr_mutex = std.Mutex.init();
6060
61pub fn warn(comptime fmt: []const u8, args: var) void {61pub fn warn(comptime fmt: []const u8, args: var) void {
...@@ -65,13 +65,13 @@ pub fn warn(comptime fmt: []const u8, args: var) void {...@@ -65,13 +65,13 @@ pub fn warn(comptime fmt: []const u8, args: var) void {
65 noasync stderr.print(fmt, args) catch return;65 noasync stderr.print(fmt, args) catch return;
66}66}
6767
68pub fn getStderrStream() *io.OutStream(File.WriteError) {68pub fn getStderrStream() *File.OutStream {
69 if (stderr_stream) |st| {69 if (stderr_stream) |st| {
70 return st;70 return st;
71 } else {71 } else {
72 stderr_file = io.getStdErr();72 stderr_file = io.getStdErr();
73 stderr_file_out_stream = stderr_file.outStream();73 stderr_file_out_stream = stderr_file.outStream();
74 const st = &stderr_file_out_stream.stream;74 const st = &stderr_file_out_stream;
75 stderr_stream = st;75 stderr_stream = st;
76 return st;76 return st;
77 }77 }
...@@ -408,15 +408,15 @@ pub const TTY = struct {...@@ -408,15 +408,15 @@ pub const TTY = struct {
408 windows_api,408 windows_api,
409409
410 fn setColor(conf: Config, out_stream: var, color: Color) void {410 fn setColor(conf: Config, out_stream: var, color: Color) void {
411 switch (conf) {411 noasync switch (conf) {
412 .no_color => return,412 .no_color => return,
413 .escape_codes => switch (color) {413 .escape_codes => switch (color) {
414 .Red => noasync out_stream.write(RED) catch return,414 .Red => out_stream.writeAll(RED) catch return,
415 .Green => noasync out_stream.write(GREEN) catch return,415 .Green => out_stream.writeAll(GREEN) catch return,
416 .Cyan => noasync out_stream.write(CYAN) catch return,416 .Cyan => out_stream.writeAll(CYAN) catch return,
417 .White, .Bold => noasync out_stream.write(WHITE) catch return,417 .White, .Bold => out_stream.writeAll(WHITE) catch return,
418 .Dim => noasync out_stream.write(DIM) catch return,418 .Dim => out_stream.writeAll(DIM) catch return,
419 .Reset => noasync out_stream.write(RESET) catch return,419 .Reset => out_stream.writeAll(RESET) catch return,
420 },420 },
421 .windows_api => if (builtin.os.tag == .windows) {421 .windows_api => if (builtin.os.tag == .windows) {
422 const S = struct {422 const S = struct {
...@@ -455,7 +455,7 @@ pub const TTY = struct {...@@ -455,7 +455,7 @@ pub const TTY = struct {
455 } else {455 } else {
456 unreachable;456 unreachable;
457 },457 },
458 }458 };
459 }459 }
460 };460 };
461};461};
...@@ -475,15 +475,15 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {...@@ -475,15 +475,15 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
475475
476 const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;476 const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
477477
478 const signature = try modi.stream.readIntLittle(u32);478 const signature = try modi.inStream().readIntLittle(u32);
479 if (signature != 4)479 if (signature != 4)
480 return error.InvalidDebugInfo;480 return error.InvalidDebugInfo;
481481
482 mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);482 mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
483 try modi.stream.readNoEof(mod.symbols);483 try modi.inStream().readNoEof(mod.symbols);
484484
485 mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);485 mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
486 try modi.stream.readNoEof(mod.subsect_info);486 try modi.inStream().readNoEof(mod.subsect_info);
487487
488 var sect_offset: usize = 0;488 var sect_offset: usize = 0;
489 var skip_len: usize = undefined;489 var skip_len: usize = undefined;
...@@ -565,38 +565,40 @@ fn printLineInfo(...@@ -565,38 +565,40 @@ fn printLineInfo(
565 tty_config: TTY.Config,565 tty_config: TTY.Config,
566 comptime printLineFromFile: var,566 comptime printLineFromFile: var,
567) !void {567) !void {
568 tty_config.setColor(out_stream, .White);568 noasync {
569 tty_config.setColor(out_stream, .White);
569570
570 if (line_info) |*li| {571 if (line_info) |*li| {
571 try noasync out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });572 try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
572 } else {573 } else {
573 try noasync out_stream.write("???:?:?");574 try out_stream.writeAll("???:?:?");
574 }575 }
575576
576 tty_config.setColor(out_stream, .Reset);577 tty_config.setColor(out_stream, .Reset);
577 try noasync out_stream.write(": ");578 try out_stream.writeAll(": ");
578 tty_config.setColor(out_stream, .Dim);579 tty_config.setColor(out_stream, .Dim);
579 try noasync out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });580 try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
580 tty_config.setColor(out_stream, .Reset);581 tty_config.setColor(out_stream, .Reset);
581 try noasync out_stream.write("\n");582 try out_stream.writeAll("\n");
582583
583 // Show the matching source code line if possible584 // Show the matching source code line if possible
584 if (line_info) |li| {585 if (line_info) |li| {
585 if (noasync printLineFromFile(out_stream, li)) {586 if (printLineFromFile(out_stream, li)) {
586 if (li.column > 0) {587 if (li.column > 0) {
587 // The caret already takes one char588 // The caret already takes one char
588 const space_needed = @intCast(usize, li.column - 1);589 const space_needed = @intCast(usize, li.column - 1);
589590
590 try noasync out_stream.writeByteNTimes(' ', space_needed);591 try out_stream.writeByteNTimes(' ', space_needed);
591 tty_config.setColor(out_stream, .Green);592 tty_config.setColor(out_stream, .Green);
592 try noasync out_stream.write("^");593 try out_stream.writeAll("^");
593 tty_config.setColor(out_stream, .Reset);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,
594 }601 }
595 try noasync out_stream.write("\n");
596 } else |err| switch (err) {
597 error.EndOfFile, error.FileNotFound => {},
598 error.BadPathName => {},
599 else => return err,
600 }602 }
601 }603 }
602}604}
...@@ -609,21 +611,21 @@ pub const OpenSelfDebugInfoError = error{...@@ -609,21 +611,21 @@ pub const OpenSelfDebugInfoError = error{
609};611};
610612
611/// TODO resources https://github.com/ziglang/zig/issues/4353613/// 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.
614pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {614pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
615 if (builtin.strip_debug_info)615 noasync {
616 return error.MissingDebugInfo;616 if (builtin.strip_debug_info)
617 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {617 return error.MissingDebugInfo;
618 return noasync root.os.debug.openSelfDebugInfo(allocator);618 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
619 }619 return root.os.debug.openSelfDebugInfo(allocator);
620 switch (builtin.os.tag) {620 }
621 .linux,621 switch (builtin.os.tag) {
622 .freebsd,622 .linux,
623 .macosx,623 .freebsd,
624 .windows,624 .macosx,
625 => return DebugInfo.init(allocator),625 .windows,
626 else => @compileError("openSelfDebugInfo unsupported for this platform"),626 => return DebugInfo.init(allocator),
627 else => @compileError("openSelfDebugInfo unsupported for this platform"),
628 }
627 }629 }
628}630}
629631
...@@ -654,11 +656,11 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -654,11 +656,11 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
654 try di.pdb.openFile(di.coff, path);656 try di.pdb.openFile(di.coff, path);
655657
656 var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;658 var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
657 const version = try pdb_stream.stream.readIntLittle(u32);659 const version = try pdb_stream.inStream().readIntLittle(u32);
658 const signature = try pdb_stream.stream.readIntLittle(u32);660 const signature = try pdb_stream.inStream().readIntLittle(u32);
659 const age = try pdb_stream.stream.readIntLittle(u32);661 const age = try pdb_stream.inStream().readIntLittle(u32);
660 var guid: [16]u8 = undefined;662 var guid: [16]u8 = undefined;
661 try pdb_stream.stream.readNoEof(&guid);663 try pdb_stream.inStream().readNoEof(&guid);
662 if (version != 20000404) // VC70, only value observed by LLVM team664 if (version != 20000404) // VC70, only value observed by LLVM team
663 return error.UnknownPDBVersion;665 return error.UnknownPDBVersion;
664 if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age)666 if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age)
...@@ -666,9 +668,9 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -666,9 +668,9 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
666 // We validated the executable and pdb match.668 // We validated the executable and pdb match.
667669
668 const string_table_index = str_tab_index: {670 const string_table_index = str_tab_index: {
669 const name_bytes_len = try pdb_stream.stream.readIntLittle(u32);671 const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32);
670 const name_bytes = try allocator.alloc(u8, name_bytes_len);672 const name_bytes = try allocator.alloc(u8, name_bytes_len);
671 try pdb_stream.stream.readNoEof(name_bytes);673 try pdb_stream.inStream().readNoEof(name_bytes);
672674
673 const HashTableHeader = packed struct {675 const HashTableHeader = packed struct {
674 Size: u32,676 Size: u32,
...@@ -678,17 +680,17 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -678,17 +680,17 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
678 return cap * 2 / 3 + 1;680 return cap * 2 / 3 + 1;
679 }681 }
680 };682 };
681 const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader);683 const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader);
682 if (hash_tbl_hdr.Capacity == 0)684 if (hash_tbl_hdr.Capacity == 0)
683 return error.InvalidDebugInfo;685 return error.InvalidDebugInfo;
684686
685 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))687 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
686 return error.InvalidDebugInfo;688 return error.InvalidDebugInfo;
687689
688 const present = try readSparseBitVector(&pdb_stream.stream, allocator);690 const present = try readSparseBitVector(&pdb_stream.inStream(), allocator);
689 if (present.len != hash_tbl_hdr.Size)691 if (present.len != hash_tbl_hdr.Size)
690 return error.InvalidDebugInfo;692 return error.InvalidDebugInfo;
691 const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);693 const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator);
692694
693 const Bucket = struct {695 const Bucket = struct {
694 first: u32,696 first: u32,
...@@ -696,8 +698,8 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -696,8 +698,8 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
696 };698 };
697 const bucket_list = try allocator.alloc(Bucket, present.len);699 const bucket_list = try allocator.alloc(Bucket, present.len);
698 for (present) |_| {700 for (present) |_| {
699 const name_offset = try pdb_stream.stream.readIntLittle(u32);701 const name_offset = try pdb_stream.inStream().readIntLittle(u32);
700 const name_index = try pdb_stream.stream.readIntLittle(u32);702 const name_index = try pdb_stream.inStream().readIntLittle(u32);
701 const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset));703 const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset));
702 if (mem.eql(u8, name, "/names")) {704 if (mem.eql(u8, name, "/names")) {
703 break :str_tab_index name_index;705 break :str_tab_index name_index;
...@@ -712,7 +714,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -712,7 +714,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
712 const dbi = di.pdb.dbi;714 const dbi = di.pdb.dbi;
713715
714 // Dbi Header716 // Dbi Header
715 const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader);717 const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader);
716 if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team718 if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team
717 return error.UnknownPDBVersion;719 return error.UnknownPDBVersion;
718 if (dbi_stream_header.Age != age)720 if (dbi_stream_header.Age != age)
...@@ -726,7 +728,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -726,7 +728,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
726 // Module Info Substream728 // Module Info Substream
727 var mod_info_offset: usize = 0;729 var mod_info_offset: usize = 0;
728 while (mod_info_offset != mod_info_size) {730 while (mod_info_offset != mod_info_size) {
729 const mod_info = try dbi.stream.readStruct(pdb.ModInfo);731 const mod_info = try dbi.inStream().readStruct(pdb.ModInfo);
730 var this_record_len: usize = @sizeOf(pdb.ModInfo);732 var this_record_len: usize = @sizeOf(pdb.ModInfo);
731733
732 const module_name = try dbi.readNullTermString(allocator);734 const module_name = try dbi.readNullTermString(allocator);
...@@ -764,14 +766,14 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !...@@ -764,14 +766,14 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
764 var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);766 var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
765 var sect_cont_offset: usize = 0;767 var sect_cont_offset: usize = 0;
766 if (section_contrib_size != 0) {768 if (section_contrib_size != 0) {
767 const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32));769 const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32));
768 if (ver != pdb.SectionContrSubstreamVersion.Ver60)770 if (ver != pdb.SectionContrSubstreamVersion.Ver60)
769 return error.InvalidDebugInfo;771 return error.InvalidDebugInfo;
770 sect_cont_offset += @sizeOf(u32);772 sect_cont_offset += @sizeOf(u32);
771 }773 }
772 while (sect_cont_offset != section_contrib_size) {774 while (sect_cont_offset != section_contrib_size) {
773 const entry = try sect_contribs.addOne();775 const entry = try sect_contribs.addOne();
774 entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry);776 entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry);
775 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);777 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
776778
777 if (sect_cont_offset > section_contrib_size)779 if (sect_cont_offset > section_contrib_size)
...@@ -808,45 +810,71 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {...@@ -808,45 +810,71 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
808810
809/// TODO resources https://github.com/ziglang/zig/issues/4353811/// TODO resources https://github.com/ziglang/zig/issues/4353
810pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {812pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
811 const mapped_mem = try mapWholeFile(elf_file_path);813 noasync {
812814 const mapped_mem = try mapWholeFile(elf_file_path);
813 var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);815 const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
814 var efile = try noasync elf.Elf.openStream(816 if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
815 allocator,817 if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
816 @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),818
817 @ptrCast(*DW.DwarfInStream, &seekable_stream.stream),819 const endian: builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
818 );820 elf.ELFDATA2LSB => .Little,
819 defer noasync efile.close();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 str_shdr = @ptrCast(
829 *const elf.Shdr,
830 @alignCast(@alignOf(elf.Shdr), &mapped_mem[try math.cast(usize, str_section_off)]),
831 );
832 const header_strings = mapped_mem[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size];
833 const shdrs = @ptrCast(
834 [*]const elf.Shdr,
835 @alignCast(@alignOf(elf.Shdr), &mapped_mem[shoff]),
836 )[0..hdr.e_shnum];
837
838 var opt_debug_info: ?[]const u8 = null;
839 var opt_debug_abbrev: ?[]const u8 = null;
840 var opt_debug_str: ?[]const u8 = null;
841 var opt_debug_line: ?[]const u8 = null;
842 var opt_debug_ranges: ?[]const u8 = null;
843
844 for (shdrs) |*shdr| {
845 if (shdr.sh_type == elf.SHT_NULL) continue;
846
847 const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr));
848 if (mem.eql(u8, name, ".debug_info")) {
849 opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
850 } else if (mem.eql(u8, name, ".debug_abbrev")) {
851 opt_debug_abbrev = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
852 } else if (mem.eql(u8, name, ".debug_str")) {
853 opt_debug_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
854 } else if (mem.eql(u8, name, ".debug_line")) {
855 opt_debug_line = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
856 } else if (mem.eql(u8, name, ".debug_ranges")) {
857 opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
858 }
859 }
820860
821 const debug_info = (try noasync efile.findSection(".debug_info")) orelse861 var di = DW.DwarfInfo{
822 return error.MissingDebugInfo;862 .endian = endian,
823 const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse863 .debug_info = opt_debug_info orelse return error.MissingDebugInfo,
824 return error.MissingDebugInfo;864 .debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
825 const debug_str = (try noasync efile.findSection(".debug_str")) orelse865 .debug_str = opt_debug_str orelse return error.MissingDebugInfo,
826 return error.MissingDebugInfo;866 .debug_line = opt_debug_line orelse return error.MissingDebugInfo,
827 const debug_line = (try noasync efile.findSection(".debug_line")) orelse867 .debug_ranges = opt_debug_ranges,
828 return error.MissingDebugInfo;868 };
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 };
842869
843 try noasync DW.openDwarfDebugInfo(&di, allocator);870 try DW.openDwarfDebugInfo(&di, allocator);
844871
845 return ModuleDebugInfo{872 return ModuleDebugInfo{
846 .base_address = undefined,873 .base_address = undefined,
847 .dwarf = di,874 .dwarf = di,
848 .mapped_memory = mapped_mem,875 .mapped_memory = mapped_mem,
849 };876 };
877 }
850}878}
851879
852/// TODO resources https://github.com/ziglang/zig/issues/4353880/// TODO resources https://github.com/ziglang/zig/issues/4353
...@@ -936,7 +964,9 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M...@@ -936,7 +964,9 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M
936}964}
937965
938fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {966fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
939 var f = try fs.cwd().openFile(line_info.file_name, .{});967 // Need this to always block even in async I/O mode, because this could potentially
968 // be called from e.g. the event loop code crashing.
969 var f = try fs.cwd().openFile(line_info.file_name, .{ .always_blocking = true });
940 defer f.close();970 defer f.close();
941 // TODO fstat and make sure that the file has the correct size971 // TODO fstat and make sure that the file has the correct size
942972
...@@ -982,22 +1012,24 @@ const MachoSymbol = struct {...@@ -982,22 +1012,24 @@ const MachoSymbol = struct {
982 }1012 }
983};1013};
9841014
985fn mapWholeFile(path: []const u8) ![]const u8 {1015fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
986 const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true });1016 noasync {
987 defer noasync file.close();1017 const file = try fs.openFileAbsolute(path, .{ .always_blocking = true });
9881018 defer file.close();
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);
9991019
1000 return mapped_mem;1020 const file_len = try math.cast(usize, try file.getEndPos());
1021 const mapped_mem = try os.mmap(
1022 null,
1023 file_len,
1024 os.PROT_READ,
1025 os.MAP_SHARED,
1026 file.handle,
1027 0,
1028 );
1029 errdefer os.munmap(mapped_mem);
1030
1031 return mapped_mem;
1032 }
1001}1033}
10021034
1003pub const DebugInfo = struct {1035pub const DebugInfo = struct {
lib/std/debug/leb128.zig+12-12
...@@ -121,18 +121,18 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {...@@ -121,18 +121,18 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
121}121}
122122
123fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {123fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
124 var in_stream = std.io.SliceInStream.init(encoded);124 var in_stream = std.io.fixedBufferStream(encoded);
125 return try readILEB128(T, &in_stream.stream);125 return try readILEB128(T, in_stream.inStream());
126}126}
127127
128fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {128fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
129 var in_stream = std.io.SliceInStream.init(encoded);129 var in_stream = std.io.fixedBufferStream(encoded);
130 return try readULEB128(T, &in_stream.stream);130 return try readULEB128(T, in_stream.inStream());
131}131}
132132
133fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {133fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
134 var in_stream = std.io.SliceInStream.init(encoded);134 var in_stream = std.io.fixedBufferStream(encoded);
135 const v1 = readILEB128(T, &in_stream.stream);135 const v1 = readILEB128(T, in_stream.inStream());
136 var in_ptr = encoded.ptr;136 var in_ptr = encoded.ptr;
137 const v2 = readILEB128Mem(T, &in_ptr);137 const v2 = readILEB128Mem(T, &in_ptr);
138 testing.expectEqual(v1, v2);138 testing.expectEqual(v1, v2);
...@@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {...@@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
140}140}
141141
142fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {142fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
143 var in_stream = std.io.SliceInStream.init(encoded);143 var in_stream = std.io.fixedBufferStream(encoded);
144 const v1 = readULEB128(T, &in_stream.stream);144 const v1 = readULEB128(T, in_stream.inStream());
145 var in_ptr = encoded.ptr;145 var in_ptr = encoded.ptr;
146 const v2 = readULEB128Mem(T, &in_ptr);146 const v2 = readULEB128Mem(T, &in_ptr);
147 testing.expectEqual(v1, v2);147 testing.expectEqual(v1, v2);
...@@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {...@@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
149}149}
150150
151fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {151fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {
152 var in_stream = std.io.SliceInStream.init(encoded);152 var in_stream = std.io.fixedBufferStream(encoded);
153 var in_ptr = encoded.ptr;153 var in_ptr = encoded.ptr;
154 var i: usize = 0;154 var i: usize = 0;
155 while (i < N) : (i += 1) {155 while (i < N) : (i += 1) {
156 const v1 = readILEB128(T, &in_stream.stream);156 const v1 = readILEB128(T, in_stream.inStream());
157 const v2 = readILEB128Mem(T, &in_ptr);157 const v2 = readILEB128Mem(T, &in_ptr);
158 testing.expectEqual(v1, v2);158 testing.expectEqual(v1, v2);
159 }159 }
160}160}
161161
162fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {162fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {
163 var in_stream = std.io.SliceInStream.init(encoded);163 var in_stream = std.io.fixedBufferStream(encoded);
164 var in_ptr = encoded.ptr;164 var in_ptr = encoded.ptr;
165 var i: usize = 0;165 var i: usize = 0;
166 while (i < N) : (i += 1) {166 while (i < N) : (i += 1) {
167 const v1 = readULEB128(T, &in_stream.stream);167 const v1 = readULEB128(T, in_stream.inStream());
168 const v2 = readULEB128Mem(T, &in_ptr);168 const v2 = readULEB128Mem(T, &in_ptr);
169 testing.expectEqual(v1, v2);169 testing.expectEqual(v1, v2);
170 }170 }
lib/std/dwarf.zig+84-77
...@@ -11,9 +11,6 @@ const ArrayList = std.ArrayList;...@@ -11,9 +11,6 @@ const ArrayList = std.ArrayList;
1111
12usingnamespace @import("dwarf_bits.zig");12usingnamespace @import("dwarf_bits.zig");
1313
14pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
15pub const DwarfInStream = io.InStream(anyerror);
16
17const PcRange = struct {14const PcRange = struct {
18 start: u64,15 start: u64,
19 end: u64,16 end: u64,
...@@ -239,7 +236,7 @@ const LineNumberProgram = struct {...@@ -239,7 +236,7 @@ const LineNumberProgram = struct {
239 }236 }
240};237};
241238
242fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {239fn readInitialLength(in_stream: var, is_64: *bool) !u64 {
243 const first_32_bits = try in_stream.readIntLittle(u32);240 const first_32_bits = try in_stream.readIntLittle(u32);
244 is_64.* = (first_32_bits == 0xffffffff);241 is_64.* = (first_32_bits == 0xffffffff);
245 if (is_64.*) {242 if (is_64.*) {
...@@ -414,40 +411,42 @@ pub const DwarfInfo = struct {...@@ -414,40 +411,42 @@ pub const DwarfInfo = struct {
414 }411 }
415412
416 fn scanAllFunctions(di: *DwarfInfo) !void {413 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();
418 var this_unit_offset: u64 = 0;417 var this_unit_offset: u64 = 0;
419418
420 while (this_unit_offset < try s.seekable_stream.getEndPos()) {419 while (this_unit_offset < try seekable.getEndPos()) {
421 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {420 seekable.seekTo(this_unit_offset) catch |err| switch (err) {
422 error.EndOfStream => unreachable,421 error.EndOfStream => unreachable,
423 else => return err,422 else => return err,
424 };423 };
425424
426 var is_64: bool = undefined;425 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);
428 if (unit_length == 0) return;427 if (unit_length == 0) return;
429 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));428 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);
432 if (version < 2 or version > 5) return error.InvalidDebugInfo;431 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();
437 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;436 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();
440 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);439 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
444 const next_unit_pos = this_unit_offset + next_offset;443 const next_unit_pos = this_unit_offset + next_offset;
445444
446 while ((try s.seekable_stream.getPos()) < next_unit_pos) {445 while ((try seekable.getPos()) < next_unit_pos) {
447 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;446 const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue;
448 defer die_obj.attrs.deinit();447 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
452 switch (die_obj.tag_id) {451 switch (die_obj.tag_id) {
453 TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {452 TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
...@@ -463,14 +462,14 @@ pub const DwarfInfo = struct {...@@ -463,14 +462,14 @@ pub const DwarfInfo = struct {
463 // Follow the DIE it points to and repeat462 // Follow the DIE it points to and repeat
464 const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);463 const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
465 if (ref_offset > next_offset) return error.InvalidDebugInfo;464 if (ref_offset > next_offset) return error.InvalidDebugInfo;
466 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);465 try seekable.seekTo(this_unit_offset + ref_offset);
467 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;466 this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
468 } else if (this_die_obj.getAttr(AT_specification)) |ref| {467 } else if (this_die_obj.getAttr(AT_specification)) |ref| {
469 // Follow the DIE it points to and repeat468 // Follow the DIE it points to and repeat
470 const ref_offset = try this_die_obj.getAttrRef(AT_specification);469 const ref_offset = try this_die_obj.getAttrRef(AT_specification);
471 if (ref_offset > next_offset) return error.InvalidDebugInfo;470 if (ref_offset > next_offset) return error.InvalidDebugInfo;
472 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);471 try seekable.seekTo(this_unit_offset + ref_offset);
473 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;472 this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
474 } else {473 } else {
475 break :x null;474 break :x null;
476 }475 }
...@@ -511,7 +510,7 @@ pub const DwarfInfo = struct {...@@ -511,7 +510,7 @@ pub const DwarfInfo = struct {
511 else => {},510 else => {},
512 }511 }
513512
514 try s.seekable_stream.seekTo(after_die_offset);513 try seekable.seekTo(after_die_offset);
515 }514 }
516515
517 this_unit_offset += next_offset;516 this_unit_offset += next_offset;
...@@ -519,35 +518,37 @@ pub const DwarfInfo = struct {...@@ -519,35 +518,37 @@ pub const DwarfInfo = struct {
519 }518 }
520519
521 fn scanAllCompileUnits(di: *DwarfInfo) !void {520 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();
523 var this_unit_offset: u64 = 0;524 var this_unit_offset: u64 = 0;
524525
525 while (this_unit_offset < try s.seekable_stream.getEndPos()) {526 while (this_unit_offset < try seekable.getEndPos()) {
526 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {527 seekable.seekTo(this_unit_offset) catch |err| switch (err) {
527 error.EndOfStream => unreachable,528 error.EndOfStream => unreachable,
528 else => return err,529 else => return err,
529 };530 };
530531
531 var is_64: bool = undefined;532 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);
533 if (unit_length == 0) return;534 if (unit_length == 0) return;
534 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));535 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);
537 if (version < 2 or version > 5) return error.InvalidDebugInfo;538 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();
542 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;543 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();
545 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);546 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
549 const compile_unit_die = try di.allocator().create(Die);550 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
552 if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;553 if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
553554
...@@ -593,7 +594,9 @@ pub const DwarfInfo = struct {...@@ -593,7 +594,9 @@ pub const DwarfInfo = struct {
593 }594 }
594 if (di.debug_ranges) |debug_ranges| {595 if (di.debug_ranges) |debug_ranges| {
595 if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {596 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
598 // All the addresses in the list are relative to the value601 // All the addresses in the list are relative to the value
599 // specified by DW_AT_low_pc or to some other value encoded602 // specified by DW_AT_low_pc or to some other value encoded
...@@ -604,11 +607,11 @@ pub const DwarfInfo = struct {...@@ -604,11 +607,11 @@ pub const DwarfInfo = struct {
604 else => return err,607 else => return err,
605 };608 };
606609
607 try s.seekable_stream.seekTo(ranges_offset);610 try seekable.seekTo(ranges_offset);
608611
609 while (true) {612 while (true) {
610 const begin_addr = try s.stream.readIntLittle(usize);613 const begin_addr = try in.readIntLittle(usize);
611 const end_addr = try s.stream.readIntLittle(usize);614 const end_addr = try in.readIntLittle(usize);
612 if (begin_addr == 0 and end_addr == 0) {615 if (begin_addr == 0 and end_addr == 0) {
613 break;616 break;
614 }617 }
...@@ -646,25 +649,27 @@ pub const DwarfInfo = struct {...@@ -646,25 +649,27 @@ pub const DwarfInfo = struct {
646 }649 }
647650
648 fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {651 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);
652 var result = AbbrevTable.init(di.allocator());657 var result = AbbrevTable.init(di.allocator());
653 errdefer result.deinit();658 errdefer result.deinit();
654 while (true) {659 while (true) {
655 const abbrev_code = try leb.readULEB128(u64, &s.stream);660 const abbrev_code = try leb.readULEB128(u64, in);
656 if (abbrev_code == 0) return result;661 if (abbrev_code == 0) return result;
657 try result.append(AbbrevTableEntry{662 try result.append(AbbrevTableEntry{
658 .abbrev_code = abbrev_code,663 .abbrev_code = abbrev_code,
659 .tag_id = try leb.readULEB128(u64, &s.stream),664 .tag_id = try leb.readULEB128(u64, in),
660 .has_children = (try s.stream.readByte()) == CHILDREN_yes,665 .has_children = (try in.readByte()) == CHILDREN_yes,
661 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),666 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
662 });667 });
663 const attrs = &result.items[result.len - 1].attrs;668 const attrs = &result.items[result.len - 1].attrs;
664669
665 while (true) {670 while (true) {
666 const attr_id = try leb.readULEB128(u64, &s.stream);671 const attr_id = try leb.readULEB128(u64, in);
667 const form_id = try leb.readULEB128(u64, &s.stream);672 const form_id = try leb.readULEB128(u64, in);
668 if (attr_id == 0 and form_id == 0) break;673 if (attr_id == 0 and form_id == 0) break;
669 try attrs.append(AbbrevAttr{674 try attrs.append(AbbrevAttr{
670 .attr_id = attr_id,675 .attr_id = attr_id,
...@@ -695,42 +700,44 @@ pub const DwarfInfo = struct {...@@ -695,42 +700,44 @@ pub const DwarfInfo = struct {
695 }700 }
696701
697 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {702 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
700 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);707 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
701 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list);708 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
705 var is_64: bool = undefined;712 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);
707 if (unit_length == 0) {714 if (unit_length == 0) {
708 return error.MissingDebugInfo;715 return error.MissingDebugInfo;
709 }716 }
710 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));717 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);
713 // TODO support 3 and 5720 // TODO support 3 and 5
714 if (version != 2 and version != 4) return error.InvalidDebugInfo;721 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);723 const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
717 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;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();
720 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;727 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
721728
722 if (version >= 4) {729 if (version >= 4) {
723 // maximum_operations_per_instruction730 // maximum_operations_per_instruction
724 _ = try s.stream.readByte();731 _ = try in.readByte();
725 }732 }
726733
727 const default_is_stmt = (try s.stream.readByte()) != 0;734 const default_is_stmt = (try in.readByte()) != 0;
728 const line_base = try s.stream.readByteSigned();735 const line_base = try in.readByteSigned();
729736
730 const line_range = try s.stream.readByte();737 const line_range = try in.readByte();
731 if (line_range == 0) return error.InvalidDebugInfo;738 if (line_range == 0) return error.InvalidDebugInfo;
732739
733 const opcode_base = try s.stream.readByte();740 const opcode_base = try in.readByte();
734741
735 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);742 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
736 defer di.allocator().free(standard_opcode_lengths);743 defer di.allocator().free(standard_opcode_lengths);
...@@ -738,14 +745,14 @@ pub const DwarfInfo = struct {...@@ -738,14 +745,14 @@ pub const DwarfInfo = struct {
738 {745 {
739 var i: usize = 0;746 var i: usize = 0;
740 while (i < opcode_base - 1) : (i += 1) {747 while (i < opcode_base - 1) : (i += 1) {
741 standard_opcode_lengths[i] = try s.stream.readByte();748 standard_opcode_lengths[i] = try in.readByte();
742 }749 }
743 }750 }
744751
745 var include_directories = ArrayList([]const u8).init(di.allocator());752 var include_directories = ArrayList([]const u8).init(di.allocator());
746 try include_directories.append(compile_unit_cwd);753 try include_directories.append(compile_unit_cwd);
747 while (true) {754 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));
749 if (dir.len == 0) break;756 if (dir.len == 0) break;
750 try include_directories.append(dir);757 try include_directories.append(dir);
751 }758 }
...@@ -754,11 +761,11 @@ pub const DwarfInfo = struct {...@@ -754,11 +761,11 @@ pub const DwarfInfo = struct {
754 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);761 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
755762
756 while (true) {763 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));
758 if (file_name.len == 0) break;765 if (file_name.len == 0) break;
759 const dir_index = try leb.readULEB128(usize, &s.stream);766 const dir_index = try leb.readULEB128(usize, in);
760 const mtime = try leb.readULEB128(usize, &s.stream);767 const mtime = try leb.readULEB128(usize, in);
761 const len_bytes = try leb.readULEB128(usize, &s.stream);768 const len_bytes = try leb.readULEB128(usize, in);
762 try file_entries.append(FileEntry{769 try file_entries.append(FileEntry{
763 .file_name = file_name,770 .file_name = file_name,
764 .dir_index = dir_index,771 .dir_index = dir_index,
...@@ -767,17 +774,17 @@ pub const DwarfInfo = struct {...@@ -767,17 +774,17 @@ pub const DwarfInfo = struct {
767 });774 });
768 }775 }
769776
770 try s.seekable_stream.seekTo(prog_start_offset);777 try seekable.seekTo(prog_start_offset);
771778
772 const next_unit_pos = line_info_offset + next_offset;779 const next_unit_pos = line_info_offset + next_offset;
773780
774 while ((try s.seekable_stream.getPos()) < next_unit_pos) {781 while ((try seekable.getPos()) < next_unit_pos) {
775 const opcode = try s.stream.readByte();782 const opcode = try in.readByte();
776783
777 if (opcode == LNS_extended_op) {784 if (opcode == LNS_extended_op) {
778 const op_size = try leb.readULEB128(u64, &s.stream);785 const op_size = try leb.readULEB128(u64, in);
779 if (op_size < 1) return error.InvalidDebugInfo;786 if (op_size < 1) return error.InvalidDebugInfo;
780 var sub_op = try s.stream.readByte();787 var sub_op = try in.readByte();
781 switch (sub_op) {788 switch (sub_op) {
782 LNE_end_sequence => {789 LNE_end_sequence => {
783 prog.end_sequence = true;790 prog.end_sequence = true;
...@@ -785,14 +792,14 @@ pub const DwarfInfo = struct {...@@ -785,14 +792,14 @@ pub const DwarfInfo = struct {
785 prog.reset();792 prog.reset();
786 },793 },
787 LNE_set_address => {794 LNE_set_address => {
788 const addr = try s.stream.readInt(usize, di.endian);795 const addr = try in.readInt(usize, di.endian);
789 prog.address = addr;796 prog.address = addr;
790 },797 },
791 LNE_define_file => {798 LNE_define_file => {
792 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));799 const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
793 const dir_index = try leb.readULEB128(usize, &s.stream);800 const dir_index = try leb.readULEB128(usize, in);
794 const mtime = try leb.readULEB128(usize, &s.stream);801 const mtime = try leb.readULEB128(usize, in);
795 const len_bytes = try leb.readULEB128(usize, &s.stream);802 const len_bytes = try leb.readULEB128(usize, in);
796 try file_entries.append(FileEntry{803 try file_entries.append(FileEntry{
797 .file_name = file_name,804 .file_name = file_name,
798 .dir_index = dir_index,805 .dir_index = dir_index,
...@@ -802,7 +809,7 @@ pub const DwarfInfo = struct {...@@ -802,7 +809,7 @@ pub const DwarfInfo = struct {
802 },809 },
803 else => {810 else => {
804 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;811 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);
806 },813 },
807 }814 }
808 } else if (opcode >= opcode_base) {815 } else if (opcode >= opcode_base) {
...@@ -821,19 +828,19 @@ pub const DwarfInfo = struct {...@@ -821,19 +828,19 @@ pub const DwarfInfo = struct {
821 prog.basic_block = false;828 prog.basic_block = false;
822 },829 },
823 LNS_advance_pc => {830 LNS_advance_pc => {
824 const arg = try leb.readULEB128(usize, &s.stream);831 const arg = try leb.readULEB128(usize, in);
825 prog.address += arg * minimum_instruction_length;832 prog.address += arg * minimum_instruction_length;
826 },833 },
827 LNS_advance_line => {834 LNS_advance_line => {
828 const arg = try leb.readILEB128(i64, &s.stream);835 const arg = try leb.readILEB128(i64, in);
829 prog.line += arg;836 prog.line += arg;
830 },837 },
831 LNS_set_file => {838 LNS_set_file => {
832 const arg = try leb.readULEB128(usize, &s.stream);839 const arg = try leb.readULEB128(usize, in);
833 prog.file = arg;840 prog.file = arg;
834 },841 },
835 LNS_set_column => {842 LNS_set_column => {
836 const arg = try leb.readULEB128(u64, &s.stream);843 const arg = try leb.readULEB128(u64, in);
837 prog.column = arg;844 prog.column = arg;
838 },845 },
839 LNS_negate_stmt => {846 LNS_negate_stmt => {
...@@ -847,14 +854,14 @@ pub const DwarfInfo = struct {...@@ -847,14 +854,14 @@ pub const DwarfInfo = struct {
847 prog.address += inc_addr;854 prog.address += inc_addr;
848 },855 },
849 LNS_fixed_advance_pc => {856 LNS_fixed_advance_pc => {
850 const arg = try s.stream.readInt(u16, di.endian);857 const arg = try in.readInt(u16, di.endian);
851 prog.address += arg;858 prog.address += arg;
852 },859 },
853 LNS_set_prologue_end => {},860 LNS_set_prologue_end => {},
854 else => {861 else => {
855 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;862 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
856 const len_bytes = standard_opcode_lengths[opcode - 1];863 const len_bytes = standard_opcode_lengths[opcode - 1];
857 try s.seekable_stream.seekBy(len_bytes);864 try seekable.seekBy(len_bytes);
858 },865 },
859 }866 }
860 }867 }
lib/std/elf.zig+207-193
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const builtin = @import("builtin");
2const std = @import("std.zig");1const std = @import("std.zig");
2const builtin = std.builtin;
3const io = std.io;3const io = std.io;
4const os = std.os;4const os = std.os;
5const math = std.math;5const math = std.math;
...@@ -330,218 +330,232 @@ pub const ET = extern enum(u16) {...@@ -330,218 +330,232 @@ pub const ET = extern enum(u16) {
330 pub const HIPROC = 0xffff;330 pub const HIPROC = 0xffff;
331};331};
332332
333pub const SectionHeader = Elf64_Shdr;333/// All integers are native endian.
334pub const ProgramHeader = Elf64_Phdr;334const Header = struct {
335
336pub const Elf = struct {
337 seekable_stream: *io.SeekableStream(anyerror, anyerror),
338 in_stream: *io.InStream(anyerror),
339 is_64: bool,
340 endian: builtin.Endian,335 endian: builtin.Endian,
341 file_type: ET,336 is_64: bool,
342 arch: EM,337 entry: u64,
343 entry_addr: u64,338 phoff: u64,
344 program_header_offset: u64,339 shoff: u64,
345 section_header_offset: u64,340 phentsize: u16,
346 string_section_index: usize,341 phnum: u16,
347 string_section: *SectionHeader,342 shentsize: u16,
348 section_headers: []SectionHeader,343 shnum: u16,
349 program_headers: []ProgramHeader,344 shstrndx: u16,
350 allocator: *mem.Allocator,345};
351
352 pub fn openStream(
353 allocator: *mem.Allocator,
354 seekable_stream: *io.SeekableStream(anyerror, anyerror),
355 in: *io.InStream(anyerror),
356 ) !Elf {
357 var elf: Elf = undefined;
358 elf.allocator = allocator;
359 elf.seekable_stream = seekable_stream;
360 elf.in_stream = in;
361
362 var magic: [4]u8 = undefined;
363 try in.readNoEof(magic[0..]);
364 if (!mem.eql(u8, &magic, "\x7fELF")) return error.InvalidFormat;
365
366 elf.is_64 = switch (try in.readByte()) {
367 1 => false,
368 2 => true,
369 else => return error.InvalidFormat,
370 };
371
372 elf.endian = switch (try in.readByte()) {
373 1 => .Little,
374 2 => .Big,
375 else => return error.InvalidFormat,
376 };
377
378 const version_byte = try in.readByte();
379 if (version_byte != 1) return error.InvalidFormat;
380
381 // skip over padding
382 try seekable_stream.seekBy(9);
383346
384 elf.file_type = try in.readEnum(ET, elf.endian);347pub fn readHeader(file: File) !Header {
385 elf.arch = try in.readEnum(EM, elf.endian);348 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
349 try preadNoEof(file, &hdr_buf, 0);
350 const hdr32 = @ptrCast(*Elf32_Ehdr, &hdr_buf);
351 const hdr64 = @ptrCast(*Elf64_Ehdr, &hdr_buf);
352 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
353 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
354
355 const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) {
356 ELFDATA2LSB => .Little,
357 ELFDATA2MSB => .Big,
358 else => return error.InvalidElfEndian,
359 };
360 const need_bswap = endian != std.builtin.endian;
361
362 const is_64 = switch (hdr32.e_ident[EI_CLASS]) {
363 ELFCLASS32 => false,
364 ELFCLASS64 => true,
365 else => return error.InvalidElfClass,
366 };
367
368 return @as(Header, .{
369 .endian = endian,
370 .is_64 = is_64,
371 .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),
372 .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),
373 .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),
374 .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),
375 .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),
376 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
377 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
378 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
379 });
380}
386381
387 const elf_version = try in.readInt(u32, elf.endian);382/// All integers are native endian.
388 if (elf_version != 1) return error.InvalidFormat;383pub const AllHeaders = struct {
384 header: Header,
385 section_headers: []Elf64_Shdr,
386 program_headers: []Elf64_Phdr,
387 allocator: *mem.Allocator,
388};
389389
390 if (elf.is_64) {390pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
391 elf.entry_addr = try in.readInt(u64, elf.endian);391 var hdrs: AllHeaders = .{
392 elf.program_header_offset = try in.readInt(u64, elf.endian);392 .allocator = allocator,
393 elf.section_header_offset = try in.readInt(u64, elf.endian);393 .header = try readHeader(file),
394 } else {394 .section_headers = undefined,
395 elf.entry_addr = @as(u64, try in.readInt(u32, elf.endian));395 .program_headers = undefined,
396 elf.program_header_offset = @as(u64, try in.readInt(u32, elf.endian));396 };
397 elf.section_header_offset = @as(u64, try in.readInt(u32, elf.endian));397 const is_64 = hdrs.header.is_64;
398 const need_bswap = hdrs.header.endian != std.builtin.endian;
399
400 hdrs.section_headers = try allocator.alloc(Elf64_Shdr, hdrs.header.shnum);
401 errdefer allocator.free(hdrs.section_headers);
402
403 hdrs.program_headers = try allocator.alloc(Elf64_Phdr, hdrs.header.phnum);
404 errdefer allocator.free(hdrs.program_headers);
405
406 // If the ELF file is 64-bit and same-endianness, then all we have to do is
407 // yeet the bytes into memory.
408 // If only the endianness is different, they can be simply byte swapped.
409 if (is_64) {
410 const shdr_buf = std.mem.sliceAsBytes(hdrs.section_headers);
411 const phdr_buf = std.mem.sliceAsBytes(hdrs.program_headers);
412 try preadNoEof(file, shdr_buf, hdrs.header.shoff);
413 try preadNoEof(file, phdr_buf, hdrs.header.phoff);
414
415 if (need_bswap) {
416 for (hdrs.section_headers) |*shdr| {
417 shdr.* = .{
418 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
419 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
420 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
421 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
422 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
423 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
424 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
425 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
426 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
427 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
428 };
429 }
430 for (hdrs.program_headers) |*phdr| {
431 phdr.* = .{
432 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
433 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
434 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
435 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
436 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
437 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
438 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
439 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
440 };
441 }
398 }442 }
399443
400 // skip over flags444 return hdrs;
401 try seekable_stream.seekBy(4);445 }
402446
403 const header_size = try in.readInt(u16, elf.endian);447 const shdrs_32 = try allocator.alloc(Elf32_Shdr, hdrs.header.shnum);
404 if ((elf.is_64 and header_size != @sizeOf(Elf64_Ehdr)) or (!elf.is_64 and header_size != @sizeOf(Elf32_Ehdr))) {448 defer allocator.free(shdrs_32);
405 return error.InvalidFormat;449
450 const phdrs_32 = try allocator.alloc(Elf32_Phdr, hdrs.header.phnum);
451 defer allocator.free(phdrs_32);
452
453 const shdr_buf = std.mem.sliceAsBytes(shdrs_32);
454 const phdr_buf = std.mem.sliceAsBytes(phdrs_32);
455 try preadNoEof(file, shdr_buf, hdrs.header.shoff);
456 try preadNoEof(file, phdr_buf, hdrs.header.phoff);
457
458 if (need_bswap) {
459 for (hdrs.section_headers) |*shdr, i| {
460 const o = shdrs_32[i];
461 shdr.* = .{
462 .sh_name = @byteSwap(@TypeOf(o.sh_name), o.sh_name),
463 .sh_type = @byteSwap(@TypeOf(o.sh_type), o.sh_type),
464 .sh_flags = @byteSwap(@TypeOf(o.sh_flags), o.sh_flags),
465 .sh_addr = @byteSwap(@TypeOf(o.sh_addr), o.sh_addr),
466 .sh_offset = @byteSwap(@TypeOf(o.sh_offset), o.sh_offset),
467 .sh_size = @byteSwap(@TypeOf(o.sh_size), o.sh_size),
468 .sh_link = @byteSwap(@TypeOf(o.sh_link), o.sh_link),
469 .sh_info = @byteSwap(@TypeOf(o.sh_info), o.sh_info),
470 .sh_addralign = @byteSwap(@TypeOf(o.sh_addralign), o.sh_addralign),
471 .sh_entsize = @byteSwap(@TypeOf(o.sh_entsize), o.sh_entsize),
472 };
406 }473 }
407474 for (hdrs.program_headers) |*phdr, i| {
408 const ph_entry_size = try in.readInt(u16, elf.endian);475 const o = phdrs_32[i];
409 const ph_entry_count = try in.readInt(u16, elf.endian);476 phdr.* = .{
410477 .p_type = @byteSwap(@TypeOf(o.p_type), o.p_type),
411 if ((elf.is_64 and ph_entry_size != @sizeOf(Elf64_Phdr)) or (!elf.is_64 and ph_entry_size != @sizeOf(Elf32_Phdr))) {478 .p_offset = @byteSwap(@TypeOf(o.p_offset), o.p_offset),
412 return error.InvalidFormat;479 .p_vaddr = @byteSwap(@TypeOf(o.p_vaddr), o.p_vaddr),
480 .p_paddr = @byteSwap(@TypeOf(o.p_paddr), o.p_paddr),
481 .p_filesz = @byteSwap(@TypeOf(o.p_filesz), o.p_filesz),
482 .p_memsz = @byteSwap(@TypeOf(o.p_memsz), o.p_memsz),
483 .p_flags = @byteSwap(@TypeOf(o.p_flags), o.p_flags),
484 .p_align = @byteSwap(@TypeOf(o.p_align), o.p_align),
485 };
413 }486 }
414487 } else {
415 const sh_entry_size = try in.readInt(u16, elf.endian);488 for (hdrs.section_headers) |*shdr, i| {
416 const sh_entry_count = try in.readInt(u16, elf.endian);489 const o = shdrs_32[i];
417490 shdr.* = .{
418 if ((elf.is_64 and sh_entry_size != @sizeOf(Elf64_Shdr)) or (!elf.is_64 and sh_entry_size != @sizeOf(Elf32_Shdr))) {491 .sh_name = o.sh_name,
419 return error.InvalidFormat;492 .sh_type = o.sh_type,
493 .sh_flags = o.sh_flags,
494 .sh_addr = o.sh_addr,
495 .sh_offset = o.sh_offset,
496 .sh_size = o.sh_size,
497 .sh_link = o.sh_link,
498 .sh_info = o.sh_info,
499 .sh_addralign = o.sh_addralign,
500 .sh_entsize = o.sh_entsize,
501 };
420 }502 }
421503 for (hdrs.program_headers) |*phdr, i| {
422 elf.string_section_index = @as(usize, try in.readInt(u16, elf.endian));504 const o = phdrs_32[i];
423505 phdr.* = .{
424 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;506 .p_type = o.p_type,
425507 .p_offset = o.p_offset,
426 const sh_byte_count = @as(u64, sh_entry_size) * @as(u64, sh_entry_count);508 .p_vaddr = o.p_vaddr,
427 const end_sh = try math.add(u64, elf.section_header_offset, sh_byte_count);509 .p_paddr = o.p_paddr,
428 const ph_byte_count = @as(u64, ph_entry_size) * @as(u64, ph_entry_count);510 .p_filesz = o.p_filesz,
429 const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count);511 .p_memsz = o.p_memsz,
430512 .p_flags = o.p_flags,
431 const stream_end = try seekable_stream.getEndPos();513 .p_align = o.p_align,
432 if (stream_end < end_sh or stream_end < end_ph) {514 };
433 return error.InvalidFormat;
434 }515 }
516 }
435517
436 try seekable_stream.seekTo(elf.program_header_offset);518 return hdrs;
437519}
438 elf.program_headers = try elf.allocator.alloc(ProgramHeader, ph_entry_count);
439 errdefer elf.allocator.free(elf.program_headers);
440
441 if (elf.is_64) {
442 for (elf.program_headers) |*elf_program| {
443 elf_program.p_type = try in.readInt(Elf64_Word, elf.endian);
444 elf_program.p_flags = try in.readInt(Elf64_Word, elf.endian);
445 elf_program.p_offset = try in.readInt(Elf64_Off, elf.endian);
446 elf_program.p_vaddr = try in.readInt(Elf64_Addr, elf.endian);
447 elf_program.p_paddr = try in.readInt(Elf64_Addr, elf.endian);
448 elf_program.p_filesz = try in.readInt(Elf64_Xword, elf.endian);
449 elf_program.p_memsz = try in.readInt(Elf64_Xword, elf.endian);
450 elf_program.p_align = try in.readInt(Elf64_Xword, elf.endian);
451 }
452 } else {
453 for (elf.program_headers) |*elf_program| {
454 elf_program.p_type = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
455 elf_program.p_offset = @as(Elf64_Off, try in.readInt(Elf32_Off, elf.endian));
456 elf_program.p_vaddr = @as(Elf64_Addr, try in.readInt(Elf32_Addr, elf.endian));
457 elf_program.p_paddr = @as(Elf64_Addr, try in.readInt(Elf32_Addr, elf.endian));
458 elf_program.p_filesz = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
459 elf_program.p_memsz = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
460 elf_program.p_flags = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
461 elf_program.p_align = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
462 }
463 }
464520
465 try seekable_stream.seekTo(elf.section_header_offset);521pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
466522 if (is_64) {
467 elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count);523 if (need_bswap) {
468 errdefer elf.allocator.free(elf.section_headers);524 return @byteSwap(@TypeOf(int_64), int_64);
469
470 if (elf.is_64) {
471 for (elf.section_headers) |*elf_section| {
472 elf_section.sh_name = try in.readInt(u32, elf.endian);
473 elf_section.sh_type = try in.readInt(u32, elf.endian);
474 elf_section.sh_flags = try in.readInt(u64, elf.endian);
475 elf_section.sh_addr = try in.readInt(u64, elf.endian);
476 elf_section.sh_offset = try in.readInt(u64, elf.endian);
477 elf_section.sh_size = try in.readInt(u64, elf.endian);
478 elf_section.sh_link = try in.readInt(u32, elf.endian);
479 elf_section.sh_info = try in.readInt(u32, elf.endian);
480 elf_section.sh_addralign = try in.readInt(u64, elf.endian);
481 elf_section.sh_entsize = try in.readInt(u64, elf.endian);
482 }
483 } else {525 } else {
484 for (elf.section_headers) |*elf_section| {526 return int_64;
485 // TODO (multiple occurrences) allow implicit cast from %u32 -> %u64 ?
486 elf_section.sh_name = try in.readInt(u32, elf.endian);
487 elf_section.sh_type = try in.readInt(u32, elf.endian);
488 elf_section.sh_flags = @as(u64, try in.readInt(u32, elf.endian));
489 elf_section.sh_addr = @as(u64, try in.readInt(u32, elf.endian));
490 elf_section.sh_offset = @as(u64, try in.readInt(u32, elf.endian));
491 elf_section.sh_size = @as(u64, try in.readInt(u32, elf.endian));
492 elf_section.sh_link = try in.readInt(u32, elf.endian);
493 elf_section.sh_info = try in.readInt(u32, elf.endian);
494 elf_section.sh_addralign = @as(u64, try in.readInt(u32, elf.endian));
495 elf_section.sh_entsize = @as(u64, try in.readInt(u32, elf.endian));
496 }
497 }527 }
498528 } else {
499 for (elf.section_headers) |*elf_section| {529 return int32(need_bswap, int_32, @TypeOf(int_64));
500 if (elf_section.sh_type != SHT_NOBITS) {
501 const file_end_offset = try math.add(u64, elf_section.sh_offset, elf_section.sh_size);
502 if (stream_end < file_end_offset) return error.InvalidFormat;
503 }
504 }
505
506 elf.string_section = &elf.section_headers[elf.string_section_index];
507 if (elf.string_section.sh_type != SHT_STRTAB) {
508 // not a string table
509 return error.InvalidFormat;
510 }
511
512 return elf;
513 }530 }
531}
514532
515 pub fn close(elf: *Elf) void {533pub fn int32(need_bswap: bool, int_32: var, comptime Int64: var) Int64 {
516 elf.allocator.free(elf.section_headers);534 if (need_bswap) {
517 elf.allocator.free(elf.program_headers);535 return @byteSwap(@TypeOf(int_32), int_32);
518 }536 } else {
519537 return int_32;
520 pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
521 section_loop: for (elf.section_headers) |*elf_section| {
522 if (elf_section.sh_type == SHT_NULL) continue;
523
524 const name_offset = elf.string_section.sh_offset + elf_section.sh_name;
525 try elf.seekable_stream.seekTo(name_offset);
526
527 for (name) |expected_c| {
528 const target_c = try elf.in_stream.readByte();
529 if (target_c == 0 or expected_c != target_c) continue :section_loop;
530 }
531
532 {
533 const null_byte = try elf.in_stream.readByte();
534 if (null_byte == 0) return elf_section;
535 }
536 }
537
538 return null;
539 }538 }
539}
540540
541 pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void {541fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
542 try elf.seekable_stream.seekTo(elf_section.sh_offset);542 var i: u64 = 0;
543 while (i < buf.len) {
544 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
545 error.SystemResources => return error.SystemResources,
546 error.IsDir => return error.UnableToReadElfFile,
547 error.OperationAborted => return error.UnableToReadElfFile,
548 error.BrokenPipe => return error.UnableToReadElfFile,
549 error.Unseekable => return error.UnableToReadElfFile,
550 error.ConnectionResetByPeer => return error.UnableToReadElfFile,
551 error.InputOutput => return error.FileSystem,
552 error.Unexpected => return error.Unexpected,
553 error.WouldBlock => return error.Unexpected,
554 };
555 if (len == 0) return error.UnexpectedEndOfFile;
556 i += len;
543 }557 }
544};558}
545559
546pub const EI_NIDENT = 16;560pub const EI_NIDENT = 16;
547561
lib/std/event/group.zig+3-1
...@@ -120,9 +120,11 @@ test "std.event.Group" {...@@ -120,9 +120,11 @@ test "std.event.Group" {
120 // https://github.com/ziglang/zig/issues/1908120 // https://github.com/ziglang/zig/issues/1908
121 if (builtin.single_threaded) return error.SkipZigTest;121 if (builtin.single_threaded) return error.SkipZigTest;
122122
123 // TODO provide a way to run tests in evented I/O mode
124 if (!std.io.is_async) return error.SkipZigTest;123 if (!std.io.is_async) return error.SkipZigTest;
125124
125 // TODO this file has bit-rotted. repair it
126 if (true) return error.SkipZigTest;
127
126 const handle = async testGroup(std.heap.page_allocator);128 const handle = async testGroup(std.heap.page_allocator);
127}129}
128130
lib/std/event/lock.zig+3
...@@ -125,6 +125,9 @@ test "std.event.Lock" {...@@ -125,6 +125,9 @@ test "std.event.Lock" {
125 // TODO https://github.com/ziglang/zig/issues/3251125 // TODO https://github.com/ziglang/zig/issues/3251
126 if (builtin.os.tag == .freebsd) return error.SkipZigTest;126 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
127127
128 // TODO this file has bit-rotted. repair it
129 if (true) return error.SkipZigTest;
130
128 var lock = Lock.init();131 var lock = Lock.init();
129 defer lock.deinit();132 defer lock.deinit();
130133
lib/std/fs.zig+13-22
...@@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {...@@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
96/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.96/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
97/// Returns the previous status of the file before updating.97/// Returns the previous status of the file before updating.
98/// If any of the directories do not exist for dest_path, they are created.98/// If any of the directories do not exist for dest_path, they are created.
99/// TODO rework this to integrate with Dir
99pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {100pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
100 const my_cwd = cwd();101 const my_cwd = cwd();
101102
...@@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil...@@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
141/// there is a possibility of power loss or application termination leaving temporary files present142/// there is a possibility of power loss or application termination leaving temporary files present
142/// in the same directory as dest_path.143/// in the same directory as dest_path.
143/// Destination file will have the same mode as the source file.144/// Destination file will have the same mode as the source file.
145/// TODO rework this to integrate with Dir
144pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {146pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
145 var in_file = try cwd().openFile(source_path, .{});147 var in_file = try cwd().openFile(source_path, .{});
146 defer in_file.close();148 defer in_file.close();
147149
148 const mode = try in_file.mode();150 const stat = try in_file.stat();
149 const in_stream = &in_file.inStream().stream;
150151
151 var atomic_file = try AtomicFile.init(dest_path, mode);152 var atomic_file = try AtomicFile.init(dest_path, stat.mode);
152 defer atomic_file.deinit();153 defer atomic_file.deinit();
153154
154 var buf: [mem.page_size]u8 = undefined;155 try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
155 while (true) {156 return atomic_file.finish();
156 const amt = try in_stream.readFull(buf[0..]);
157 try atomic_file.file.write(buf[0..amt]);
158 if (amt != buf.len) {
159 return atomic_file.finish();
160 }
161 }
162}157}
163158
164/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is159/// Guaranteed to be atomic.
165/// merged and readily available,160/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
166/// there is a possibility of power loss or application termination leaving temporary files present161/// there is a possibility of power loss or application termination leaving temporary files present
162/// in the same directory as dest_path.
163/// TODO rework this to integrate with Dir
167pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {164pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
168 var in_file = try cwd().openFile(source_path, .{});165 var in_file = try cwd().openFile(source_path, .{});
169 defer in_file.close();166 defer in_file.close();
...@@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M...@@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
171 var atomic_file = try AtomicFile.init(dest_path, mode);168 var atomic_file = try AtomicFile.init(dest_path, mode);
172 defer atomic_file.deinit();169 defer atomic_file.deinit();
173170
174 var buf: [mem.page_size * 6]u8 = undefined;171 try atomic_file.file.writeFileAll(in_file, .{});
175 while (true) {172 return atomic_file.finish();
176 const amt = try in_file.read(buf[0..]);
177 try atomic_file.file.write(buf[0..amt]);
178 if (amt != buf.len) {
179 return atomic_file.finish();
180 }
181 }
182}173}
183174
184/// TODO update this API to avoid a getrandom syscall for every operation. It175/// TODO update this API to avoid a getrandom syscall for every operation. It
...@@ -1150,7 +1141,7 @@ pub const Dir = struct {...@@ -1150,7 +1141,7 @@ pub const Dir = struct {
1150 const buf = try allocator.alignedAlloc(u8, A, size);1141 const buf = try allocator.alignedAlloc(u8, A, size);
1151 errdefer allocator.free(buf);1142 errdefer allocator.free(buf);
11521143
1153 try file.inStream().stream.readNoEof(buf);1144 try file.inStream().readNoEof(buf);
1154 return buf;1145 return buf;
1155 }1146 }
11561147
lib/std/fs/file.zig+52-84
...@@ -71,7 +71,7 @@ pub const File = struct {...@@ -71,7 +71,7 @@ pub const File = struct {
71 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {71 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
72 std.event.Loop.instance.?.close(self.handle);72 std.event.Loop.instance.?.close(self.handle);
73 } else {73 } else {
74 return os.close(self.handle);74 os.close(self.handle);
75 }75 }
76 }76 }
7777
...@@ -250,11 +250,16 @@ pub const File = struct {...@@ -250,11 +250,16 @@ pub const File = struct {
250 }250 }
251 }251 }
252252
253 pub fn readAll(self: File, buffer: []u8) ReadError!void {253 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
254 /// means the file reached the end. Reaching the end of a file is not an error condition.
255 pub fn readAll(self: File, buffer: []u8) ReadError!usize {
254 var index: usize = 0;256 var index: usize = 0;
255 while (index < buffer.len) {257 while (index != buffer.len) {
256 index += try self.read(buffer[index..]);258 const amt = try self.read(buffer[index..]);
259 if (amt == 0) break;
260 index += amt;
257 }261 }
262 return index;
258 }263 }
259264
260 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {265 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
...@@ -265,11 +270,16 @@ pub const File = struct {...@@ -265,11 +270,16 @@ pub const File = struct {
265 }270 }
266 }271 }
267272
268 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!void {273 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
274 /// means the file reached the end. Reaching the end of a file is not an error condition.
275 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
269 var index: usize = 0;276 var index: usize = 0;
270 while (index < buffer.len) {277 while (index != buffer.len) {
271 index += try self.pread(buffer[index..], offset + index);278 const amt = try self.pread(buffer[index..], offset + index);
279 if (amt == 0) break;
280 index += amt;
272 }281 }
282 return index;
273 }283 }
274284
275 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {285 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
...@@ -280,19 +290,27 @@ pub const File = struct {...@@ -280,19 +290,27 @@ pub const File = struct {
280 }290 }
281 }291 }
282292
293 /// Returns the number of bytes read. If the number read is smaller than the total bytes
294 /// from all the buffers, it means the file reached the end. Reaching the end of a file
295 /// is not an error condition.
283 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in296 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
284 /// order to handle partial reads from the underlying OS layer.297 /// order to handle partial reads from the underlying OS layer.
285 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void {298 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
286 if (iovecs.len == 0) return;299 if (iovecs.len == 0) return;
287300
288 var i: usize = 0;301 var i: usize = 0;
302 var off: usize = 0;
289 while (true) {303 while (true) {
290 var amt = try self.readv(iovecs[i..]);304 var amt = try self.readv(iovecs[i..]);
305 var eof = amt == 0;
306 off += amt;
291 while (amt >= iovecs[i].iov_len) {307 while (amt >= iovecs[i].iov_len) {
292 amt -= iovecs[i].iov_len;308 amt -= iovecs[i].iov_len;
293 i += 1;309 i += 1;
294 if (i >= iovecs.len) return;310 if (i >= iovecs.len) return off;
311 eof = false;
295 }312 }
313 if (eof) return off;
296 iovecs[i].iov_base += amt;314 iovecs[i].iov_base += amt;
297 iovecs[i].iov_len -= amt;315 iovecs[i].iov_len -= amt;
298 }316 }
...@@ -306,6 +324,9 @@ pub const File = struct {...@@ -306,6 +324,9 @@ pub const File = struct {
306 }324 }
307 }325 }
308326
327 /// Returns the number of bytes read. If the number read is smaller than the total bytes
328 /// from all the buffers, it means the file reached the end. Reaching the end of a file
329 /// is not an error condition.
309 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in330 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
310 /// order to handle partial reads from the underlying OS layer.331 /// order to handle partial reads from the underlying OS layer.
311 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {332 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
...@@ -315,12 +336,15 @@ pub const File = struct {...@@ -315,12 +336,15 @@ pub const File = struct {
315 var off: usize = 0;336 var off: usize = 0;
316 while (true) {337 while (true) {
317 var amt = try self.preadv(iovecs[i..], offset + off);338 var amt = try self.preadv(iovecs[i..], offset + off);
339 var eof = amt == 0;
318 off += amt;340 off += amt;
319 while (amt >= iovecs[i].iov_len) {341 while (amt >= iovecs[i].iov_len) {
320 amt -= iovecs[i].iov_len;342 amt -= iovecs[i].iov_len;
321 i += 1;343 i += 1;
322 if (i >= iovecs.len) return;344 if (i >= iovecs.len) return off;
345 eof = false;
323 }346 }
347 if (eof) return off;
324 iovecs[i].iov_base += amt;348 iovecs[i].iov_base += amt;
325 iovecs[i].iov_len -= amt;349 iovecs[i].iov_len -= amt;
326 }350 }
...@@ -496,85 +520,29 @@ pub const File = struct {...@@ -496,85 +520,29 @@ pub const File = struct {
496 }520 }
497 }521 }
498522
499 pub fn inStream(file: File) InStream {523 pub const InStream = io.InStream(File, ReadError, read);
500 return InStream{524
501 .file = file,525 pub fn inStream(file: File) io.InStream(File, ReadError, read) {
502 .stream = InStream.Stream{ .readFn = InStream.readFn },526 return .{ .context = file };
503 };
504 }527 }
505528
529 pub const OutStream = io.OutStream(File, WriteError, write);
530
506 pub fn outStream(file: File) OutStream {531 pub fn outStream(file: File) OutStream {
507 return OutStream{532 return .{ .context = file };
508 .file = file,
509 .stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
510 };
511 }533 }
512534
535 pub const SeekableStream = io.SeekableStream(
536 File,
537 SeekError,
538 GetPosError,
539 seekTo,
540 seekBy,
541 getPos,
542 getEndPos,
543 );
544
513 pub fn seekableStream(file: File) SeekableStream {545 pub fn seekableStream(file: File) SeekableStream {
514 return SeekableStream{546 return .{ .context = file };
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 };
523 }547 }
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 };
580};548};
lib/std/heap.zig+1
...@@ -10,6 +10,7 @@ const c = std.c;...@@ -10,6 +10,7 @@ const c = std.c;
10const maxInt = std.math.maxInt;10const maxInt = std.math.maxInt;
1111
12pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;12pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
13pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
1314
14const Allocator = mem.Allocator;15const Allocator = mem.Allocator;
1516
lib/std/heap/logging_allocator.zig+51-45
...@@ -1,63 +1,69 @@...@@ -1,63 +1,69 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const Allocator = std.mem.Allocator;2const Allocator = std.mem.Allocator;
33
4const AnyErrorOutStream = std.io.OutStream(anyerror);
5
6/// This allocator is used in front of another allocator and logs to the provided stream4/// This allocator is used in front of another allocator and logs to the provided stream
7/// on every call to the allocator. Stream errors are ignored.5/// on every call to the allocator. Stream errors are ignored.
8/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.6/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.
9pub const LoggingAllocator = struct {7pub fn LoggingAllocator(comptime OutStreamType: type) type {
10 allocator: Allocator,8 return struct {
11 parent_allocator: *Allocator,9 allocator: Allocator,
12 out_stream: *AnyErrorOutStream,10 parent_allocator: *Allocator,
11 out_stream: OutStreamType,
1312
14 const Self = @This();13 const Self = @This();
1514
16 pub fn init(parent_allocator: *Allocator, out_stream: *AnyErrorOutStream) Self {15 pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
17 return Self{16 return Self{
18 .allocator = Allocator{17 .allocator = Allocator{
19 .reallocFn = realloc,18 .reallocFn = realloc,
20 .shrinkFn = shrink,19 .shrinkFn = shrink,
21 },20 },
22 .parent_allocator = parent_allocator,21 .parent_allocator = parent_allocator,
23 .out_stream = out_stream,22 .out_stream = out_stream,
24 };23 };
25 }
26
27 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
28 const self = @fieldParentPtr(Self, "allocator", allocator);
29 if (old_mem.len == 0) {
30 self.out_stream.print("allocation of {} ", .{new_size}) catch {};
31 } else {
32 self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {};
33 }24 }
34 const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);25
35 if (result) |buff| {26 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
36 self.out_stream.print("success!\n", .{}) catch {};27 const self = @fieldParentPtr(Self, "allocator", allocator);
37 } else |err| {28 if (old_mem.len == 0) {
38 self.out_stream.print("failure!\n", .{}) catch {};29 self.out_stream.print("allocation of {} ", .{new_size}) catch {};
30 } else {
31 self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {};
32 }
33 const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
34 if (result) |buff| {
35 self.out_stream.print("success!\n", .{}) catch {};
36 } else |err| {
37 self.out_stream.print("failure!\n", .{}) catch {};
38 }
39 return result;
39 }40 }
40 return result;
41 }
4241
43 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {42 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
44 const self = @fieldParentPtr(Self, "allocator", allocator);43 const self = @fieldParentPtr(Self, "allocator", allocator);
45 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);44 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
46 if (new_size == 0) {45 if (new_size == 0) {
47 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};46 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
48 } else {47 } else {
49 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};48 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
49 }
50 return result;
50 }51 }
51 return result;52 };
52 }53}
53};54
55pub fn loggingAllocator(
56 parent_allocator: *Allocator,
57 out_stream: var,
58) LoggingAllocator(@TypeOf(out_stream)) {
59 return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);
60}
5461
55test "LoggingAllocator" {62test "LoggingAllocator" {
56 var buf: [255]u8 = undefined;63 var buf: [255]u8 = undefined;
57 var slice_stream = std.io.SliceOutStream.init(buf[0..]);64 var fbs = std.io.fixedBufferStream(&buf);
58 const stream = &slice_stream.stream;
5965
60 const allocator = &LoggingAllocator.init(std.testing.allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator;66 const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator;
6167
62 const ptr = try allocator.alloc(u8, 10);68 const ptr = try allocator.alloc(u8, 10);
63 allocator.free(ptr);69 allocator.free(ptr);
...@@ -66,5 +72,5 @@ test "LoggingAllocator" {...@@ -66,5 +72,5 @@ test "LoggingAllocator" {
66 \\allocation of 10 success!72 \\allocation of 10 success!
67 \\free of 10 bytes success!73 \\free of 10 bytes success!
68 \\74 \\
69 , slice_stream.getWritten());75 , fbs.getWritten());
70}76}
lib/std/io.zig+39-1026
...@@ -4,17 +4,13 @@ const root = @import("root");...@@ -4,17 +4,13 @@ const root = @import("root");
4const c = std.c;4const c = std.c;
55
6const math = std.math;6const math = std.math;
7const debug = std.debug;7const assert = std.debug.assert;
8const assert = debug.assert;
9const os = std.os;8const os = std.os;
10const fs = std.fs;9const fs = std.fs;
11const mem = std.mem;10const mem = std.mem;
12const meta = std.meta;11const meta = std.meta;
13const trait = meta.trait;12const trait = meta.trait;
14const Buffer = std.Buffer;
15const fmt = std.fmt;
16const File = std.fs.File;13const File = std.fs.File;
17const testing = std.testing;
1814
19pub const Mode = enum {15pub const Mode = enum {
20 /// I/O operates normally, waiting for the operating system syscalls to complete.16 /// I/O operates normally, waiting for the operating system syscalls to complete.
...@@ -92,1051 +88,68 @@ pub fn getStdIn() File {...@@ -92,1051 +88,68 @@ pub fn getStdIn() File {
92 };88 };
93}89}
9490
95pub 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;
98pub const InStream = @import("io/in_stream.zig").InStream;91pub const InStream = @import("io/in_stream.zig").InStream;
99pub const OutStream = @import("io/out_stream.zig").OutStream;92pub const OutStream = @import("io/out_stream.zig").OutStream;
93pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
10094
101/// Deprecated; use `std.fs.Dir.writeFile`.95pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream;
102pub fn writeFile(path: []const u8, data: []const u8) !void {96pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream;
103 return fs.cwd().writeFile(path, data);
104}
105
106/// Deprecated; use `std.fs.Dir.readFileAlloc`.
107pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
108 return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
109}
110
111pub fn BufferedInStream(comptime Error: type) type {
112 return BufferedInStreamCustom(mem.page_size, Error);
113}
114
115pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type {
116 return struct {
117 const Self = @This();
118 const Stream = InStream(Error);
119
120 stream: Stream,
121
122 unbuffered_in_stream: *Stream,
123
124 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
125 fifo: FifoType,
126
127 pub fn init(unbuffered_in_stream: *Stream) Self {
128 return Self{
129 .unbuffered_in_stream = unbuffered_in_stream,
130 .fifo = FifoType.init(),
131 .stream = Stream{ .readFn = readFn },
132 };
133 }
134
135 fn readFn(in_stream: *Stream, dest: []u8) !usize {
136 const self = @fieldParentPtr(Self, "stream", in_stream);
137 var dest_index: usize = 0;
138 while (dest_index < dest.len) {
139 const written = self.fifo.read(dest[dest_index..]);
140 if (written == 0) {
141 // fifo empty, fill it
142 const writable = self.fifo.writableSlice(0);
143 assert(writable.len > 0);
144 const n = try self.unbuffered_in_stream.read(writable);
145 if (n == 0) {
146 // reading from the unbuffered stream returned nothing
147 // so we have nothing left to read.
148 return dest_index;
149 }
150 self.fifo.update(n);
151 }
152 dest_index += written;
153 }
154 return dest.len;
155 }
156 };
157}
158
159test "io.BufferedInStream" {
160 const OneByteReadInStream = struct {
161 const Error = error{NoError};
162 const Stream = InStream(Error);
163
164 stream: Stream,
165 str: []const u8,
166 curr: usize,
167
168 fn init(str: []const u8) @This() {
169 return @This(){
170 .stream = Stream{ .readFn = readFn },
171 .str = str,
172 .curr = 0,
173 };
174 }
175
176 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
177 const self = @fieldParentPtr(@This(), "stream", in_stream);
178 if (self.str.len <= self.curr or dest.len == 0)
179 return 0;
180
181 dest[0] = self.str[self.curr];
182 self.curr += 1;
183 return 1;
184 }
185 };
186
187 const str = "This is a test";
188 var one_byte_stream = OneByteReadInStream.init(str);
189 var buf_in_stream = BufferedInStream(OneByteReadInStream.Error).init(&one_byte_stream.stream);
190 const stream = &buf_in_stream.stream;
191
192 const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
193 defer testing.allocator.free(res);
194 testing.expectEqualSlices(u8, str, res);
195}
196
197/// Creates a stream which supports 'un-reading' data, so that it can be read again.
198/// This makes look-ahead style parsing much easier.
199pub fn PeekStream(comptime buffer_type: std.fifo.LinearFifoBufferType, comptime InStreamError: type) type {
200 return struct {
201 const Self = @This();
202 pub const Error = InStreamError;
203 pub const Stream = InStream(Error);
204
205 stream: Stream,
206 base: *Stream,
207
208 const FifoType = std.fifo.LinearFifo(u8, buffer_type);
209 fifo: FifoType,
210
211 pub usingnamespace switch (buffer_type) {
212 .Static => struct {
213 pub fn init(base: *Stream) Self {
214 return .{
215 .base = base,
216 .fifo = FifoType.init(),
217 .stream = Stream{ .readFn = readFn },
218 };
219 }
220 },
221 .Slice => struct {
222 pub fn init(base: *Stream, buf: []u8) Self {
223 return .{
224 .base = base,
225 .fifo = FifoType.init(buf),
226 .stream = Stream{ .readFn = readFn },
227 };
228 }
229 },
230 .Dynamic => struct {
231 pub fn init(base: *Stream, allocator: *mem.Allocator) Self {
232 return .{
233 .base = base,
234 .fifo = FifoType.init(allocator),
235 .stream = Stream{ .readFn = readFn },
236 };
237 }
238 },
239 };
240
241 pub fn putBackByte(self: *Self, byte: u8) !void {
242 try self.putBack(&[_]u8{byte});
243 }
244
245 pub fn putBack(self: *Self, bytes: []const u8) !void {
246 try self.fifo.unget(bytes);
247 }
248
249 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
250 const self = @fieldParentPtr(Self, "stream", in_stream);
251
252 // copy over anything putBack()'d
253 var dest_index = self.fifo.read(dest);
254 if (dest_index == dest.len) return dest_index;
255
256 // ask the backing stream for more
257 dest_index += try self.base.read(dest[dest_index..]);
258 return dest_index;
259 }
260 };
261}
262
263pub const SliceInStream = struct {
264 const Self = @This();
265 pub const Error = error{};
266 pub const Stream = InStream(Error);
267
268 stream: Stream,
269
270 pos: usize,
271 slice: []const u8,
272
273 pub fn init(slice: []const u8) Self {
274 return Self{
275 .slice = slice,
276 .pos = 0,
277 .stream = Stream{ .readFn = readFn },
278 };
279 }
280
281 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
282 const self = @fieldParentPtr(Self, "stream", in_stream);
283 const size = math.min(dest.len, self.slice.len - self.pos);
284 const end = self.pos + size;
285
286 mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
287 self.pos = end;
288
289 return size;
290 }
291};
292
293/// Creates a stream which allows for reading bit fields from another stream
294pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
295 return struct {
296 const Self = @This();
297
298 in_stream: *Stream,
299 bit_buffer: u7,
300 bit_count: u3,
301 stream: Stream,
302
303 pub const Stream = InStream(Error);
304 const u8_bit_count = comptime meta.bitCount(u8);
305 const u7_bit_count = comptime meta.bitCount(u7);
306 const u4_bit_count = comptime meta.bitCount(u4);
307
308 pub fn init(in_stream: *Stream) Self {
309 return Self{
310 .in_stream = in_stream,
311 .bit_buffer = 0,
312 .bit_count = 0,
313 .stream = Stream{ .readFn = read },
314 };
315 }
316
317 /// Reads `bits` bits from the stream and returns a specified unsigned int type
318 /// containing them in the least significant end, returning an error if the
319 /// specified number of bits could not be read.
320 pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
321 var n: usize = undefined;
322 const result = try self.readBits(U, bits, &n);
323 if (n < bits) return error.EndOfStream;
324 return result;
325 }
32697
327 /// Reads `bits` bits from the stream and returns a specified unsigned int type98pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream;
328 /// containing them in the least significant end. The number of bits successfully99pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream;
329 /// read is placed in `out_bits`, as reaching the end of the stream is not an error.
330 pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U {
331 comptime assert(trait.isUnsignedInt(U));
332100
333 //by extending the buffer to a minimum of u8 we can cover a number of edge cases101pub const PeekStream = @import("io/peek_stream.zig").PeekStream;
334 // related to shifting and casting.102pub const peekStream = @import("io/peek_stream.zig").peekStream;
335 const u_bit_count = comptime meta.bitCount(U);
336 const buf_bit_count = bc: {
337 assert(u_bit_count >= bits);
338 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
339 };
340 const Buf = std.meta.IntType(false, buf_bit_count);
341 const BufShift = math.Log2Int(Buf);
342103
343 out_bits.* = @as(usize, 0);104pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream;
344 if (U == u0 or bits == 0) return 0;105pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream;
345 var out_buffer = @as(Buf, 0);
346106
347 if (self.bit_count > 0) {107pub const COutStream = @import("io/c_out_stream.zig").COutStream;
348 const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;108pub const cOutStream = @import("io/c_out_stream.zig").cOutStream;
349 const shift = u7_bit_count - n;
350 switch (endian) {
351 .Big => {
352 out_buffer = @as(Buf, self.bit_buffer >> shift);
353 if (n >= u7_bit_count)
354 self.bit_buffer = 0
355 else
356 self.bit_buffer <<= n;
357 },
358 .Little => {
359 const value = (self.bit_buffer << shift) >> shift;
360 out_buffer = @as(Buf, value);
361 if (n >= u7_bit_count)
362 self.bit_buffer = 0
363 else
364 self.bit_buffer >>= n;
365 },
366 }
367 self.bit_count -= n;
368 out_bits.* = n;
369 }
370 //at this point we know bit_buffer is empty
371109
372 //copy bytes until we have enough bits, then leave the rest in bit_buffer110pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream;
373 while (out_bits.* < bits) {111pub const countingOutStream = @import("io/counting_out_stream.zig").countingOutStream;
374 const n = bits - out_bits.*;
375 const next_byte = self.in_stream.readByte() catch |err| {
376 if (err == error.EndOfStream) {
377 return @intCast(U, out_buffer);
378 }
379 //@BUG: See #1810. Not sure if the bug is that I have to do this for some
380 // streams, or that I don't for streams with emtpy errorsets.
381 return @errSetCast(Error, err);
382 };
383112
384 switch (endian) {113pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream;
385 .Big => {114pub const bitInStream = @import("io/bit_in_stream.zig").bitInStream;
386 if (n >= u8_bit_count) {
387 out_buffer <<= @intCast(u3, u8_bit_count - 1);
388 out_buffer <<= 1;
389 out_buffer |= @as(Buf, next_byte);
390 out_bits.* += u8_bit_count;
391 continue;
392 }
393115
394 const shift = @intCast(u3, u8_bit_count - n);116pub const BitOutStream = @import("io/bit_out_stream.zig").BitOutStream;
395 out_buffer <<= @intCast(BufShift, n);117pub const bitOutStream = @import("io/bit_out_stream.zig").bitOutStream;
396 out_buffer |= @as(Buf, next_byte >> shift);
397 out_bits.* += n;
398 self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
399 self.bit_count = shift;
400 },
401 .Little => {
402 if (n >= u8_bit_count) {
403 out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*);
404 out_bits.* += u8_bit_count;
405 continue;
406 }
407118
408 const shift = @intCast(u3, u8_bit_count - n);119pub const Packing = @import("io/serialization.zig").Packing;
409 const value = (next_byte << shift) >> shift;
410 out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*);
411 out_bits.* += n;
412 self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n));
413 self.bit_count = shift;
414 },
415 }
416 }
417120
418 return @intCast(U, out_buffer);121pub const Serializer = @import("io/serialization.zig").Serializer;
419 }122pub const serializer = @import("io/serialization.zig").serializer;
420123
421 pub fn alignToByte(self: *Self) void {124pub const Deserializer = @import("io/serialization.zig").Deserializer;
422 self.bit_buffer = 0;125pub const deserializer = @import("io/serialization.zig").deserializer;
423 self.bit_count = 0;
424 }
425126
426 pub fn read(self_stream: *Stream, buffer: []u8) Error!usize {127pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
427 var self = @fieldParentPtr(Self, "stream", self_stream);
428128
429 var out_bits: usize = undefined;129pub const StreamSource = @import("io/stream_source.zig").StreamSource;
430 var out_bits_total = @as(usize, 0);
431 //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced
432 if (self.bit_count > 0) {
433 for (buffer) |*b, i| {
434 b.* = try self.readBits(u8, u8_bit_count, &out_bits);
435 out_bits_total += out_bits;
436 }
437 const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0);
438 return (out_bits_total / u8_bit_count) + incomplete_byte;
439 }
440130
441 return self.in_stream.read(buffer);131/// Deprecated; use `std.fs.Dir.writeFile`.
442 }132pub fn writeFile(path: []const u8, data: []const u8) !void {
443 };133 return fs.cwd().writeFile(path, data);
444}134}
445135
446/// This is a simple OutStream that writes to a fixed buffer. If the returned number136/// Deprecated; use `std.fs.Dir.readFileAlloc`.
447/// of bytes written is less than requested, the buffer is full.137pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
448/// Returns error.OutOfMemory when no bytes would be written.138 return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
449pub const SliceOutStream = struct {
450 pub const Error = error{OutOfMemory};
451 pub const Stream = OutStream(Error);
452
453 stream: Stream,
454
455 pos: usize,
456 slice: []u8,
457
458 pub fn init(slice: []u8) SliceOutStream {
459 return SliceOutStream{
460 .slice = slice,
461 .pos = 0,
462 .stream = Stream{ .writeFn = writeFn },
463 };
464 }
465
466 pub fn getWritten(self: *const SliceOutStream) []const u8 {
467 return self.slice[0..self.pos];
468 }
469
470 pub fn reset(self: *SliceOutStream) void {
471 self.pos = 0;
472 }
473
474 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
475 const self = @fieldParentPtr(SliceOutStream, "stream", out_stream);
476
477 if (bytes.len == 0) return 0;
478
479 assert(self.pos <= self.slice.len);
480
481 const n = if (self.pos + bytes.len <= self.slice.len)
482 bytes.len
483 else
484 self.slice.len - self.pos;
485
486 std.mem.copy(u8, self.slice[self.pos .. self.pos + n], bytes[0..n]);
487 self.pos += n;
488
489 if (n == 0) return error.OutOfMemory;
490
491 return n;
492 }
493};
494
495test "io.SliceOutStream" {
496 var buf: [255]u8 = undefined;
497 var slice_stream = SliceOutStream.init(buf[0..]);
498 const stream = &slice_stream.stream;
499
500 try stream.print("{}{}!", .{ "Hello", "World" });
501 testing.expectEqualSlices(u8, "HelloWorld!", slice_stream.getWritten());
502}139}
503140
504var null_out_stream_state = NullOutStream.init();
505pub const null_out_stream = &null_out_stream_state.stream;
506
507/// An OutStream that doesn't write to anything.141/// An OutStream that doesn't write to anything.
508pub const NullOutStream = struct {142pub const null_out_stream = @as(NullOutStream, .{ .context = {} });
509 pub const Error = error{};
510 pub const Stream = OutStream(Error);
511
512 stream: Stream,
513
514 pub fn init() NullOutStream {
515 return NullOutStream{
516 .stream = Stream{ .writeFn = writeFn },
517 };
518 }
519
520 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
521 return bytes.len;
522 }
523};
524
525test "io.NullOutStream" {
526 var null_stream = NullOutStream.init();
527 const stream = &null_stream.stream;
528 stream.write("yay" ** 10000) catch unreachable;
529}
530
531/// An OutStream that counts how many bytes has been written to it.
532pub fn CountingOutStream(comptime OutStreamError: type) type {
533 return struct {
534 const Self = @This();
535 pub const Stream = OutStream(Error);
536 pub const Error = OutStreamError;
537
538 stream: Stream,
539 bytes_written: u64,
540 child_stream: *Stream,
541
542 pub fn init(child_stream: *Stream) Self {
543 return Self{
544 .stream = Stream{ .writeFn = writeFn },
545 .bytes_written = 0,
546 .child_stream = child_stream,
547 };
548 }
549
550 fn writeFn(out_stream: *Stream, bytes: []const u8) OutStreamError!usize {
551 const self = @fieldParentPtr(Self, "stream", out_stream);
552 try self.child_stream.write(bytes);
553 self.bytes_written += bytes.len;
554 return bytes.len;
555 }
556 };
557}
558
559test "io.CountingOutStream" {
560 var null_stream = NullOutStream.init();
561 var counting_stream = CountingOutStream(NullOutStream.Error).init(&null_stream.stream);
562 const stream = &counting_stream.stream;
563
564 const bytes = "yay" ** 10000;
565 stream.write(bytes) catch unreachable;
566 testing.expect(counting_stream.bytes_written == bytes.len);
567}
568143
569pub fn BufferedOutStream(comptime Error: type) type {144const NullOutStream = OutStream(void, error{}, dummyWrite);
570 return BufferedOutStreamCustom(mem.page_size, Error);145fn dummyWrite(context: void, data: []const u8) error{}!usize {
146 return data.len;
571}147}
572148
573pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type {149test "null_out_stream" {
574 return struct {150 null_out_stream.writeAll("yay" ** 10) catch |err| switch (err) {};
575 const Self = @This();
576 pub const Stream = OutStream(Error);
577 pub const Error = OutStreamError;
578
579 stream: Stream,
580
581 unbuffered_out_stream: *Stream,
582
583 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
584 fifo: FifoType,
585
586 pub fn init(unbuffered_out_stream: *Stream) Self {
587 return Self{
588 .unbuffered_out_stream = unbuffered_out_stream,
589 .fifo = FifoType.init(),
590 .stream = Stream{ .writeFn = writeFn },
591 };
592 }
593
594 pub fn flush(self: *Self) !void {
595 while (true) {
596 const slice = self.fifo.readableSlice(0);
597 if (slice.len == 0) break;
598 try self.unbuffered_out_stream.write(slice);
599 self.fifo.discard(slice.len);
600 }
601 }
602
603 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
604 const self = @fieldParentPtr(Self, "stream", out_stream);
605 if (bytes.len >= self.fifo.writableLength()) {
606 try self.flush();
607 return self.unbuffered_out_stream.writeOnce(bytes);
608 }
609 self.fifo.writeAssumeCapacity(bytes);
610 return bytes.len;
611 }
612 };
613}
614
615/// Implementation of OutStream trait for Buffer
616pub const BufferOutStream = struct {
617 buffer: *Buffer,
618 stream: Stream,
619
620 pub const Error = error{OutOfMemory};
621 pub const Stream = OutStream(Error);
622
623 pub fn init(buffer: *Buffer) BufferOutStream {
624 return BufferOutStream{
625 .buffer = buffer,
626 .stream = Stream{ .writeFn = writeFn },
627 };
628 }
629
630 fn writeFn(out_stream: *Stream, bytes: []const u8) !usize {
631 const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
632 try self.buffer.append(bytes);
633 return bytes.len;
634 }
635};
636
637/// Creates a stream which allows for writing bit fields to another stream
638pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
639 return struct {
640 const Self = @This();
641
642 out_stream: *Stream,
643 bit_buffer: u8,
644 bit_count: u4,
645 stream: Stream,
646
647 pub const Stream = OutStream(Error);
648 const u8_bit_count = comptime meta.bitCount(u8);
649 const u4_bit_count = comptime meta.bitCount(u4);
650
651 pub fn init(out_stream: *Stream) Self {
652 return Self{
653 .out_stream = out_stream,
654 .bit_buffer = 0,
655 .bit_count = 0,
656 .stream = Stream{ .writeFn = write },
657 };
658 }
659
660 /// Write the specified number of bits to the stream from the least significant bits of
661 /// the specified unsigned int value. Bits will only be written to the stream when there
662 /// are enough to fill a byte.
663 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
664 if (bits == 0) return;
665
666 const U = @TypeOf(value);
667 comptime assert(trait.isUnsignedInt(U));
668
669 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
670 // related to shifting and casting.
671 const u_bit_count = comptime meta.bitCount(U);
672 const buf_bit_count = bc: {
673 assert(u_bit_count >= bits);
674 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
675 };
676 const Buf = std.meta.IntType(false, buf_bit_count);
677 const BufShift = math.Log2Int(Buf);
678
679 const buf_value = @intCast(Buf, value);
680
681 const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
682 var in_buffer = switch (endian) {
683 .Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
684 .Little => buf_value,
685 };
686 var in_bits = bits;
687
688 if (self.bit_count > 0) {
689 const bits_remaining = u8_bit_count - self.bit_count;
690 const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
691 switch (endian) {
692 .Big => {
693 const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
694 const v = @intCast(u8, in_buffer >> shift);
695 self.bit_buffer |= v;
696 in_buffer <<= n;
697 },
698 .Little => {
699 const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
700 self.bit_buffer |= v;
701 in_buffer >>= n;
702 },
703 }
704 self.bit_count += n;
705 in_bits -= n;
706
707 //if we didn't fill the buffer, it's because bits < bits_remaining;
708 if (self.bit_count != u8_bit_count) return;
709 try self.out_stream.writeByte(self.bit_buffer);
710 self.bit_buffer = 0;
711 self.bit_count = 0;
712 }
713 //at this point we know bit_buffer is empty
714
715 //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
716 while (in_bits >= u8_bit_count) {
717 switch (endian) {
718 .Big => {
719 const v = @intCast(u8, in_buffer >> high_byte_shift);
720 try self.out_stream.writeByte(v);
721 in_buffer <<= @intCast(u3, u8_bit_count - 1);
722 in_buffer <<= 1;
723 },
724 .Little => {
725 const v = @truncate(u8, in_buffer);
726 try self.out_stream.writeByte(v);
727 in_buffer >>= @intCast(u3, u8_bit_count - 1);
728 in_buffer >>= 1;
729 },
730 }
731 in_bits -= u8_bit_count;
732 }
733
734 if (in_bits > 0) {
735 self.bit_count = @intCast(u4, in_bits);
736 self.bit_buffer = switch (endian) {
737 .Big => @truncate(u8, in_buffer >> high_byte_shift),
738 .Little => @truncate(u8, in_buffer),
739 };
740 }
741 }
742
743 /// Flush any remaining bits to the stream.
744 pub fn flushBits(self: *Self) Error!void {
745 if (self.bit_count == 0) return;
746 try self.out_stream.writeByte(self.bit_buffer);
747 self.bit_buffer = 0;
748 self.bit_count = 0;
749 }
750
751 pub fn write(self_stream: *Stream, buffer: []const u8) Error!usize {
752 var self = @fieldParentPtr(Self, "stream", self_stream);
753
754 // TODO: I'm not sure this is a good idea, maybe flushBits should be forced
755 if (self.bit_count > 0) {
756 for (buffer) |b, i|
757 try self.writeBits(b, u8_bit_count);
758 return buffer.len;
759 }
760
761 return self.out_stream.writeOnce(buffer);
762 }
763 };
764}
765
766pub const BufferedAtomicFile = struct {
767 atomic_file: fs.AtomicFile,
768 file_stream: File.OutStream,
769 buffered_stream: BufferedOutStream(File.WriteError),
770 allocator: *mem.Allocator,
771
772 pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
773 // TODO with well defined copy elision we don't need this allocation
774 var self = try allocator.create(BufferedAtomicFile);
775 self.* = BufferedAtomicFile{
776 .atomic_file = undefined,
777 .file_stream = undefined,
778 .buffered_stream = undefined,
779 .allocator = allocator,
780 };
781 errdefer allocator.destroy(self);
782
783 self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode);
784 errdefer self.atomic_file.deinit();
785
786 self.file_stream = self.atomic_file.file.outStream();
787 self.buffered_stream = BufferedOutStream(File.WriteError).init(&self.file_stream.stream);
788 return self;
789 }
790
791 /// always call destroy, even after successful finish()
792 pub fn destroy(self: *BufferedAtomicFile) void {
793 self.atomic_file.deinit();
794 self.allocator.destroy(self);
795 }
796
797 pub fn finish(self: *BufferedAtomicFile) !void {
798 try self.buffered_stream.flush();
799 try self.atomic_file.finish();
800 }
801
802 pub fn stream(self: *BufferedAtomicFile) *OutStream(File.WriteError) {
803 return &self.buffered_stream.stream;
804 }
805};
806
807pub const Packing = enum {
808 /// Pack data to byte alignment
809 Byte,
810
811 /// Pack data to bit alignment
812 Bit,
813};
814
815/// Creates a deserializer that deserializes types from any stream.
816/// If `is_packed` is true, the data stream is treated as bit-packed,
817/// otherwise data is expected to be packed to the smallest byte.
818/// Types may implement a custom deserialization routine with a
819/// function named `deserialize` in the form of:
820/// pub fn deserialize(self: *Self, deserializer: var) !void
821/// which will be called when the deserializer is used to deserialize
822/// that type. It will pass a pointer to the type instance to deserialize
823/// into and a pointer to the deserializer struct.
824pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
825 return struct {
826 const Self = @This();
827
828 in_stream: if (packing == .Bit) BitInStream(endian, Stream.Error) else *Stream,
829
830 pub const Stream = InStream(Error);
831
832 pub fn init(in_stream: *Stream) Self {
833 return Self{
834 .in_stream = switch (packing) {
835 .Bit => BitInStream(endian, Stream.Error).init(in_stream),
836 .Byte => in_stream,
837 },
838 };
839 }
840
841 pub fn alignToByte(self: *Self) void {
842 if (packing == .Byte) return;
843 self.in_stream.alignToByte();
844 }
845
846 //@BUG: inferred error issue. See: #1386
847 fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
848 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
849
850 const u8_bit_count = 8;
851 const t_bit_count = comptime meta.bitCount(T);
852
853 const U = std.meta.IntType(false, t_bit_count);
854 const Log2U = math.Log2Int(U);
855 const int_size = (U.bit_count + 7) / 8;
856
857 if (packing == .Bit) {
858 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
859 return @bitCast(T, result);
860 }
861
862 var buffer: [int_size]u8 = undefined;
863 const read_size = try self.in_stream.read(buffer[0..]);
864 if (read_size < int_size) return error.EndOfStream;
865
866 if (int_size == 1) {
867 if (t_bit_count == 8) return @bitCast(T, buffer[0]);
868 const PossiblySignedByte = std.meta.IntType(T.is_signed, 8);
869 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
870 }
871
872 var result = @as(U, 0);
873 for (buffer) |byte, i| {
874 switch (endian) {
875 .Big => {
876 result = (result << u8_bit_count) | byte;
877 },
878 .Little => {
879 result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
880 },
881 }
882 }
883
884 return @bitCast(T, result);
885 }
886
887 /// Deserializes and returns data of the specified type from the stream
888 pub fn deserialize(self: *Self, comptime T: type) !T {
889 var value: T = undefined;
890 try self.deserializeInto(&value);
891 return value;
892 }
893
894 /// Deserializes data into the type pointed to by `ptr`
895 pub fn deserializeInto(self: *Self, ptr: var) !void {
896 const T = @TypeOf(ptr);
897 comptime assert(trait.is(.Pointer)(T));
898
899 if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
900 for (ptr) |*v|
901 try self.deserializeInto(v);
902 return;
903 }
904
905 comptime assert(trait.isSingleItemPtr(T));
906
907 const C = comptime meta.Child(T);
908 const child_type_id = @typeInfo(C);
909
910 //custom deserializer: fn(self: *Self, deserializer: var) !void
911 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
912
913 if (comptime trait.isPacked(C) and packing != .Bit) {
914 var packed_deserializer = Deserializer(endian, .Bit, Error).init(self.in_stream);
915 return packed_deserializer.deserializeInto(ptr);
916 }
917
918 switch (child_type_id) {
919 .Void => return,
920 .Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
921 .Float, .Int => ptr.* = try self.deserializeInt(C),
922 .Struct => {
923 const info = @typeInfo(C).Struct;
924
925 inline for (info.fields) |*field_info| {
926 const name = field_info.name;
927 const FieldType = field_info.field_type;
928
929 if (FieldType == void or FieldType == u0) continue;
930
931 //it doesn't make any sense to read pointers
932 if (comptime trait.is(.Pointer)(FieldType)) {
933 @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
934 @typeName(C) ++ " because it " ++ "is of pointer-type " ++
935 @typeName(FieldType) ++ ".");
936 }
937
938 try self.deserializeInto(&@field(ptr, name));
939 }
940 },
941 .Union => {
942 const info = @typeInfo(C).Union;
943 if (info.tag_type) |TagType| {
944 //we avoid duplicate iteration over the enum tags
945 // by getting the int directly and casting it without
946 // safety. If it is bad, it will be caught anyway.
947 const TagInt = @TagType(TagType);
948 const tag = try self.deserializeInt(TagInt);
949
950 inline for (info.fields) |field_info| {
951 if (field_info.enum_field.?.value == tag) {
952 const name = field_info.name;
953 const FieldType = field_info.field_type;
954 ptr.* = @unionInit(C, name, undefined);
955 try self.deserializeInto(&@field(ptr, name));
956 return;
957 }
958 }
959 //This is reachable if the enum data is bad
960 return error.InvalidEnumTag;
961 }
962 @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
963 " because it is an untagged union. Use a custom deserialize().");
964 },
965 .Optional => {
966 const OC = comptime meta.Child(C);
967 const exists = (try self.deserializeInt(u1)) > 0;
968 if (!exists) {
969 ptr.* = null;
970 return;
971 }
972
973 ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
974 const val_ptr = &ptr.*.?;
975 try self.deserializeInto(val_ptr);
976 },
977 .Enum => {
978 var value = try self.deserializeInt(@TagType(C));
979 ptr.* = try meta.intToEnum(C, value);
980 },
981 else => {
982 @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
983 },
984 }
985 }
986 };
987}151}
988152
989/// Creates a serializer that serializes types to any stream.153test "" {
990/// If `is_packed` is true, the data will be bit-packed into the stream.154 _ = @import("io/test.zig");
991/// Note that the you must call `serializer.flush()` when you are done
992/// writing bit-packed data in order ensure any unwritten bits are committed.
993/// If `is_packed` is false, data is packed to the smallest byte. In the case
994/// of packed structs, the struct will written bit-packed and with the specified
995/// endianess, after which data will resume being written at the next byte boundary.
996/// Types may implement a custom serialization routine with a
997/// function named `serialize` in the form of:
998/// pub fn serialize(self: Self, serializer: var) !void
999/// which will be called when the serializer is used to serialize that type. It will
1000/// pass a const pointer to the type instance to be serialized and a pointer
1001/// to the serializer struct.
1002pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
1003 return struct {
1004 const Self = @This();
1005
1006 out_stream: if (packing == .Bit) BitOutStream(endian, Stream.Error) else *Stream,
1007
1008 pub const Stream = OutStream(Error);
1009
1010 pub fn init(out_stream: *Stream) Self {
1011 return Self{
1012 .out_stream = switch (packing) {
1013 .Bit => BitOutStream(endian, Stream.Error).init(out_stream),
1014 .Byte => out_stream,
1015 },
1016 };
1017 }
1018
1019 /// Flushes any unwritten bits to the stream
1020 pub fn flush(self: *Self) Error!void {
1021 if (packing == .Bit) return self.out_stream.flushBits();
1022 }
1023
1024 fn serializeInt(self: *Self, value: var) Error!void {
1025 const T = @TypeOf(value);
1026 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
1027
1028 const t_bit_count = comptime meta.bitCount(T);
1029 const u8_bit_count = comptime meta.bitCount(u8);
1030
1031 const U = std.meta.IntType(false, t_bit_count);
1032 const Log2U = math.Log2Int(U);
1033 const int_size = (U.bit_count + 7) / 8;
1034
1035 const u_value = @bitCast(U, value);
1036
1037 if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
1038
1039 var buffer: [int_size]u8 = undefined;
1040 if (int_size == 1) buffer[0] = u_value;
1041
1042 for (buffer) |*byte, i| {
1043 const idx = switch (endian) {
1044 .Big => int_size - i - 1,
1045 .Little => i,
1046 };
1047 const shift = @intCast(Log2U, idx * u8_bit_count);
1048 const v = u_value >> shift;
1049 byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
1050 }
1051
1052 try self.out_stream.write(&buffer);
1053 }
1054
1055 /// Serializes the passed value into the stream
1056 pub fn serialize(self: *Self, value: var) Error!void {
1057 const T = comptime @TypeOf(value);
1058
1059 if (comptime trait.isIndexable(T)) {
1060 for (value) |v|
1061 try self.serialize(v);
1062 return;
1063 }
1064
1065 //custom serializer: fn(self: Self, serializer: var) !void
1066 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
1067
1068 if (comptime trait.isPacked(T) and packing != .Bit) {
1069 var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
1070 try packed_serializer.serialize(value);
1071 try packed_serializer.flush();
1072 return;
1073 }
1074
1075 switch (@typeInfo(T)) {
1076 .Void => return,
1077 .Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
1078 .Float, .Int => try self.serializeInt(value),
1079 .Struct => {
1080 const info = @typeInfo(T);
1081
1082 inline for (info.Struct.fields) |*field_info| {
1083 const name = field_info.name;
1084 const FieldType = field_info.field_type;
1085
1086 if (FieldType == void or FieldType == u0) continue;
1087
1088 //It doesn't make sense to write pointers
1089 if (comptime trait.is(.Pointer)(FieldType)) {
1090 @compileError("Will not " ++ "serialize field " ++ name ++
1091 " of struct " ++ @typeName(T) ++ " because it " ++
1092 "is of pointer-type " ++ @typeName(FieldType) ++ ".");
1093 }
1094 try self.serialize(@field(value, name));
1095 }
1096 },
1097 .Union => {
1098 const info = @typeInfo(T).Union;
1099 if (info.tag_type) |TagType| {
1100 const active_tag = meta.activeTag(value);
1101 try self.serialize(active_tag);
1102 //This inline loop is necessary because active_tag is a runtime
1103 // value, but @field requires a comptime value. Our alternative
1104 // is to check each field for a match
1105 inline for (info.fields) |field_info| {
1106 if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
1107 const name = field_info.name;
1108 const FieldType = field_info.field_type;
1109 try self.serialize(@field(value, name));
1110 return;
1111 }
1112 }
1113 unreachable;
1114 }
1115 @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
1116 " because it is an untagged union. Use a custom serialize().");
1117 },
1118 .Optional => {
1119 if (value == null) {
1120 try self.serializeInt(@as(u1, @boolToInt(false)));
1121 return;
1122 }
1123 try self.serializeInt(@as(u1, @boolToInt(true)));
1124
1125 const OC = comptime meta.Child(T);
1126 const val_ptr = &value.?;
1127 try self.serialize(val_ptr.*);
1128 },
1129 .Enum => {
1130 try self.serializeInt(@enumToInt(value));
1131 },
1132 else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
1133 }
1134 }
1135 };
1136}
1137
1138test "import io tests" {
1139 comptime {
1140 _ = @import("io/test.zig");
1141 }
1142}155}
lib/std/io/bit_in_stream.zig created+243
...@@ -0,0 +1,243 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const assert = std.debug.assert;
5const testing = std.testing;
6const trait = std.meta.trait;
7const meta = std.meta;
8const math = std.math;
9
10/// Creates a stream which allows for reading bit fields from another stream
11pub fn BitInStream(endian: builtin.Endian, comptime InStreamType: type) type {
12 return struct {
13 in_stream: InStreamType,
14 bit_buffer: u7,
15 bit_count: u3,
16
17 pub const Error = InStreamType.Error;
18 pub const InStream = io.InStream(*Self, Error, read);
19
20 const Self = @This();
21 const u8_bit_count = comptime meta.bitCount(u8);
22 const u7_bit_count = comptime meta.bitCount(u7);
23 const u4_bit_count = comptime meta.bitCount(u4);
24
25 pub fn init(in_stream: InStreamType) Self {
26 return Self{
27 .in_stream = in_stream,
28 .bit_buffer = 0,
29 .bit_count = 0,
30 };
31 }
32
33 /// Reads `bits` bits from the stream and returns a specified unsigned int type
34 /// containing them in the least significant end, returning an error if the
35 /// specified number of bits could not be read.
36 pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
37 var n: usize = undefined;
38 const result = try self.readBits(U, bits, &n);
39 if (n < bits) return error.EndOfStream;
40 return result;
41 }
42
43 /// Reads `bits` bits from the stream and returns a specified unsigned int type
44 /// containing them in the least significant end. The number of bits successfully
45 /// read is placed in `out_bits`, as reaching the end of the stream is not an error.
46 pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U {
47 comptime assert(trait.isUnsignedInt(U));
48
49 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
50 // related to shifting and casting.
51 const u_bit_count = comptime meta.bitCount(U);
52 const buf_bit_count = bc: {
53 assert(u_bit_count >= bits);
54 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
55 };
56 const Buf = std.meta.IntType(false, buf_bit_count);
57 const BufShift = math.Log2Int(Buf);
58
59 out_bits.* = @as(usize, 0);
60 if (U == u0 or bits == 0) return 0;
61 var out_buffer = @as(Buf, 0);
62
63 if (self.bit_count > 0) {
64 const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
65 const shift = u7_bit_count - n;
66 switch (endian) {
67 .Big => {
68 out_buffer = @as(Buf, self.bit_buffer >> shift);
69 if (n >= u7_bit_count)
70 self.bit_buffer = 0
71 else
72 self.bit_buffer <<= n;
73 },
74 .Little => {
75 const value = (self.bit_buffer << shift) >> shift;
76 out_buffer = @as(Buf, value);
77 if (n >= u7_bit_count)
78 self.bit_buffer = 0
79 else
80 self.bit_buffer >>= n;
81 },
82 }
83 self.bit_count -= n;
84 out_bits.* = n;
85 }
86 //at this point we know bit_buffer is empty
87
88 //copy bytes until we have enough bits, then leave the rest in bit_buffer
89 while (out_bits.* < bits) {
90 const n = bits - out_bits.*;
91 const next_byte = self.in_stream.readByte() catch |err| {
92 if (err == error.EndOfStream) {
93 return @intCast(U, out_buffer);
94 }
95 //@BUG: See #1810. Not sure if the bug is that I have to do this for some
96 // streams, or that I don't for streams with emtpy errorsets.
97 return @errSetCast(Error, err);
98 };
99
100 switch (endian) {
101 .Big => {
102 if (n >= u8_bit_count) {
103 out_buffer <<= @intCast(u3, u8_bit_count - 1);
104 out_buffer <<= 1;
105 out_buffer |= @as(Buf, next_byte);
106 out_bits.* += u8_bit_count;
107 continue;
108 }
109
110 const shift = @intCast(u3, u8_bit_count - n);
111 out_buffer <<= @intCast(BufShift, n);
112 out_buffer |= @as(Buf, next_byte >> shift);
113 out_bits.* += n;
114 self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
115 self.bit_count = shift;
116 },
117 .Little => {
118 if (n >= u8_bit_count) {
119 out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*);
120 out_bits.* += u8_bit_count;
121 continue;
122 }
123
124 const shift = @intCast(u3, u8_bit_count - n);
125 const value = (next_byte << shift) >> shift;
126 out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*);
127 out_bits.* += n;
128 self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n));
129 self.bit_count = shift;
130 },
131 }
132 }
133
134 return @intCast(U, out_buffer);
135 }
136
137 pub fn alignToByte(self: *Self) void {
138 self.bit_buffer = 0;
139 self.bit_count = 0;
140 }
141
142 pub fn read(self: *Self, buffer: []u8) Error!usize {
143 var out_bits: usize = undefined;
144 var out_bits_total = @as(usize, 0);
145 //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced
146 if (self.bit_count > 0) {
147 for (buffer) |*b, i| {
148 b.* = try self.readBits(u8, u8_bit_count, &out_bits);
149 out_bits_total += out_bits;
150 }
151 const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0);
152 return (out_bits_total / u8_bit_count) + incomplete_byte;
153 }
154
155 return self.in_stream.read(buffer);
156 }
157
158 pub fn inStream(self: *Self) InStream {
159 return .{ .context = self };
160 }
161 };
162}
163
164pub fn bitInStream(
165 comptime endian: builtin.Endian,
166 underlying_stream: var,
167) BitInStream(endian, @TypeOf(underlying_stream)) {
168 return BitInStream(endian, @TypeOf(underlying_stream)).init(underlying_stream);
169}
170
171test "api coverage" {
172 const mem_be = [_]u8{ 0b11001101, 0b00001011 };
173 const mem_le = [_]u8{ 0b00011101, 0b10010101 };
174
175 var mem_in_be = io.fixedBufferStream(&mem_be);
176 var bit_stream_be = bitInStream(.Big, mem_in_be.inStream());
177
178 var out_bits: usize = undefined;
179
180 const expect = testing.expect;
181 const expectError = testing.expectError;
182
183 expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
184 expect(out_bits == 1);
185 expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
186 expect(out_bits == 2);
187 expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
188 expect(out_bits == 3);
189 expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
190 expect(out_bits == 4);
191 expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
192 expect(out_bits == 5);
193 expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
194 expect(out_bits == 1);
195
196 mem_in_be.pos = 0;
197 bit_stream_be.bit_count = 0;
198 expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
199 expect(out_bits == 15);
200
201 mem_in_be.pos = 0;
202 bit_stream_be.bit_count = 0;
203 expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
204 expect(out_bits == 16);
205
206 _ = try bit_stream_be.readBits(u0, 0, &out_bits);
207
208 expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
209 expect(out_bits == 0);
210 expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1));
211
212 var mem_in_le = io.fixedBufferStream(&mem_le);
213 var bit_stream_le = bitInStream(.Little, mem_in_le.inStream());
214
215 expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
216 expect(out_bits == 1);
217 expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
218 expect(out_bits == 2);
219 expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
220 expect(out_bits == 3);
221 expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
222 expect(out_bits == 4);
223 expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
224 expect(out_bits == 5);
225 expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
226 expect(out_bits == 1);
227
228 mem_in_le.pos = 0;
229 bit_stream_le.bit_count = 0;
230 expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
231 expect(out_bits == 15);
232
233 mem_in_le.pos = 0;
234 bit_stream_le.bit_count = 0;
235 expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
236 expect(out_bits == 16);
237
238 _ = try bit_stream_le.readBits(u0, 0, &out_bits);
239
240 expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
241 expect(out_bits == 0);
242 expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1));
243}
lib/std/io/bit_out_stream.zig created+197
...@@ -0,0 +1,197 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const testing = std.testing;
5const assert = std.debug.assert;
6const trait = std.meta.trait;
7const meta = std.meta;
8const math = std.math;
9
10/// Creates a stream which allows for writing bit fields to another stream
11pub fn BitOutStream(endian: builtin.Endian, comptime OutStreamType: type) type {
12 return struct {
13 out_stream: OutStreamType,
14 bit_buffer: u8,
15 bit_count: u4,
16
17 pub const Error = OutStreamType.Error;
18 pub const OutStream = io.OutStream(*Self, Error, write);
19
20 const Self = @This();
21 const u8_bit_count = comptime meta.bitCount(u8);
22 const u4_bit_count = comptime meta.bitCount(u4);
23
24 pub fn init(out_stream: OutStreamType) Self {
25 return Self{
26 .out_stream = out_stream,
27 .bit_buffer = 0,
28 .bit_count = 0,
29 };
30 }
31
32 /// Write the specified number of bits to the stream from the least significant bits of
33 /// the specified unsigned int value. Bits will only be written to the stream when there
34 /// are enough to fill a byte.
35 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
36 if (bits == 0) return;
37
38 const U = @TypeOf(value);
39 comptime assert(trait.isUnsignedInt(U));
40
41 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
42 // related to shifting and casting.
43 const u_bit_count = comptime meta.bitCount(U);
44 const buf_bit_count = bc: {
45 assert(u_bit_count >= bits);
46 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
47 };
48 const Buf = std.meta.IntType(false, buf_bit_count);
49 const BufShift = math.Log2Int(Buf);
50
51 const buf_value = @intCast(Buf, value);
52
53 const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
54 var in_buffer = switch (endian) {
55 .Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
56 .Little => buf_value,
57 };
58 var in_bits = bits;
59
60 if (self.bit_count > 0) {
61 const bits_remaining = u8_bit_count - self.bit_count;
62 const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
63 switch (endian) {
64 .Big => {
65 const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
66 const v = @intCast(u8, in_buffer >> shift);
67 self.bit_buffer |= v;
68 in_buffer <<= n;
69 },
70 .Little => {
71 const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
72 self.bit_buffer |= v;
73 in_buffer >>= n;
74 },
75 }
76 self.bit_count += n;
77 in_bits -= n;
78
79 //if we didn't fill the buffer, it's because bits < bits_remaining;
80 if (self.bit_count != u8_bit_count) return;
81 try self.out_stream.writeByte(self.bit_buffer);
82 self.bit_buffer = 0;
83 self.bit_count = 0;
84 }
85 //at this point we know bit_buffer is empty
86
87 //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
88 while (in_bits >= u8_bit_count) {
89 switch (endian) {
90 .Big => {
91 const v = @intCast(u8, in_buffer >> high_byte_shift);
92 try self.out_stream.writeByte(v);
93 in_buffer <<= @intCast(u3, u8_bit_count - 1);
94 in_buffer <<= 1;
95 },
96 .Little => {
97 const v = @truncate(u8, in_buffer);
98 try self.out_stream.writeByte(v);
99 in_buffer >>= @intCast(u3, u8_bit_count - 1);
100 in_buffer >>= 1;
101 },
102 }
103 in_bits -= u8_bit_count;
104 }
105
106 if (in_bits > 0) {
107 self.bit_count = @intCast(u4, in_bits);
108 self.bit_buffer = switch (endian) {
109 .Big => @truncate(u8, in_buffer >> high_byte_shift),
110 .Little => @truncate(u8, in_buffer),
111 };
112 }
113 }
114
115 /// Flush any remaining bits to the stream.
116 pub fn flushBits(self: *Self) Error!void {
117 if (self.bit_count == 0) return;
118 try self.out_stream.writeByte(self.bit_buffer);
119 self.bit_buffer = 0;
120 self.bit_count = 0;
121 }
122
123 pub fn write(self: *Self, buffer: []const u8) Error!usize {
124 // TODO: I'm not sure this is a good idea, maybe flushBits should be forced
125 if (self.bit_count > 0) {
126 for (buffer) |b, i|
127 try self.writeBits(b, u8_bit_count);
128 return buffer.len;
129 }
130
131 return self.out_stream.write(buffer);
132 }
133
134 pub fn outStream(self: *Self) OutStream {
135 return .{ .context = self };
136 }
137 };
138}
139
140pub fn bitOutStream(
141 comptime endian: builtin.Endian,
142 underlying_stream: var,
143) BitOutStream(endian, @TypeOf(underlying_stream)) {
144 return BitOutStream(endian, @TypeOf(underlying_stream)).init(underlying_stream);
145}
146
147test "api coverage" {
148 var mem_be = [_]u8{0} ** 2;
149 var mem_le = [_]u8{0} ** 2;
150
151 var mem_out_be = io.fixedBufferStream(&mem_be);
152 var bit_stream_be = bitOutStream(.Big, mem_out_be.outStream());
153
154 try bit_stream_be.writeBits(@as(u2, 1), 1);
155 try bit_stream_be.writeBits(@as(u5, 2), 2);
156 try bit_stream_be.writeBits(@as(u128, 3), 3);
157 try bit_stream_be.writeBits(@as(u8, 4), 4);
158 try bit_stream_be.writeBits(@as(u9, 5), 5);
159 try bit_stream_be.writeBits(@as(u1, 1), 1);
160
161 testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
162
163 mem_out_be.pos = 0;
164
165 try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15);
166 try bit_stream_be.flushBits();
167 testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
168
169 mem_out_be.pos = 0;
170 try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16);
171 testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
172
173 try bit_stream_be.writeBits(@as(u0, 0), 0);
174
175 var mem_out_le = io.fixedBufferStream(&mem_le);
176 var bit_stream_le = bitOutStream(.Little, mem_out_le.outStream());
177
178 try bit_stream_le.writeBits(@as(u2, 1), 1);
179 try bit_stream_le.writeBits(@as(u5, 2), 2);
180 try bit_stream_le.writeBits(@as(u128, 3), 3);
181 try bit_stream_le.writeBits(@as(u8, 4), 4);
182 try bit_stream_le.writeBits(@as(u9, 5), 5);
183 try bit_stream_le.writeBits(@as(u1, 1), 1);
184
185 testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
186
187 mem_out_le.pos = 0;
188 try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15);
189 try bit_stream_le.flushBits();
190 testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
191
192 mem_out_le.pos = 0;
193 try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16);
194 testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
195
196 try bit_stream_le.writeBits(@as(u0, 0), 0);
197}
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.BufferedOutStream(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 = .{ .unbuffered_out_stream = 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_in_stream.zig created+86
...@@ -0,0 +1,86 @@
1const std = @import("../std.zig");
2const io = std.io;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType: type) type {
7 return struct {
8 unbuffered_in_stream: InStreamType,
9 fifo: FifoType = FifoType.init(),
10
11 pub const Error = InStreamType.Error;
12 pub const InStream = io.InStream(*Self, Error, read);
13
14 const Self = @This();
15 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
16
17 pub fn read(self: *Self, dest: []u8) Error!usize {
18 var dest_index: usize = 0;
19 while (dest_index < dest.len) {
20 const written = self.fifo.read(dest[dest_index..]);
21 if (written == 0) {
22 // fifo empty, fill it
23 const writable = self.fifo.writableSlice(0);
24 assert(writable.len > 0);
25 const n = try self.unbuffered_in_stream.read(writable);
26 if (n == 0) {
27 // reading from the unbuffered stream returned nothing
28 // so we have nothing left to read.
29 return dest_index;
30 }
31 self.fifo.update(n);
32 }
33 dest_index += written;
34 }
35 return dest.len;
36 }
37
38 pub fn inStream(self: *Self) InStream {
39 return .{ .context = self };
40 }
41 };
42}
43
44pub fn bufferedInStream(underlying_stream: var) BufferedInStream(4096, @TypeOf(underlying_stream)) {
45 return .{ .unbuffered_in_stream = underlying_stream };
46}
47
48test "io.BufferedInStream" {
49 const OneByteReadInStream = struct {
50 str: []const u8,
51 curr: usize,
52
53 const Error = error{NoError};
54 const Self = @This();
55 const InStream = io.InStream(*Self, Error, read);
56
57 fn init(str: []const u8) Self {
58 return Self{
59 .str = str,
60 .curr = 0,
61 };
62 }
63
64 fn read(self: *Self, dest: []u8) Error!usize {
65 if (self.str.len <= self.curr or dest.len == 0)
66 return 0;
67
68 dest[0] = self.str[self.curr];
69 self.curr += 1;
70 return 1;
71 }
72
73 fn inStream(self: *Self) InStream {
74 return .{ .context = self };
75 }
76 };
77
78 const str = "This is a test";
79 var one_byte_stream = OneByteReadInStream.init(str);
80 var buf_in_stream = bufferedInStream(one_byte_stream.inStream());
81 const stream = buf_in_stream.inStream();
82
83 const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
84 defer testing.allocator.free(res);
85 testing.expectEqualSlices(u8, str, res);
86}
lib/std/io/buffered_out_stream.zig created+41
...@@ -0,0 +1,41 @@
1const std = @import("../std.zig");
2const io = std.io;
3
4pub fn BufferedOutStream(comptime buffer_size: usize, comptime OutStreamType: type) type {
5 return struct {
6 unbuffered_out_stream: OutStreamType,
7 fifo: FifoType = FifoType.init(),
8
9 pub const Error = OutStreamType.Error;
10 pub const OutStream = io.OutStream(*Self, Error, write);
11
12 const Self = @This();
13 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
14
15 pub fn flush(self: *Self) !void {
16 while (true) {
17 const slice = self.fifo.readableSlice(0);
18 if (slice.len == 0) break;
19 try self.unbuffered_out_stream.writeAll(slice);
20 self.fifo.discard(slice.len);
21 }
22 }
23
24 pub fn outStream(self: *Self) OutStream {
25 return .{ .context = self };
26 }
27
28 pub fn write(self: *Self, bytes: []const u8) Error!usize {
29 if (bytes.len >= self.fifo.writableLength()) {
30 try self.flush();
31 return self.unbuffered_out_stream.write(bytes);
32 }
33 self.fifo.writeAssumeCapacity(bytes);
34 return bytes.len;
35 }
36 };
37}
38
39pub fn bufferedOutStream(underlying_stream: var) BufferedOutStream(4096, @TypeOf(underlying_stream)) {
40 return .{ .unbuffered_out_stream = underlying_stream };
41}
lib/std/io/c_out_stream.zig+37-36
...@@ -1,43 +1,44 @@...@@ -1,43 +1,44 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const os = std.os;2const builtin = std.builtin;
3const OutStream = std.io.OutStream;3const io = std.io;
4const builtin = @import("builtin");4const testing = std.testing;
55
6/// TODO make a proposal to make `std.fs.File` use *FILE when linking libc and this just becomes6pub const COutStream = io.OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite);
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);
127
13 stream: Stream,8pub fn cOutStream(c_file: *std.c.FILE) COutStream {
14 c_file: *std.c.FILE,9 return .{ .context = c_file };
10}
1511
16 pub fn init(c_file: *std.c.FILE) COutStream {12fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
17 return COutStream{13 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
18 .c_file = c_file,14 if (amt_written >= 0) return amt_written;
19 .stream = Stream{ .writeFn = writeFn },15 switch (std.c._errno().*) {
20 };16 0 => unreachable,
17 os.EINVAL => unreachable,
18 os.EFAULT => unreachable,
19 os.EAGAIN => unreachable, // this is a blocking API
20 os.EBADF => unreachable, // always a race condition
21 os.EDESTADDRREQ => unreachable, // connect was never called
22 os.EDQUOT => return error.DiskQuota,
23 os.EFBIG => return error.FileTooBig,
24 os.EIO => return error.InputOutput,
25 os.ENOSPC => return error.NoSpaceLeft,
26 os.EPERM => return error.AccessDenied,
27 os.EPIPE => return error.BrokenPipe,
28 else => |err| return os.unexpectedErrno(@intCast(usize, err)),
21 }29 }
30}
2231
23 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {32test "" {
24 const self = @fieldParentPtr(COutStream, "stream", out_stream);33 if (!builtin.link_libc) return error.SkipZigTest;
25 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file);34
26 if (amt_written >= 0) return amt_written;35 const filename = "tmp_io_test_file.txt";
27 switch (std.c._errno().*) {36 const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
28 0 => unreachable,37 defer {
29 os.EINVAL => unreachable,38 _ = std.c.fclose(out_file);
30 os.EFAULT => unreachable,39 fs.cwd().deleteFileC(filename) catch {};
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 }40 }
43};41
42 const out_stream = &io.COutStream.init(out_file).stream;
43 try out_stream.print("hi: {}\n", .{@as(i32, 123)});
44}
lib/std/io/counting_out_stream.zig created+39
...@@ -0,0 +1,39 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4
5/// An OutStream that counts how many bytes has been written to it.
6pub fn CountingOutStream(comptime OutStreamType: type) type {
7 return struct {
8 bytes_written: u64,
9 child_stream: OutStreamType,
10
11 pub const Error = OutStreamType.Error;
12 pub const OutStream = io.OutStream(*Self, Error, write);
13
14 const Self = @This();
15
16 pub fn write(self: *Self, bytes: []const u8) Error!usize {
17 const amt = try self.child_stream.write(bytes);
18 self.bytes_written += amt;
19 return amt;
20 }
21
22 pub fn outStream(self: *Self) OutStream {
23 return .{ .context = self };
24 }
25 };
26}
27
28pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) {
29 return .{ .bytes_written = 0, .child_stream = child_stream };
30}
31
32test "io.CountingOutStream" {
33 var counting_stream = countingOutStream(std.io.null_out_stream);
34 const stream = counting_stream.outStream();
35
36 const bytes = "yay" ** 100;
37 stream.writeAll(bytes) catch unreachable;
38 testing.expect(counting_stream.bytes_written == bytes.len);
39}
lib/std/io/fixed_buffer_stream.zig created+171
...@@ -0,0 +1,171 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
6
7/// This turns a byte buffer into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`.
8/// If the supplied byte buffer is const, then `io.OutStream` is not available.
9pub fn FixedBufferStream(comptime Buffer: type) type {
10 return struct {
11 /// `Buffer` is either a `[]u8` or `[]const u8`.
12 buffer: Buffer,
13 pos: usize,
14
15 pub const ReadError = error{};
16 pub const WriteError = error{NoSpaceLeft};
17 pub const SeekError = error{};
18 pub const GetSeekPosError = error{};
19
20 pub const InStream = io.InStream(*Self, ReadError, read);
21 pub const OutStream = io.OutStream(*Self, WriteError, write);
22
23 pub const SeekableStream = io.SeekableStream(
24 *Self,
25 SeekError,
26 GetSeekPosError,
27 seekTo,
28 seekBy,
29 getPos,
30 getEndPos,
31 );
32
33 const Self = @This();
34
35 pub fn inStream(self: *Self) InStream {
36 return .{ .context = self };
37 }
38
39 pub fn outStream(self: *Self) OutStream {
40 return .{ .context = self };
41 }
42
43 pub fn seekableStream(self: *Self) SeekableStream {
44 return .{ .context = self };
45 }
46
47 pub fn read(self: *Self, dest: []u8) ReadError!usize {
48 const size = std.math.min(dest.len, self.buffer.len - self.pos);
49 const end = self.pos + size;
50
51 mem.copy(u8, dest[0..size], self.buffer[self.pos..end]);
52 self.pos = end;
53
54 return size;
55 }
56
57 /// If the returned number of bytes written is less than requested, the
58 /// buffer is full. Returns `error.NoSpaceLeft` when no bytes would be written.
59 /// Note: `error.NoSpaceLeft` matches the corresponding error from
60 /// `std.fs.File.WriteError`.
61 pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
62 if (bytes.len == 0) return 0;
63 if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
64
65 const n = if (self.pos + bytes.len <= self.buffer.len)
66 bytes.len
67 else
68 self.buffer.len - self.pos;
69
70 mem.copy(u8, self.buffer[self.pos .. self.pos + n], bytes[0..n]);
71 self.pos += n;
72
73 if (n == 0) return error.NoSpaceLeft;
74
75 return n;
76 }
77
78 pub fn seekTo(self: *Self, pos: u64) SeekError!void {
79 self.pos = if (std.math.cast(usize, pos)) |x| x else |_| self.buffer.len;
80 }
81
82 pub fn seekBy(self: *Self, amt: i64) SeekError!void {
83 if (amt < 0) {
84 const abs_amt = std.math.absCast(amt);
85 const abs_amt_usize = std.math.cast(usize, abs_amt) catch std.math.maxInt(usize);
86 if (abs_amt_usize > self.pos) {
87 self.pos = 0;
88 } else {
89 self.pos -= abs_amt_usize;
90 }
91 } else {
92 const amt_usize = std.math.cast(usize, amt) catch std.math.maxInt(usize);
93 const new_pos = std.math.add(usize, self.pos, amt_usize) catch std.math.maxInt(usize);
94 self.pos = std.math.min(self.buffer.len, new_pos);
95 }
96 }
97
98 pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
99 return self.buffer.len;
100 }
101
102 pub fn getPos(self: *Self) GetSeekPosError!u64 {
103 return self.pos;
104 }
105
106 pub fn getWritten(self: Self) []const u8 {
107 return self.buffer[0..self.pos];
108 }
109
110 pub fn reset(self: *Self) void {
111 self.pos = 0;
112 }
113 };
114}
115
116pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
117 return .{ .buffer = mem.span(buffer), .pos = 0 };
118}
119
120fn NonSentinelSpan(comptime T: type) type {
121 var ptr_info = @typeInfo(mem.Span(T)).Pointer;
122 ptr_info.sentinel = null;
123 return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info });
124}
125
126test "FixedBufferStream output" {
127 var buf: [255]u8 = undefined;
128 var fbs = fixedBufferStream(&buf);
129 const stream = fbs.outStream();
130
131 try stream.print("{}{}!", .{ "Hello", "World" });
132 testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
133}
134
135test "FixedBufferStream output 2" {
136 var buffer: [10]u8 = undefined;
137 var fbs = fixedBufferStream(&buffer);
138
139 try fbs.outStream().writeAll("Hello");
140 testing.expect(mem.eql(u8, fbs.getWritten(), "Hello"));
141
142 try fbs.outStream().writeAll("world");
143 testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
144
145 testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("!"));
146 testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
147
148 fbs.reset();
149 testing.expect(fbs.getWritten().len == 0);
150
151 testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("Hello world!"));
152 testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl"));
153}
154
155test "FixedBufferStream input" {
156 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
157 var fbs = fixedBufferStream(&bytes);
158
159 var dest: [4]u8 = undefined;
160
161 var read = try fbs.inStream().read(dest[0..4]);
162 testing.expect(read == 4);
163 testing.expect(mem.eql(u8, dest[0..4], bytes[0..4]));
164
165 read = try fbs.inStream().read(dest[0..4]);
166 testing.expect(read == 3);
167 testing.expect(mem.eql(u8, dest[0..3], bytes[4..7]));
168
169 read = try fbs.inStream().read(dest[0..4]);
170 testing.expect(read == 0);
171}
lib/std/io/in_stream.zig+36-53
...@@ -1,53 +1,37 @@...@@ -1,53 +1,37 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = std.builtin;
3const root = @import("root");
4const math = std.math;3const math = std.math;
5const assert = std.debug.assert;4const assert = std.debug.assert;
6const mem = std.mem;5const mem = std.mem;
7const Buffer = std.Buffer;6const Buffer = std.Buffer;
8const testing = std.testing;7const testing = std.testing;
98
10pub const default_stack_size = 1 * 1024 * 1024;9pub fn InStream(
11pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))10 comptime Context: type,
12 root.stack_size_std_io_InStream11 comptime ReadError: type,
13else12 /// Returns the number of bytes read. It may be less than buffer.len.
14 default_stack_size;13 /// If the number of bytes read is 0, it means end of stream.
1514 /// End of stream is not an error condition.
16pub fn InStream(comptime ReadError: type) type {15 comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
16) type {
17 return struct {17 return struct {
18 const Self = @This();
19 pub const Error = ReadError;18 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.20 context: Context,
26 /// If the number of bytes read is 0, it means end of stream.21
27 /// End of stream is not an error condition.22 const Self = @This();
28 readFn: ReadFn,
2923
30 /// Returns the number of bytes read. It may be less than buffer.len.24 /// Returns the number of bytes read. It may be less than buffer.len.
31 /// If the number of bytes read is 0, it means end of stream.25 /// If the number of bytes read is 0, it means end of stream.
32 /// End of stream is not an error condition.26 /// End of stream is not an error condition.
33 pub fn read(self: *Self, buffer: []u8) Error!usize {27 pub fn read(self: Self, buffer: []u8) Error!usize {
34 if (std.io.is_async) {28 return readFn(self.context, buffer);
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 }
42 }29 }
4330
44 /// Deprecated: use `readAll`.31 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
45 pub const readFull = readAll;
46
47 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
48 /// means the stream reached the end. Reaching the end of a stream is not an error32 /// means the stream reached the end. Reaching the end of a stream is not an error
49 /// condition.33 /// condition.
50 pub fn readAll(self: *Self, buffer: []u8) Error!usize {34 pub fn readAll(self: Self, buffer: []u8) Error!usize {
51 var index: usize = 0;35 var index: usize = 0;
52 while (index != buffer.len) {36 while (index != buffer.len) {
53 const amt = try self.read(buffer[index..]);37 const amt = try self.read(buffer[index..]);
...@@ -59,13 +43,13 @@ pub fn InStream(comptime ReadError: type) type {...@@ -59,13 +43,13 @@ pub fn InStream(comptime ReadError: type) type {
5943
60 /// Returns the number of bytes read. If the number read would be smaller than buf.len,44 /// Returns the number of bytes read. If the number read would be smaller than buf.len,
61 /// error.EndOfStream is returned instead.45 /// error.EndOfStream is returned instead.
62 pub fn readNoEof(self: *Self, buf: []u8) !void {46 pub fn readNoEof(self: Self, buf: []u8) !void {
63 const amt_read = try self.readAll(buf);47 const amt_read = try self.readAll(buf);
64 if (amt_read < buf.len) return error.EndOfStream;48 if (amt_read < buf.len) return error.EndOfStream;
65 }49 }
6650
67 /// Deprecated: use `readAllArrayList`.51 /// Deprecated: use `readAllArrayList`.
68 pub fn readAllBuffer(self: *Self, buffer: *Buffer, max_size: usize) !void {52 pub fn readAllBuffer(self: Self, buffer: *Buffer, max_size: usize) !void {
69 buffer.list.shrink(0);53 buffer.list.shrink(0);
70 try self.readAllArrayList(&buffer.list, max_size);54 try self.readAllArrayList(&buffer.list, max_size);
71 errdefer buffer.shrink(0);55 errdefer buffer.shrink(0);
...@@ -75,7 +59,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -75,7 +59,7 @@ pub fn InStream(comptime ReadError: type) type {
75 /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found.59 /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found.
76 /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned60 /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned
77 /// and the `std.ArrayList` has exactly `max_append_size` bytes appended.61 /// 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 {62 pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void {
79 try array_list.ensureCapacity(math.min(max_append_size, 4096));63 try array_list.ensureCapacity(math.min(max_append_size, 4096));
80 const original_len = array_list.len;64 const original_len = array_list.len;
81 var start_index: usize = original_len;65 var start_index: usize = original_len;
...@@ -104,7 +88,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -104,7 +88,7 @@ pub fn InStream(comptime ReadError: type) type {
104 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.88 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
105 /// Caller owns returned memory.89 /// Caller owns returned memory.
106 /// If this function returns an error, the contents from the stream read so far are lost.90 /// 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 {91 pub fn readAllAlloc(self: Self, allocator: *mem.Allocator, max_size: usize) ![]u8 {
108 var array_list = std.ArrayList(u8).init(allocator);92 var array_list = std.ArrayList(u8).init(allocator);
109 defer array_list.deinit();93 defer array_list.deinit();
110 try self.readAllArrayList(&array_list, max_size);94 try self.readAllArrayList(&array_list, max_size);
...@@ -116,7 +100,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -116,7 +100,7 @@ pub fn InStream(comptime ReadError: type) type {
116 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the100 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
117 /// `std.ArrayList` is populated with `max_size` bytes from the stream.101 /// `std.ArrayList` is populated with `max_size` bytes from the stream.
118 pub fn readUntilDelimiterArrayList(102 pub fn readUntilDelimiterArrayList(
119 self: *Self,103 self: Self,
120 array_list: *std.ArrayList(u8),104 array_list: *std.ArrayList(u8),
121 delimiter: u8,105 delimiter: u8,
122 max_size: usize,106 max_size: usize,
...@@ -142,7 +126,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -142,7 +126,7 @@ pub fn InStream(comptime ReadError: type) type {
142 /// Caller owns returned memory.126 /// Caller owns returned memory.
143 /// If this function returns an error, the contents from the stream read so far are lost.127 /// If this function returns an error, the contents from the stream read so far are lost.
144 pub fn readUntilDelimiterAlloc(128 pub fn readUntilDelimiterAlloc(
145 self: *Self,129 self: Self,
146 allocator: *mem.Allocator,130 allocator: *mem.Allocator,
147 delimiter: u8,131 delimiter: u8,
148 max_size: usize,132 max_size: usize,
...@@ -159,7 +143,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -159,7 +143,7 @@ pub fn InStream(comptime ReadError: type) type {
159 /// function is called again after that, returns null.143 /// function is called again after that, returns null.
160 /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The144 /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
161 /// delimiter byte is not included in the returned slice.145 /// delimiter byte is not included in the returned slice.
162 pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 {146 pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {
163 var index: usize = 0;147 var index: usize = 0;
164 while (true) {148 while (true) {
165 const byte = self.readByte() catch |err| switch (err) {149 const byte = self.readByte() catch |err| switch (err) {
...@@ -184,7 +168,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -184,7 +168,7 @@ pub fn InStream(comptime ReadError: type) type {
184 /// Reads from the stream until specified byte is found, discarding all data,168 /// Reads from the stream until specified byte is found, discarding all data,
185 /// including the delimiter.169 /// including the delimiter.
186 /// If end-of-stream is found, this function succeeds.170 /// If end-of-stream is found, this function succeeds.
187 pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void {171 pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void {
188 while (true) {172 while (true) {
189 const byte = self.readByte() catch |err| switch (err) {173 const byte = self.readByte() catch |err| switch (err) {
190 error.EndOfStream => return,174 error.EndOfStream => return,
...@@ -195,7 +179,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -195,7 +179,7 @@ pub fn InStream(comptime ReadError: type) type {
195 }179 }
196180
197 /// Reads 1 byte from the stream or returns `error.EndOfStream`.181 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
198 pub fn readByte(self: *Self) !u8 {182 pub fn readByte(self: Self) !u8 {
199 var result: [1]u8 = undefined;183 var result: [1]u8 = undefined;
200 const amt_read = try self.read(result[0..]);184 const amt_read = try self.read(result[0..]);
201 if (amt_read < 1) return error.EndOfStream;185 if (amt_read < 1) return error.EndOfStream;
...@@ -203,43 +187,43 @@ pub fn InStream(comptime ReadError: type) type {...@@ -203,43 +187,43 @@ pub fn InStream(comptime ReadError: type) type {
203 }187 }
204188
205 /// Same as `readByte` except the returned byte is signed.189 /// Same as `readByte` except the returned byte is signed.
206 pub fn readByteSigned(self: *Self) !i8 {190 pub fn readByteSigned(self: Self) !i8 {
207 return @bitCast(i8, try self.readByte());191 return @bitCast(i8, try self.readByte());
208 }192 }
209193
210 /// Reads a native-endian integer194 /// Reads a native-endian integer
211 pub fn readIntNative(self: *Self, comptime T: type) !T {195 pub fn readIntNative(self: Self, comptime T: type) !T {
212 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;196 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
213 try self.readNoEof(bytes[0..]);197 try self.readNoEof(bytes[0..]);
214 return mem.readIntNative(T, &bytes);198 return mem.readIntNative(T, &bytes);
215 }199 }
216200
217 /// Reads a foreign-endian integer201 /// Reads a foreign-endian integer
218 pub fn readIntForeign(self: *Self, comptime T: type) !T {202 pub fn readIntForeign(self: Self, comptime T: type) !T {
219 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;203 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
220 try self.readNoEof(bytes[0..]);204 try self.readNoEof(bytes[0..]);
221 return mem.readIntForeign(T, &bytes);205 return mem.readIntForeign(T, &bytes);
222 }206 }
223207
224 pub fn readIntLittle(self: *Self, comptime T: type) !T {208 pub fn readIntLittle(self: Self, comptime T: type) !T {
225 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;209 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
226 try self.readNoEof(bytes[0..]);210 try self.readNoEof(bytes[0..]);
227 return mem.readIntLittle(T, &bytes);211 return mem.readIntLittle(T, &bytes);
228 }212 }
229213
230 pub fn readIntBig(self: *Self, comptime T: type) !T {214 pub fn readIntBig(self: Self, comptime T: type) !T {
231 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;215 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
232 try self.readNoEof(bytes[0..]);216 try self.readNoEof(bytes[0..]);
233 return mem.readIntBig(T, &bytes);217 return mem.readIntBig(T, &bytes);
234 }218 }
235219
236 pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {220 pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T {
237 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;221 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
238 try self.readNoEof(bytes[0..]);222 try self.readNoEof(bytes[0..]);
239 return mem.readInt(T, &bytes, endian);223 return mem.readInt(T, &bytes, endian);
240 }224 }
241225
242 pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {226 pub fn readVarInt(self: Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
243 assert(size <= @sizeOf(ReturnType));227 assert(size <= @sizeOf(ReturnType));
244 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;228 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
245 const bytes = bytes_buf[0..size];229 const bytes = bytes_buf[0..size];
...@@ -247,14 +231,14 @@ pub fn InStream(comptime ReadError: type) type {...@@ -247,14 +231,14 @@ pub fn InStream(comptime ReadError: type) type {
247 return mem.readVarInt(ReturnType, bytes, endian);231 return mem.readVarInt(ReturnType, bytes, endian);
248 }232 }
249233
250 pub fn skipBytes(self: *Self, num_bytes: u64) !void {234 pub fn skipBytes(self: Self, num_bytes: u64) !void {
251 var i: u64 = 0;235 var i: u64 = 0;
252 while (i < num_bytes) : (i += 1) {236 while (i < num_bytes) : (i += 1) {
253 _ = try self.readByte();237 _ = try self.readByte();
254 }238 }
255 }239 }
256240
257 pub fn readStruct(self: *Self, comptime T: type) !T {241 pub fn readStruct(self: Self, comptime T: type) !T {
258 // Only extern and packed structs have defined in-memory layout.242 // Only extern and packed structs have defined in-memory layout.
259 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);243 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
260 var res: [1]T = undefined;244 var res: [1]T = undefined;
...@@ -265,7 +249,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -265,7 +249,7 @@ pub fn InStream(comptime ReadError: type) type {
265 /// Reads an integer with the same size as the given enum's tag type. If the integer matches249 /// Reads an integer with the same size as the given enum's tag type. If the integer matches
266 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error.250 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error.
267 /// TODO optimization taking advantage of most fields being in order251 /// TODO optimization taking advantage of most fields being in order
268 pub fn readEnum(self: *Self, comptime Enum: type, endian: builtin.Endian) !Enum {252 pub fn readEnum(self: Self, comptime Enum: type, endian: builtin.Endian) !Enum {
269 const E = error{253 const E = error{
270 /// An integer was read, but it did not match any of the tags in the supplied enum.254 /// An integer was read, but it did not match any of the tags in the supplied enum.
271 InvalidValue,255 InvalidValue,
...@@ -286,8 +270,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -286,8 +270,7 @@ pub fn InStream(comptime ReadError: type) type {
286270
287test "InStream" {271test "InStream" {
288 var buf = "a\x02".*;272 var buf = "a\x02".*;
289 var slice_stream = std.io.SliceInStream.init(&buf);273 const in_stream = std.io.fixedBufferStream(&buf).inStream();
290 const in_stream = &slice_stream.stream;
291 testing.expect((try in_stream.readByte()) == 'a');274 testing.expect((try in_stream.readByte()) == 'a');
292 testing.expect((try in_stream.readEnum(enum(u8) {275 testing.expect((try in_stream.readEnum(enum(u8) {
293 a = 0,276 a = 0,
lib/std/io/out_stream.zig+33-42
...@@ -1,94 +1,85 @@...@@ -1,94 +1,85 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = std.builtin;
3const root = @import("root");
4const mem = std.mem;3const mem = std.mem;
54
6pub const default_stack_size = 1 * 1024 * 1024;5pub fn OutStream(
7pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))6 comptime Context: type,
8 root.stack_size_std_io_OutStream7 comptime WriteError: type,
9else8 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
10 default_stack_size;9) type {
11
12pub fn OutStream(comptime WriteError: type) type {
13 return struct {10 return struct {
11 context: Context,
12
14 const Self = @This();13 const Self = @This();
15 pub const Error = WriteError;14 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,16 pub fn write(self: Self, bytes: []const u8) Error!usize {
2217 return writeFn(self.context, bytes);
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 }
32 }18 }
3319
34 pub fn write(self: *Self, bytes: []const u8) Error!void {20 pub fn writeAll(self: Self, bytes: []const u8) Error!void {
35 var index: usize = 0;21 var index: usize = 0;
36 while (index != bytes.len) {22 while (index != bytes.len) {
37 index += try self.writeOnce(bytes[index..]);23 index += try self.write(bytes[index..]);
38 }24 }
39 }25 }
4026
41 pub fn print(self: *Self, comptime format: []const u8, args: var) Error!void {27 pub fn print(self: Self, comptime format: []const u8, args: var) Error!void {
42 return std.fmt.format(self, Error, write, format, args);28 return std.fmt.format(self, Error, writeAll, format, args);
43 }29 }
4430
45 pub fn writeByte(self: *Self, byte: u8) Error!void {31 pub fn writeByte(self: Self, byte: u8) Error!void {
46 const array = [1]u8{byte};32 const array = [1]u8{byte};
47 return self.write(&array);33 return self.writeAll(&array);
48 }34 }
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 {
51 var bytes: [256]u8 = undefined;37 var bytes: [256]u8 = undefined;
52 mem.set(u8, bytes[0..], byte);38 mem.set(u8, bytes[0..], byte);
5339
54 var remaining: usize = n;40 var remaining: usize = n;
55 while (remaining > 0) {41 while (remaining > 0) {
56 const to_write = std.math.min(remaining, bytes.len);42 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]);
58 remaining -= to_write;44 remaining -= to_write;
59 }45 }
60 }46 }
6147
62 /// Write a native-endian integer.48 /// 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 {
64 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;51 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
65 mem.writeIntNative(T, &bytes, value);52 mem.writeIntNative(T, &bytes, value);
66 return self.write(&bytes);53 return self.writeAll(&bytes);
67 }54 }
6855
69 /// Write a foreign-endian integer.56 /// 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 {
71 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;59 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
72 mem.writeIntForeign(T, &bytes, value);60 mem.writeIntForeign(T, &bytes, value);
73 return self.write(&bytes);61 return self.writeAll(&bytes);
74 }62 }
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 {
77 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;66 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
78 mem.writeIntLittle(T, &bytes, value);67 mem.writeIntLittle(T, &bytes, value);
79 return self.write(&bytes);68 return self.writeAll(&bytes);
80 }69 }
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 {
83 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;73 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
84 mem.writeIntBig(T, &bytes, value);74 mem.writeIntBig(T, &bytes, value);
85 return self.write(&bytes);75 return self.writeAll(&bytes);
86 }76 }
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 {
89 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;80 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
90 mem.writeInt(T, &bytes, value, endian);81 mem.writeInt(T, &bytes, value, endian);
91 return self.write(&bytes);82 return self.writeAll(&bytes);
92 }83 }
93 };84 };
94}85}
lib/std/io/peek_stream.zig created+112
...@@ -0,0 +1,112 @@
1const std = @import("../std.zig");
2const io = std.io;
3const mem = std.mem;
4const testing = std.testing;
5
6/// Creates a stream which supports 'un-reading' data, so that it can be read again.
7/// This makes look-ahead style parsing much easier.
8/// TODO merge this with `std.io.BufferedInStream`: https://github.com/ziglang/zig/issues/4501
9pub fn PeekStream(
10 comptime buffer_type: std.fifo.LinearFifoBufferType,
11 comptime InStreamType: type,
12) type {
13 return struct {
14 unbuffered_in_stream: InStreamType,
15 fifo: FifoType,
16
17 pub const Error = InStreamType.Error;
18 pub const InStream = io.InStream(*Self, Error, read);
19
20 const Self = @This();
21 const FifoType = std.fifo.LinearFifo(u8, buffer_type);
22
23 pub usingnamespace switch (buffer_type) {
24 .Static => struct {
25 pub fn init(base: InStreamType) Self {
26 return .{
27 .base = base,
28 .fifo = FifoType.init(),
29 };
30 }
31 },
32 .Slice => struct {
33 pub fn init(base: InStreamType, buf: []u8) Self {
34 return .{
35 .base = base,
36 .fifo = FifoType.init(buf),
37 };
38 }
39 },
40 .Dynamic => struct {
41 pub fn init(base: InStreamType, allocator: *mem.Allocator) Self {
42 return .{
43 .base = base,
44 .fifo = FifoType.init(allocator),
45 };
46 }
47 },
48 };
49
50 pub fn putBackByte(self: *Self, byte: u8) !void {
51 try self.putBack(&[_]u8{byte});
52 }
53
54 pub fn putBack(self: *Self, bytes: []const u8) !void {
55 try self.fifo.unget(bytes);
56 }
57
58 pub fn read(self: *Self, dest: []u8) Error!usize {
59 // copy over anything putBack()'d
60 var dest_index = self.fifo.read(dest);
61 if (dest_index == dest.len) return dest_index;
62
63 // ask the backing stream for more
64 dest_index += try self.base.read(dest[dest_index..]);
65 return dest_index;
66 }
67
68 pub fn inStream(self: *Self) InStream {
69 return .{ .context = self };
70 }
71 };
72}
73
74pub fn peekStream(
75 comptime lookahead: comptime_int,
76 underlying_stream: var,
77) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) {
78 return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream);
79}
80
81test "PeekStream" {
82 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
83 var fbs = io.fixedBufferStream(&bytes);
84 var ps = peekStream(2, fbs.inStream());
85
86 var dest: [4]u8 = undefined;
87
88 try ps.putBackByte(9);
89 try ps.putBackByte(10);
90
91 var read = try ps.inStream().read(dest[0..4]);
92 testing.expect(read == 4);
93 testing.expect(dest[0] == 10);
94 testing.expect(dest[1] == 9);
95 testing.expect(mem.eql(u8, dest[2..4], bytes[0..2]));
96
97 read = try ps.inStream().read(dest[0..4]);
98 testing.expect(read == 4);
99 testing.expect(mem.eql(u8, dest[0..4], bytes[2..6]));
100
101 read = try ps.inStream().read(dest[0..4]);
102 testing.expect(read == 2);
103 testing.expect(mem.eql(u8, dest[0..2], bytes[6..8]));
104
105 try ps.putBackByte(11);
106 try ps.putBackByte(12);
107
108 read = try ps.inStream().read(dest[0..4]);
109 testing.expect(read == 2);
110 testing.expect(dest[0] == 12);
111 testing.expect(dest[1] == 11);
112}
lib/std/io/seekable_stream.zig+19-86
...@@ -1,103 +1,36 @@...@@ -1,103 +1,36 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const InStream = std.io.InStream;2const 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 {
5 return struct {13 return struct {
14 context: Context,
15
6 const Self = @This();16 const Self = @This();
7 pub const SeekError = SeekErrorType;17 pub const SeekError = SeekErrorType;
8 pub const GetSeekPosError = GetSeekPosErrorType;18 pub const GetSeekPosError = GetSeekPosErrorType;
919
10 seekToFn: fn (self: *Self, pos: u64) SeekError!void,20 pub fn seekTo(self: Self, pos: u64) SeekError!void {
11 seekByFn: fn (self: *Self, pos: i64) SeekError!void,21 return seekToFn(self.context, pos);
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);
18 }22 }
1923
20 pub fn seekBy(self: *Self, amt: i64) SeekError!void {24 pub fn seekBy(self: Self, amt: i64) SeekError!void {
21 return self.seekByFn(self, amt);25 return seekByFn(self.context, amt);
22 }26 }
2327
24 pub fn getEndPos(self: *Self) GetSeekPosError!u64 {28 pub fn getEndPos(self: Self) GetSeekPosError!u64 {
25 return self.getEndPosFn(self);29 return getEndPosFn(self.context);
26 }30 }
2731
28 pub fn getPos(self: *Self) GetSeekPosError!u64 {32 pub fn getPos(self: Self) GetSeekPosError!u64 {
29 return self.getPosFn(self);33 return getPosFn(self.context);
30 }34 }
31 };35 };
32}36}
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/io/serialization.zig created+606
...@@ -0,0 +1,606 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4
5pub const Packing = enum {
6 /// Pack data to byte alignment
7 Byte,
8
9 /// Pack data to bit alignment
10 Bit,
11};
12
13/// Creates a deserializer that deserializes types from any stream.
14/// If `is_packed` is true, the data stream is treated as bit-packed,
15/// otherwise data is expected to be packed to the smallest byte.
16/// Types may implement a custom deserialization routine with a
17/// function named `deserialize` in the form of:
18/// pub fn deserialize(self: *Self, deserializer: var) !void
19/// which will be called when the deserializer is used to deserialize
20/// that type. It will pass a pointer to the type instance to deserialize
21/// into and a pointer to the deserializer struct.
22pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime InStreamType: type) type {
23 return struct {
24 in_stream: if (packing == .Bit) io.BitInStream(endian, InStreamType) else InStreamType,
25
26 const Self = @This();
27
28 pub fn init(in_stream: InStreamType) Self {
29 return Self{
30 .in_stream = switch (packing) {
31 .Bit => io.bitInStream(endian, in_stream),
32 .Byte => in_stream,
33 },
34 };
35 }
36
37 pub fn alignToByte(self: *Self) void {
38 if (packing == .Byte) return;
39 self.in_stream.alignToByte();
40 }
41
42 //@BUG: inferred error issue. See: #1386
43 fn deserializeInt(self: *Self, comptime T: type) (InStreamType.Error || error{EndOfStream})!T {
44 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
45
46 const u8_bit_count = 8;
47 const t_bit_count = comptime meta.bitCount(T);
48
49 const U = std.meta.IntType(false, t_bit_count);
50 const Log2U = math.Log2Int(U);
51 const int_size = (U.bit_count + 7) / 8;
52
53 if (packing == .Bit) {
54 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
55 return @bitCast(T, result);
56 }
57
58 var buffer: [int_size]u8 = undefined;
59 const read_size = try self.in_stream.read(buffer[0..]);
60 if (read_size < int_size) return error.EndOfStream;
61
62 if (int_size == 1) {
63 if (t_bit_count == 8) return @bitCast(T, buffer[0]);
64 const PossiblySignedByte = std.meta.IntType(T.is_signed, 8);
65 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
66 }
67
68 var result = @as(U, 0);
69 for (buffer) |byte, i| {
70 switch (endian) {
71 .Big => {
72 result = (result << u8_bit_count) | byte;
73 },
74 .Little => {
75 result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
76 },
77 }
78 }
79
80 return @bitCast(T, result);
81 }
82
83 /// Deserializes and returns data of the specified type from the stream
84 pub fn deserialize(self: *Self, comptime T: type) !T {
85 var value: T = undefined;
86 try self.deserializeInto(&value);
87 return value;
88 }
89
90 /// Deserializes data into the type pointed to by `ptr`
91 pub fn deserializeInto(self: *Self, ptr: var) !void {
92 const T = @TypeOf(ptr);
93 comptime assert(trait.is(.Pointer)(T));
94
95 if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
96 for (ptr) |*v|
97 try self.deserializeInto(v);
98 return;
99 }
100
101 comptime assert(trait.isSingleItemPtr(T));
102
103 const C = comptime meta.Child(T);
104 const child_type_id = @typeInfo(C);
105
106 //custom deserializer: fn(self: *Self, deserializer: var) !void
107 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
108
109 if (comptime trait.isPacked(C) and packing != .Bit) {
110 var packed_deserializer = deserializer(endian, .Bit, self.in_stream);
111 return packed_deserializer.deserializeInto(ptr);
112 }
113
114 switch (child_type_id) {
115 .Void => return,
116 .Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
117 .Float, .Int => ptr.* = try self.deserializeInt(C),
118 .Struct => {
119 const info = @typeInfo(C).Struct;
120
121 inline for (info.fields) |*field_info| {
122 const name = field_info.name;
123 const FieldType = field_info.field_type;
124
125 if (FieldType == void or FieldType == u0) continue;
126
127 //it doesn't make any sense to read pointers
128 if (comptime trait.is(.Pointer)(FieldType)) {
129 @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
130 @typeName(C) ++ " because it " ++ "is of pointer-type " ++
131 @typeName(FieldType) ++ ".");
132 }
133
134 try self.deserializeInto(&@field(ptr, name));
135 }
136 },
137 .Union => {
138 const info = @typeInfo(C).Union;
139 if (info.tag_type) |TagType| {
140 //we avoid duplicate iteration over the enum tags
141 // by getting the int directly and casting it without
142 // safety. If it is bad, it will be caught anyway.
143 const TagInt = @TagType(TagType);
144 const tag = try self.deserializeInt(TagInt);
145
146 inline for (info.fields) |field_info| {
147 if (field_info.enum_field.?.value == tag) {
148 const name = field_info.name;
149 const FieldType = field_info.field_type;
150 ptr.* = @unionInit(C, name, undefined);
151 try self.deserializeInto(&@field(ptr, name));
152 return;
153 }
154 }
155 //This is reachable if the enum data is bad
156 return error.InvalidEnumTag;
157 }
158 @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
159 " because it is an untagged union. Use a custom deserialize().");
160 },
161 .Optional => {
162 const OC = comptime meta.Child(C);
163 const exists = (try self.deserializeInt(u1)) > 0;
164 if (!exists) {
165 ptr.* = null;
166 return;
167 }
168
169 ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
170 const val_ptr = &ptr.*.?;
171 try self.deserializeInto(val_ptr);
172 },
173 .Enum => {
174 var value = try self.deserializeInt(@TagType(C));
175 ptr.* = try meta.intToEnum(C, value);
176 },
177 else => {
178 @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
179 },
180 }
181 }
182 };
183}
184
185pub fn deserializer(
186 comptime endian: builtin.Endian,
187 comptime packing: Packing,
188 in_stream: var,
189) Deserializer(endian, packing, @TypeOf(in_stream)) {
190 return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
191}
192
193/// Creates a serializer that serializes types to any stream.
194/// If `is_packed` is true, the data will be bit-packed into the stream.
195/// Note that the you must call `serializer.flush()` when you are done
196/// writing bit-packed data in order ensure any unwritten bits are committed.
197/// If `is_packed` is false, data is packed to the smallest byte. In the case
198/// of packed structs, the struct will written bit-packed and with the specified
199/// endianess, after which data will resume being written at the next byte boundary.
200/// Types may implement a custom serialization routine with a
201/// function named `serialize` in the form of:
202/// pub fn serialize(self: Self, serializer: var) !void
203/// which will be called when the serializer is used to serialize that type. It will
204/// pass a const pointer to the type instance to be serialized and a pointer
205/// to the serializer struct.
206pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
207 return struct {
208 out_stream: if (packing == .Bit) BitOutStream(endian, OutStreamType) else OutStreamType,
209
210 const Self = @This();
211 pub const Error = OutStreamType.Error;
212
213 pub fn init(out_stream: OutStreamType) Self {
214 return Self{
215 .out_stream = switch (packing) {
216 .Bit => io.bitOutStream(endian, out_stream),
217 .Byte => out_stream,
218 },
219 };
220 }
221
222 /// Flushes any unwritten bits to the stream
223 pub fn flush(self: *Self) Error!void {
224 if (packing == .Bit) return self.out_stream.flushBits();
225 }
226
227 fn serializeInt(self: *Self, value: var) Error!void {
228 const T = @TypeOf(value);
229 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
230
231 const t_bit_count = comptime meta.bitCount(T);
232 const u8_bit_count = comptime meta.bitCount(u8);
233
234 const U = std.meta.IntType(false, t_bit_count);
235 const Log2U = math.Log2Int(U);
236 const int_size = (U.bit_count + 7) / 8;
237
238 const u_value = @bitCast(U, value);
239
240 if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
241
242 var buffer: [int_size]u8 = undefined;
243 if (int_size == 1) buffer[0] = u_value;
244
245 for (buffer) |*byte, i| {
246 const idx = switch (endian) {
247 .Big => int_size - i - 1,
248 .Little => i,
249 };
250 const shift = @intCast(Log2U, idx * u8_bit_count);
251 const v = u_value >> shift;
252 byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
253 }
254
255 try self.out_stream.write(&buffer);
256 }
257
258 /// Serializes the passed value into the stream
259 pub fn serialize(self: *Self, value: var) Error!void {
260 const T = comptime @TypeOf(value);
261
262 if (comptime trait.isIndexable(T)) {
263 for (value) |v|
264 try self.serialize(v);
265 return;
266 }
267
268 //custom serializer: fn(self: Self, serializer: var) !void
269 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
270
271 if (comptime trait.isPacked(T) and packing != .Bit) {
272 var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
273 try packed_serializer.serialize(value);
274 try packed_serializer.flush();
275 return;
276 }
277
278 switch (@typeInfo(T)) {
279 .Void => return,
280 .Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
281 .Float, .Int => try self.serializeInt(value),
282 .Struct => {
283 const info = @typeInfo(T);
284
285 inline for (info.Struct.fields) |*field_info| {
286 const name = field_info.name;
287 const FieldType = field_info.field_type;
288
289 if (FieldType == void or FieldType == u0) continue;
290
291 //It doesn't make sense to write pointers
292 if (comptime trait.is(.Pointer)(FieldType)) {
293 @compileError("Will not " ++ "serialize field " ++ name ++
294 " of struct " ++ @typeName(T) ++ " because it " ++
295 "is of pointer-type " ++ @typeName(FieldType) ++ ".");
296 }
297 try self.serialize(@field(value, name));
298 }
299 },
300 .Union => {
301 const info = @typeInfo(T).Union;
302 if (info.tag_type) |TagType| {
303 const active_tag = meta.activeTag(value);
304 try self.serialize(active_tag);
305 //This inline loop is necessary because active_tag is a runtime
306 // value, but @field requires a comptime value. Our alternative
307 // is to check each field for a match
308 inline for (info.fields) |field_info| {
309 if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
310 const name = field_info.name;
311 const FieldType = field_info.field_type;
312 try self.serialize(@field(value, name));
313 return;
314 }
315 }
316 unreachable;
317 }
318 @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
319 " because it is an untagged union. Use a custom serialize().");
320 },
321 .Optional => {
322 if (value == null) {
323 try self.serializeInt(@as(u1, @boolToInt(false)));
324 return;
325 }
326 try self.serializeInt(@as(u1, @boolToInt(true)));
327
328 const OC = comptime meta.Child(T);
329 const val_ptr = &value.?;
330 try self.serialize(val_ptr.*);
331 },
332 .Enum => {
333 try self.serializeInt(@enumToInt(value));
334 },
335 else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
336 }
337 }
338 };
339}
340
341pub fn serializer(
342 comptime endian: builtin.Endian,
343 comptime packing: Packing,
344 out_stream: var,
345) Serializer(endian, packing, @TypeOf(out_stream)) {
346 return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
347}
348
349fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
350 @setEvalBranchQuota(1500);
351 //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
352 const max_test_bitsize = 128;
353
354 const total_bytes = comptime blk: {
355 var bytes = 0;
356 comptime var i = 0;
357 while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
358 break :blk bytes * 2;
359 };
360
361 var data_mem: [total_bytes]u8 = undefined;
362 var out = io.fixedBufferStream(&data_mem);
363 var serializer = serializer(endian, packing, out.outStream());
364
365 var in = io.fixedBufferStream(&data_mem);
366 var deserializer = Deserializer(endian, packing, in.inStream());
367
368 comptime var i = 0;
369 inline while (i <= max_test_bitsize) : (i += 1) {
370 const U = std.meta.IntType(false, i);
371 const S = std.meta.IntType(true, i);
372 try serializer.serializeInt(@as(U, i));
373 if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0));
374 }
375 try serializer.flush();
376
377 i = 0;
378 inline while (i <= max_test_bitsize) : (i += 1) {
379 const U = std.meta.IntType(false, i);
380 const S = std.meta.IntType(true, i);
381 const x = try deserializer.deserializeInt(U);
382 const y = try deserializer.deserializeInt(S);
383 expect(x == @as(U, i));
384 if (i != 0) expect(y == @as(S, -1)) else expect(y == 0);
385 }
386
387 const u8_bit_count = comptime meta.bitCount(u8);
388 //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
389 //and we have each for unsigned and signed, so * 2
390 const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
391 const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
392 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
393
394 expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
395
396 //Verify that empty error set works with serializer.
397 //deserializer is covered by FixedBufferStream
398 var null_serializer = io.serializer(endian, packing, std.io.null_out_stream);
399 try null_serializer.serialize(data_mem[0..]);
400 try null_serializer.flush();
401}
402
403test "Serializer/Deserializer Int" {
404 try testIntSerializerDeserializer(.Big, .Byte);
405 try testIntSerializerDeserializer(.Little, .Byte);
406 // TODO these tests are disabled due to tripping an LLVM assertion
407 // https://github.com/ziglang/zig/issues/2019
408 //try testIntSerializerDeserializer(builtin.Endian.Big, true);
409 //try testIntSerializerDeserializer(builtin.Endian.Little, true);
410}
411
412fn testIntSerializerDeserializerInfNaN(
413 comptime endian: builtin.Endian,
414 comptime packing: io.Packing,
415) !void {
416 const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
417 var data_mem: [mem_size]u8 = undefined;
418
419 var out = io.fixedBufferStream(&data_mem);
420 var serializer = serializer(endian, packing, out.outStream());
421
422 var in = io.fixedBufferStream(&data_mem);
423 var deserializer = deserializer(endian, packing, in.inStream());
424
425 //@TODO: isInf/isNan not currently implemented for f128.
426 try serializer.serialize(std.math.nan(f16));
427 try serializer.serialize(std.math.inf(f16));
428 try serializer.serialize(std.math.nan(f32));
429 try serializer.serialize(std.math.inf(f32));
430 try serializer.serialize(std.math.nan(f64));
431 try serializer.serialize(std.math.inf(f64));
432 //try serializer.serialize(std.math.nan(f128));
433 //try serializer.serialize(std.math.inf(f128));
434 const nan_check_f16 = try deserializer.deserialize(f16);
435 const inf_check_f16 = try deserializer.deserialize(f16);
436 const nan_check_f32 = try deserializer.deserialize(f32);
437 deserializer.alignToByte();
438 const inf_check_f32 = try deserializer.deserialize(f32);
439 const nan_check_f64 = try deserializer.deserialize(f64);
440 const inf_check_f64 = try deserializer.deserialize(f64);
441 //const nan_check_f128 = try deserializer.deserialize(f128);
442 //const inf_check_f128 = try deserializer.deserialize(f128);
443 expect(std.math.isNan(nan_check_f16));
444 expect(std.math.isInf(inf_check_f16));
445 expect(std.math.isNan(nan_check_f32));
446 expect(std.math.isInf(inf_check_f32));
447 expect(std.math.isNan(nan_check_f64));
448 expect(std.math.isInf(inf_check_f64));
449 //expect(std.math.isNan(nan_check_f128));
450 //expect(std.math.isInf(inf_check_f128));
451}
452
453test "Serializer/Deserializer Int: Inf/NaN" {
454 try testIntSerializerDeserializerInfNaN(.Big, .Byte);
455 try testIntSerializerDeserializerInfNaN(.Little, .Byte);
456 try testIntSerializerDeserializerInfNaN(.Big, .Bit);
457 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
458}
459
460fn testAlternateSerializer(self: var, serializer: var) !void {
461 try serializer.serialize(self.f_f16);
462}
463
464fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
465 const ColorType = enum(u4) {
466 RGB8 = 1,
467 RA16 = 2,
468 R32 = 3,
469 };
470
471 const TagAlign = union(enum(u32)) {
472 A: u8,
473 B: u8,
474 C: u8,
475 };
476
477 const Color = union(ColorType) {
478 RGB8: struct {
479 r: u8,
480 g: u8,
481 b: u8,
482 a: u8,
483 },
484 RA16: struct {
485 r: u16,
486 a: u16,
487 },
488 R32: u32,
489 };
490
491 const PackedStruct = packed struct {
492 f_i3: i3,
493 f_u2: u2,
494 };
495
496 //to test custom serialization
497 const Custom = struct {
498 f_f16: f16,
499 f_unused_u32: u32,
500
501 pub fn deserialize(self: *@This(), deserializer: var) !void {
502 try deserializer.deserializeInto(&self.f_f16);
503 self.f_unused_u32 = 47;
504 }
505
506 pub const serialize = testAlternateSerializer;
507 };
508
509 const MyStruct = struct {
510 f_i3: i3,
511 f_u8: u8,
512 f_tag_align: TagAlign,
513 f_u24: u24,
514 f_i19: i19,
515 f_void: void,
516 f_f32: f32,
517 f_f128: f128,
518 f_packed_0: PackedStruct,
519 f_i7arr: [10]i7,
520 f_of64n: ?f64,
521 f_of64v: ?f64,
522 f_color_type: ColorType,
523 f_packed_1: PackedStruct,
524 f_custom: Custom,
525 f_color: Color,
526 };
527
528 const my_inst = MyStruct{
529 .f_i3 = -1,
530 .f_u8 = 8,
531 .f_tag_align = TagAlign{ .B = 148 },
532 .f_u24 = 24,
533 .f_i19 = 19,
534 .f_void = {},
535 .f_f32 = 32.32,
536 .f_f128 = 128.128,
537 .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
538 .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
539 .f_of64n = null,
540 .f_of64v = 64.64,
541 .f_color_type = ColorType.R32,
542 .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
543 .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
544 .f_color = Color{ .R32 = 123822 },
545 };
546
547 var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
548 var out = io.fixedBufferStream(&data_mem);
549 var serializer = serializer(endian, packing, out.outStream());
550
551 var in = io.fixedBufferStream(&data_mem);
552 var deserializer = deserializer(endian, packing, in.inStream());
553
554 try serializer.serialize(my_inst);
555
556 const my_copy = try deserializer.deserialize(MyStruct);
557 expect(meta.eql(my_copy, my_inst));
558}
559
560test "Serializer/Deserializer generic" {
561 if (std.Target.current.os.tag == .windows) {
562 // TODO https://github.com/ziglang/zig/issues/508
563 return error.SkipZigTest;
564 }
565 try testSerializerDeserializer(builtin.Endian.Big, .Byte);
566 try testSerializerDeserializer(builtin.Endian.Little, .Byte);
567 try testSerializerDeserializer(builtin.Endian.Big, .Bit);
568 try testSerializerDeserializer(builtin.Endian.Little, .Bit);
569}
570
571fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
572 const E = enum(u14) {
573 One = 1,
574 Two = 2,
575 };
576
577 const A = struct {
578 e: E,
579 };
580
581 const C = union(E) {
582 One: u14,
583 Two: f16,
584 };
585
586 var data_mem: [4]u8 = undefined;
587 var out = io.fixedBufferStream.init(&data_mem);
588 var serializer = serializer(endian, packing, out.outStream());
589
590 var in = io.fixedBufferStream(&data_mem);
591 var deserializer = deserializer(endian, packing, in.inStream());
592
593 try serializer.serialize(@as(u14, 3));
594 expectError(error.InvalidEnumTag, deserializer.deserialize(A));
595 out.pos = 0;
596 try serializer.serialize(@as(u14, 3));
597 try serializer.serialize(@as(u14, 88));
598 expectError(error.InvalidEnumTag, deserializer.deserialize(C));
599}
600
601test "Deserializer bad data" {
602 try testBadData(.Big, .Byte);
603 try testBadData(.Little, .Byte);
604 try testBadData(.Big, .Bit);
605 try testBadData(.Little, .Bit);
606}
lib/std/io/stream_source.zig created+90
...@@ -0,0 +1,90 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4
5/// Provides `io.InStream`, `io.OutStream`, and `io.SeekableStream` for in-memory buffers as
6/// well as files.
7/// For memory sources, if the supplied byte buffer is const, then `io.OutStream` is not available.
8/// The error set of the stream functions is the error set of the corresponding file functions.
9pub const StreamSource = union(enum) {
10 buffer: io.FixedBufferStream([]u8),
11 const_buffer: io.FixedBufferStream([]const u8),
12 file: std.fs.File,
13
14 pub const ReadError = std.fs.File.ReadError;
15 pub const WriteError = std.fs.File.WriteError;
16 pub const SeekError = std.fs.File.SeekError;
17 pub const GetSeekPosError = std.fs.File.GetPosError;
18
19 pub const InStream = io.InStream(*StreamSource, ReadError, read);
20 pub const OutStream = io.OutStream(*StreamSource, WriteError, write);
21 pub const SeekableStream = io.SeekableStream(
22 *StreamSource,
23 SeekError,
24 GetSeekPosError,
25 seekTo,
26 seekBy,
27 getPos,
28 getEndPos,
29 );
30
31 pub fn read(self: *StreamSource, dest: []u8) ReadError!usize {
32 switch (self.*) {
33 .buffer => |*x| return x.read(dest),
34 .const_buffer => |*x| return x.read(dest),
35 .file => |x| return x.read(dest),
36 }
37 }
38
39 pub fn write(self: *StreamSource, bytes: []const u8) WriteError!usize {
40 switch (self.*) {
41 .buffer => |*x| return x.write(bytes),
42 .const_buffer => |*x| return x.write(bytes),
43 .file => |x| return x.write(bytes),
44 }
45 }
46
47 pub fn seekTo(self: *StreamSource, pos: u64) SeekError!void {
48 switch (self.*) {
49 .buffer => |*x| return x.seekTo(pos),
50 .const_buffer => |*x| return x.seekTo(pos),
51 .file => |x| return x.seekTo(pos),
52 }
53 }
54
55 pub fn seekBy(self: *StreamSource, amt: i64) SeekError!void {
56 switch (self.*) {
57 .buffer => |*x| return x.seekBy(amt),
58 .const_buffer => |*x| return x.seekBy(amt),
59 .file => |x| return x.seekBy(amt),
60 }
61 }
62
63 pub fn getEndPos(self: *StreamSource) GetSeekPosError!u64 {
64 switch (self.*) {
65 .buffer => |*x| return x.getEndPos(),
66 .const_buffer => |*x| return x.getEndPos(),
67 .file => |x| return x.getEndPos(),
68 }
69 }
70
71 pub fn getPos(self: *StreamSource) GetSeekPosError!u64 {
72 switch (self.*) {
73 .buffer => |*x| return x.getPos(),
74 .const_buffer => |*x| return x.getPos(),
75 .file => |x| return x.getPos(),
76 }
77 }
78
79 pub fn inStream(self: *StreamSource) InStream {
80 return .{ .context = self };
81 }
82
83 pub fn outStream(self: *StreamSource) OutStream {
84 return .{ .context = self };
85 }
86
87 pub fn seekableStream(self: *StreamSource) SeekableStream {
88 return .{ .context = self };
89 }
90};
lib/std/io/test.zig+13-521
...@@ -22,11 +22,10 @@ test "write a file, read it, then delete it" {...@@ -22,11 +22,10 @@ test "write a file, read it, then delete it" {
22 var file = try cwd.createFile(tmp_file_name, .{});22 var file = try cwd.createFile(tmp_file_name, .{});
23 defer file.close();23 defer file.close();
2424
25 var file_out_stream = file.outStream();25 var buf_stream = io.bufferedOutStream(file.outStream());
26 var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream);26 const st = buf_stream.outStream();
27 const st = &buf_stream.stream;
28 try st.print("begin", .{});27 try st.print("begin", .{});
29 try st.write(data[0..]);28 try st.writeAll(data[0..]);
30 try st.print("end", .{});29 try st.print("end", .{});
31 try buf_stream.flush();30 try buf_stream.flush();
32 }31 }
...@@ -48,9 +47,8 @@ test "write a file, read it, then delete it" {...@@ -48,9 +47,8 @@ test "write a file, read it, then delete it" {
48 const expected_file_size: u64 = "begin".len + data.len + "end".len;47 const expected_file_size: u64 = "begin".len + data.len + "end".len;
49 expectEqual(expected_file_size, file_size);48 expectEqual(expected_file_size, file_size);
5049
51 var file_in_stream = file.inStream();50 var buf_stream = io.bufferedInStream(file.inStream());
52 var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);51 const st = buf_stream.inStream();
53 const st = &buf_stream.stream;
54 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);52 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
55 defer std.testing.allocator.free(contents);53 defer std.testing.allocator.free(contents);
5654
...@@ -61,224 +59,13 @@ test "write a file, read it, then delete it" {...@@ -61,224 +59,13 @@ test "write a file, read it, then delete it" {
61 try cwd.deleteFile(tmp_file_name);59 try cwd.deleteFile(tmp_file_name);
62}60}
6361
64test "BufferOutStream" {
65 var buffer = try std.Buffer.initSize(std.testing.allocator, 0);
66 defer buffer.deinit();
67 var buf_stream = &std.io.BufferOutStream.init(&buffer).stream;
68
69 const x: i32 = 42;
70 const y: i32 = 1234;
71 try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
72
73 expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
74}
75
76test "SliceInStream" {
77 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
78 var ss = io.SliceInStream.init(&bytes);
79
80 var dest: [4]u8 = undefined;
81
82 var read = try ss.stream.read(dest[0..4]);
83 expect(read == 4);
84 expect(mem.eql(u8, dest[0..4], bytes[0..4]));
85
86 read = try ss.stream.read(dest[0..4]);
87 expect(read == 3);
88 expect(mem.eql(u8, dest[0..3], bytes[4..7]));
89
90 read = try ss.stream.read(dest[0..4]);
91 expect(read == 0);
92}
93
94test "PeekStream" {
95 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
96 var ss = io.SliceInStream.init(&bytes);
97 var ps = io.PeekStream(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream);
98
99 var dest: [4]u8 = undefined;
100
101 try ps.putBackByte(9);
102 try ps.putBackByte(10);
103
104 var read = try ps.stream.read(dest[0..4]);
105 expect(read == 4);
106 expect(dest[0] == 10);
107 expect(dest[1] == 9);
108 expect(mem.eql(u8, dest[2..4], bytes[0..2]));
109
110 read = try ps.stream.read(dest[0..4]);
111 expect(read == 4);
112 expect(mem.eql(u8, dest[0..4], bytes[2..6]));
113
114 read = try ps.stream.read(dest[0..4]);
115 expect(read == 2);
116 expect(mem.eql(u8, dest[0..2], bytes[6..8]));
117
118 try ps.putBackByte(11);
119 try ps.putBackByte(12);
120
121 read = try ps.stream.read(dest[0..4]);
122 expect(read == 2);
123 expect(dest[0] == 12);
124 expect(dest[1] == 11);
125}
126
127test "SliceOutStream" {
128 var buffer: [10]u8 = undefined;
129 var ss = io.SliceOutStream.init(buffer[0..]);
130
131 try ss.stream.write("Hello");
132 expect(mem.eql(u8, ss.getWritten(), "Hello"));
133
134 try ss.stream.write("world");
135 expect(mem.eql(u8, ss.getWritten(), "Helloworld"));
136
137 expectError(error.OutOfMemory, ss.stream.write("!"));
138 expect(mem.eql(u8, ss.getWritten(), "Helloworld"));
139
140 ss.reset();
141 expect(ss.getWritten().len == 0);
142
143 expectError(error.OutOfMemory, ss.stream.write("Hello world!"));
144 expect(mem.eql(u8, ss.getWritten(), "Hello worl"));
145}
146
147test "BitInStream" {
148 const mem_be = [_]u8{ 0b11001101, 0b00001011 };
149 const mem_le = [_]u8{ 0b00011101, 0b10010101 };
150
151 var mem_in_be = io.SliceInStream.init(mem_be[0..]);
152 const InError = io.SliceInStream.Error;
153 var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream);
154
155 var out_bits: usize = undefined;
156
157 expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
158 expect(out_bits == 1);
159 expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
160 expect(out_bits == 2);
161 expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
162 expect(out_bits == 3);
163 expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
164 expect(out_bits == 4);
165 expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
166 expect(out_bits == 5);
167 expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
168 expect(out_bits == 1);
169
170 mem_in_be.pos = 0;
171 bit_stream_be.bit_count = 0;
172 expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
173 expect(out_bits == 15);
174
175 mem_in_be.pos = 0;
176 bit_stream_be.bit_count = 0;
177 expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
178 expect(out_bits == 16);
179
180 _ = try bit_stream_be.readBits(u0, 0, &out_bits);
181
182 expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
183 expect(out_bits == 0);
184 expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1));
185
186 var mem_in_le = io.SliceInStream.init(mem_le[0..]);
187 var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream);
188
189 expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
190 expect(out_bits == 1);
191 expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
192 expect(out_bits == 2);
193 expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
194 expect(out_bits == 3);
195 expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
196 expect(out_bits == 4);
197 expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
198 expect(out_bits == 5);
199 expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
200 expect(out_bits == 1);
201
202 mem_in_le.pos = 0;
203 bit_stream_le.bit_count = 0;
204 expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
205 expect(out_bits == 15);
206
207 mem_in_le.pos = 0;
208 bit_stream_le.bit_count = 0;
209 expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
210 expect(out_bits == 16);
211
212 _ = try bit_stream_le.readBits(u0, 0, &out_bits);
213
214 expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
215 expect(out_bits == 0);
216 expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1));
217}
218
219test "BitOutStream" {
220 var mem_be = [_]u8{0} ** 2;
221 var mem_le = [_]u8{0} ** 2;
222
223 var mem_out_be = io.SliceOutStream.init(mem_be[0..]);
224 const OutError = io.SliceOutStream.Error;
225 var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream);
226
227 try bit_stream_be.writeBits(@as(u2, 1), 1);
228 try bit_stream_be.writeBits(@as(u5, 2), 2);
229 try bit_stream_be.writeBits(@as(u128, 3), 3);
230 try bit_stream_be.writeBits(@as(u8, 4), 4);
231 try bit_stream_be.writeBits(@as(u9, 5), 5);
232 try bit_stream_be.writeBits(@as(u1, 1), 1);
233
234 expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
235
236 mem_out_be.pos = 0;
237
238 try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15);
239 try bit_stream_be.flushBits();
240 expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
241
242 mem_out_be.pos = 0;
243 try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16);
244 expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
245
246 try bit_stream_be.writeBits(@as(u0, 0), 0);
247
248 var mem_out_le = io.SliceOutStream.init(mem_le[0..]);
249 var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream);
250
251 try bit_stream_le.writeBits(@as(u2, 1), 1);
252 try bit_stream_le.writeBits(@as(u5, 2), 2);
253 try bit_stream_le.writeBits(@as(u128, 3), 3);
254 try bit_stream_le.writeBits(@as(u8, 4), 4);
255 try bit_stream_le.writeBits(@as(u9, 5), 5);
256 try bit_stream_le.writeBits(@as(u1, 1), 1);
257
258 expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
259
260 mem_out_le.pos = 0;
261 try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15);
262 try bit_stream_le.flushBits();
263 expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
264
265 mem_out_le.pos = 0;
266 try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16);
267 expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
268
269 try bit_stream_le.writeBits(@as(u0, 0), 0);
270}
271
272test "BitStreams with File Stream" {62test "BitStreams with File Stream" {
273 const tmp_file_name = "temp_test_file.txt";63 const tmp_file_name = "temp_test_file.txt";
274 {64 {
275 var file = try fs.cwd().createFile(tmp_file_name, .{});65 var file = try fs.cwd().createFile(tmp_file_name, .{});
276 defer file.close();66 defer file.close();
27767
278 var file_out = file.outStream();68 var bit_stream = io.bitOutStream(builtin.endian, file.outStream());
279 var file_out_stream = &file_out.stream;
280 const OutError = File.WriteError;
281 var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream);
28269
283 try bit_stream.writeBits(@as(u2, 1), 1);70 try bit_stream.writeBits(@as(u2, 1), 1);
284 try bit_stream.writeBits(@as(u5, 2), 2);71 try bit_stream.writeBits(@as(u5, 2), 2);
...@@ -292,10 +79,7 @@ test "BitStreams with File Stream" {...@@ -292,10 +79,7 @@ test "BitStreams with File Stream" {
292 var file = try fs.cwd().openFile(tmp_file_name, .{});79 var file = try fs.cwd().openFile(tmp_file_name, .{});
293 defer file.close();80 defer file.close();
29481
295 var file_in = file.inStream();82 var bit_stream = io.bitInStream(builtin.endian, file.inStream());
296 var file_in_stream = &file_in.stream;
297 const InError = File.ReadError;
298 var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream);
29983
300 var out_bits: usize = undefined;84 var out_bits: usize = undefined;
30185
...@@ -317,298 +101,6 @@ test "BitStreams with File Stream" {...@@ -317,298 +101,6 @@ test "BitStreams with File Stream" {
317 try fs.cwd().deleteFile(tmp_file_name);101 try fs.cwd().deleteFile(tmp_file_name);
318}102}
319103
320fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
321 @setEvalBranchQuota(1500);
322 //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
323 const max_test_bitsize = 128;
324
325 const total_bytes = comptime blk: {
326 var bytes = 0;
327 comptime var i = 0;
328 while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
329 break :blk bytes * 2;
330 };
331
332 var data_mem: [total_bytes]u8 = undefined;
333 var out = io.SliceOutStream.init(data_mem[0..]);
334 const OutError = io.SliceOutStream.Error;
335 var out_stream = &out.stream;
336 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
337
338 var in = io.SliceInStream.init(data_mem[0..]);
339 const InError = io.SliceInStream.Error;
340 var in_stream = &in.stream;
341 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
342
343 comptime var i = 0;
344 inline while (i <= max_test_bitsize) : (i += 1) {
345 const U = std.meta.IntType(false, i);
346 const S = std.meta.IntType(true, i);
347 try serializer.serializeInt(@as(U, i));
348 if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0));
349 }
350 try serializer.flush();
351
352 i = 0;
353 inline while (i <= max_test_bitsize) : (i += 1) {
354 const U = std.meta.IntType(false, i);
355 const S = std.meta.IntType(true, i);
356 const x = try deserializer.deserializeInt(U);
357 const y = try deserializer.deserializeInt(S);
358 expect(x == @as(U, i));
359 if (i != 0) expect(y == @as(S, -1)) else expect(y == 0);
360 }
361
362 const u8_bit_count = comptime meta.bitCount(u8);
363 //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
364 //and we have each for unsigned and signed, so * 2
365 const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
366 const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
367 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
368
369 expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
370
371 //Verify that empty error set works with serializer.
372 //deserializer is covered by SliceInStream
373 const NullError = io.NullOutStream.Error;
374 var null_out = io.NullOutStream.init();
375 var null_out_stream = &null_out.stream;
376 var null_serializer = io.Serializer(endian, packing, NullError).init(null_out_stream);
377 try null_serializer.serialize(data_mem[0..]);
378 try null_serializer.flush();
379}
380
381test "Serializer/Deserializer Int" {
382 try testIntSerializerDeserializer(.Big, .Byte);
383 try testIntSerializerDeserializer(.Little, .Byte);
384 // TODO these tests are disabled due to tripping an LLVM assertion
385 // https://github.com/ziglang/zig/issues/2019
386 //try testIntSerializerDeserializer(builtin.Endian.Big, true);
387 //try testIntSerializerDeserializer(builtin.Endian.Little, true);
388}
389
390fn testIntSerializerDeserializerInfNaN(
391 comptime endian: builtin.Endian,
392 comptime packing: io.Packing,
393) !void {
394 const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
395 var data_mem: [mem_size]u8 = undefined;
396
397 var out = io.SliceOutStream.init(data_mem[0..]);
398 const OutError = io.SliceOutStream.Error;
399 var out_stream = &out.stream;
400 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
401
402 var in = io.SliceInStream.init(data_mem[0..]);
403 const InError = io.SliceInStream.Error;
404 var in_stream = &in.stream;
405 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
406
407 //@TODO: isInf/isNan not currently implemented for f128.
408 try serializer.serialize(std.math.nan(f16));
409 try serializer.serialize(std.math.inf(f16));
410 try serializer.serialize(std.math.nan(f32));
411 try serializer.serialize(std.math.inf(f32));
412 try serializer.serialize(std.math.nan(f64));
413 try serializer.serialize(std.math.inf(f64));
414 //try serializer.serialize(std.math.nan(f128));
415 //try serializer.serialize(std.math.inf(f128));
416 const nan_check_f16 = try deserializer.deserialize(f16);
417 const inf_check_f16 = try deserializer.deserialize(f16);
418 const nan_check_f32 = try deserializer.deserialize(f32);
419 deserializer.alignToByte();
420 const inf_check_f32 = try deserializer.deserialize(f32);
421 const nan_check_f64 = try deserializer.deserialize(f64);
422 const inf_check_f64 = try deserializer.deserialize(f64);
423 //const nan_check_f128 = try deserializer.deserialize(f128);
424 //const inf_check_f128 = try deserializer.deserialize(f128);
425 expect(std.math.isNan(nan_check_f16));
426 expect(std.math.isInf(inf_check_f16));
427 expect(std.math.isNan(nan_check_f32));
428 expect(std.math.isInf(inf_check_f32));
429 expect(std.math.isNan(nan_check_f64));
430 expect(std.math.isInf(inf_check_f64));
431 //expect(std.math.isNan(nan_check_f128));
432 //expect(std.math.isInf(inf_check_f128));
433}
434
435test "Serializer/Deserializer Int: Inf/NaN" {
436 try testIntSerializerDeserializerInfNaN(.Big, .Byte);
437 try testIntSerializerDeserializerInfNaN(.Little, .Byte);
438 try testIntSerializerDeserializerInfNaN(.Big, .Bit);
439 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
440}
441
442fn testAlternateSerializer(self: var, serializer: var) !void {
443 try serializer.serialize(self.f_f16);
444}
445
446fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
447 const ColorType = enum(u4) {
448 RGB8 = 1,
449 RA16 = 2,
450 R32 = 3,
451 };
452
453 const TagAlign = union(enum(u32)) {
454 A: u8,
455 B: u8,
456 C: u8,
457 };
458
459 const Color = union(ColorType) {
460 RGB8: struct {
461 r: u8,
462 g: u8,
463 b: u8,
464 a: u8,
465 },
466 RA16: struct {
467 r: u16,
468 a: u16,
469 },
470 R32: u32,
471 };
472
473 const PackedStruct = packed struct {
474 f_i3: i3,
475 f_u2: u2,
476 };
477
478 //to test custom serialization
479 const Custom = struct {
480 f_f16: f16,
481 f_unused_u32: u32,
482
483 pub fn deserialize(self: *@This(), deserializer: var) !void {
484 try deserializer.deserializeInto(&self.f_f16);
485 self.f_unused_u32 = 47;
486 }
487
488 pub const serialize = testAlternateSerializer;
489 };
490
491 const MyStruct = struct {
492 f_i3: i3,
493 f_u8: u8,
494 f_tag_align: TagAlign,
495 f_u24: u24,
496 f_i19: i19,
497 f_void: void,
498 f_f32: f32,
499 f_f128: f128,
500 f_packed_0: PackedStruct,
501 f_i7arr: [10]i7,
502 f_of64n: ?f64,
503 f_of64v: ?f64,
504 f_color_type: ColorType,
505 f_packed_1: PackedStruct,
506 f_custom: Custom,
507 f_color: Color,
508 };
509
510 const my_inst = MyStruct{
511 .f_i3 = -1,
512 .f_u8 = 8,
513 .f_tag_align = TagAlign{ .B = 148 },
514 .f_u24 = 24,
515 .f_i19 = 19,
516 .f_void = {},
517 .f_f32 = 32.32,
518 .f_f128 = 128.128,
519 .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
520 .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
521 .f_of64n = null,
522 .f_of64v = 64.64,
523 .f_color_type = ColorType.R32,
524 .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
525 .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
526 .f_color = Color{ .R32 = 123822 },
527 };
528
529 var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
530 var out = io.SliceOutStream.init(data_mem[0..]);
531 const OutError = io.SliceOutStream.Error;
532 var out_stream = &out.stream;
533 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
534
535 var in = io.SliceInStream.init(data_mem[0..]);
536 const InError = io.SliceInStream.Error;
537 var in_stream = &in.stream;
538 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
539
540 try serializer.serialize(my_inst);
541
542 const my_copy = try deserializer.deserialize(MyStruct);
543 expect(meta.eql(my_copy, my_inst));
544}
545
546test "Serializer/Deserializer generic" {
547 if (std.Target.current.os.tag == .windows) {
548 // TODO https://github.com/ziglang/zig/issues/508
549 return error.SkipZigTest;
550 }
551 try testSerializerDeserializer(builtin.Endian.Big, .Byte);
552 try testSerializerDeserializer(builtin.Endian.Little, .Byte);
553 try testSerializerDeserializer(builtin.Endian.Big, .Bit);
554 try testSerializerDeserializer(builtin.Endian.Little, .Bit);
555}
556
557fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
558 const E = enum(u14) {
559 One = 1,
560 Two = 2,
561 };
562
563 const A = struct {
564 e: E,
565 };
566
567 const C = union(E) {
568 One: u14,
569 Two: f16,
570 };
571
572 var data_mem: [4]u8 = undefined;
573 var out = io.SliceOutStream.init(data_mem[0..]);
574 const OutError = io.SliceOutStream.Error;
575 var out_stream = &out.stream;
576 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
577
578 var in = io.SliceInStream.init(data_mem[0..]);
579 const InError = io.SliceInStream.Error;
580 var in_stream = &in.stream;
581 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
582
583 try serializer.serialize(@as(u14, 3));
584 expectError(error.InvalidEnumTag, deserializer.deserialize(A));
585 out.pos = 0;
586 try serializer.serialize(@as(u14, 3));
587 try serializer.serialize(@as(u14, 88));
588 expectError(error.InvalidEnumTag, deserializer.deserialize(C));
589}
590
591test "Deserializer bad data" {
592 try testBadData(.Big, .Byte);
593 try testBadData(.Little, .Byte);
594 try testBadData(.Big, .Bit);
595 try testBadData(.Little, .Bit);
596}
597
598test "c out stream" {
599 if (!builtin.link_libc) return error.SkipZigTest;
600
601 const filename = "tmp_io_test_file.txt";
602 const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
603 defer {
604 _ = std.c.fclose(out_file);
605 fs.cwd().deleteFileC(filename) catch {};
606 }
607
608 const out_stream = &io.COutStream.init(out_file).stream;
609 try out_stream.print("hi: {}\n", .{@as(i32, 123)});
610}
611
612test "File seek ops" {104test "File seek ops" {
613 const tmp_file_name = "temp_test_file.txt";105 const tmp_file_name = "temp_test_file.txt";
614 var file = try fs.cwd().createFile(tmp_file_name, .{});106 var file = try fs.cwd().createFile(tmp_file_name, .{});
...@@ -621,16 +113,16 @@ test "File seek ops" {...@@ -621,16 +113,16 @@ test "File seek ops" {
621113
622 // Seek to the end114 // Seek to the end
623 try file.seekFromEnd(0);115 try file.seekFromEnd(0);
624 std.testing.expect((try file.getPos()) == try file.getEndPos());116 expect((try file.getPos()) == try file.getEndPos());
625 // Negative delta117 // Negative delta
626 try file.seekBy(-4096);118 try file.seekBy(-4096);
627 std.testing.expect((try file.getPos()) == 4096);119 expect((try file.getPos()) == 4096);
628 // Positive delta120 // Positive delta
629 try file.seekBy(10);121 try file.seekBy(10);
630 std.testing.expect((try file.getPos()) == 4106);122 expect((try file.getPos()) == 4106);
631 // Absolute position123 // Absolute position
632 try file.seekTo(1234);124 try file.seekTo(1234);
633 std.testing.expect((try file.getPos()) == 1234);125 expect((try file.getPos()) == 1234);
634}126}
635127
636test "updateTimes" {128test "updateTimes" {
...@@ -647,6 +139,6 @@ test "updateTimes" {...@@ -647,6 +139,6 @@ test "updateTimes" {
647 stat_old.mtime - 5 * std.time.ns_per_s,139 stat_old.mtime - 5 * std.time.ns_per_s,
648 );140 );
649 var stat_new = try file.stat();141 var stat_new = try file.stat();
650 std.testing.expect(stat_new.atime < stat_old.atime);142 expect(stat_new.atime < stat_old.atime);
651 std.testing.expect(stat_new.mtime < stat_old.mtime);143 expect(stat_new.mtime < stat_old.mtime);
652}144}
lib/std/json.zig+5-4
...@@ -10,6 +10,7 @@ const mem = std.mem;...@@ -10,6 +10,7 @@ const mem = std.mem;
10const maxInt = std.math.maxInt;10const maxInt = std.math.maxInt;
1111
12pub const WriteStream = @import("json/write_stream.zig").WriteStream;12pub const WriteStream = @import("json/write_stream.zig").WriteStream;
13pub const writeStream = @import("json/write_stream.zig").writeStream;
1314
14const StringEscapes = union(enum) {15const StringEscapes = union(enum) {
15 None,16 None,
...@@ -2107,9 +2108,9 @@ test "import more json tests" {...@@ -2107,9 +2108,9 @@ test "import more json tests" {
2107test "write json then parse it" {2108test "write json then parse it" {
2108 var out_buffer: [1000]u8 = undefined;2109 var out_buffer: [1000]u8 = undefined;
21092110
2110 var slice_out_stream = std.io.SliceOutStream.init(&out_buffer);2111 var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer);
2111 const out_stream = &slice_out_stream.stream;2112 const out_stream = fixed_buffer_stream.outStream();
2112 var jw = WriteStream(@TypeOf(out_stream).Child, 4).init(out_stream);2113 var jw = writeStream(out_stream, 4);
21132114
2114 try jw.beginObject();2115 try jw.beginObject();
21152116
...@@ -2140,7 +2141,7 @@ test "write json then parse it" {...@@ -2140,7 +2141,7 @@ test "write json then parse it" {
21402141
2141 var parser = Parser.init(testing.allocator, false);2142 var parser = Parser.init(testing.allocator, false);
2142 defer parser.deinit();2143 defer parser.deinit();
2143 var tree = try parser.parse(slice_out_stream.getWritten());2144 var tree = try parser.parse(fixed_buffer_stream.getWritten());
2144 defer tree.deinit();2145 defer tree.deinit();
21452146
2146 testing.expect(tree.root.Object.get("f").?.value.Bool == false);2147 testing.expect(tree.root.Object.get("f").?.value.Bool == false);
lib/std/json/write_stream.zig+26-19
...@@ -30,11 +30,11 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -30,11 +30,11 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
30 /// The string used as spacing.30 /// The string used as spacing.
31 space: []const u8 = " ",31 space: []const u8 = " ",
3232
33 stream: *OutStream,33 stream: OutStream,
34 state_index: usize,34 state_index: usize,
35 state: [max_depth]State,35 state: [max_depth]State,
3636
37 pub fn init(stream: *OutStream) Self {37 pub fn init(stream: OutStream) Self {
38 var self = Self{38 var self = Self{
39 .stream = stream,39 .stream = stream,
40 .state_index = 1,40 .state_index = 1,
...@@ -90,8 +90,8 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -90,8 +90,8 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
90 self.pushState(.Value);90 self.pushState(.Value);
91 try self.indent();91 try self.indent();
92 try self.writeEscapedString(name);92 try self.writeEscapedString(name);
93 try self.stream.write(":");93 try self.stream.writeAll(":");
94 try self.stream.write(self.space);94 try self.stream.writeAll(self.space);
95 },95 },
96 }96 }
97 }97 }
...@@ -134,16 +134,16 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -134,16 +134,16 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
134134
135 pub fn emitNull(self: *Self) !void {135 pub fn emitNull(self: *Self) !void {
136 assert(self.state[self.state_index] == State.Value);136 assert(self.state[self.state_index] == State.Value);
137 try self.stream.write("null");137 try self.stream.writeAll("null");
138 self.popState();138 self.popState();
139 }139 }
140140
141 pub fn emitBool(self: *Self, value: bool) !void {141 pub fn emitBool(self: *Self, value: bool) !void {
142 assert(self.state[self.state_index] == State.Value);142 assert(self.state[self.state_index] == State.Value);
143 if (value) {143 if (value) {
144 try self.stream.write("true");144 try self.stream.writeAll("true");
145 } else {145 } else {
146 try self.stream.write("false");146 try self.stream.writeAll("false");
147 }147 }
148 self.popState();148 self.popState();
149 }149 }
...@@ -188,13 +188,13 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -188,13 +188,13 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
188 try self.stream.writeByte('"');188 try self.stream.writeByte('"');
189 for (string) |s| {189 for (string) |s| {
190 switch (s) {190 switch (s) {
191 '"' => try self.stream.write("\\\""),191 '"' => try self.stream.writeAll("\\\""),
192 '\t' => try self.stream.write("\\t"),192 '\t' => try self.stream.writeAll("\\t"),
193 '\r' => try self.stream.write("\\r"),193 '\r' => try self.stream.writeAll("\\r"),
194 '\n' => try self.stream.write("\\n"),194 '\n' => try self.stream.writeAll("\\n"),
195 8 => try self.stream.write("\\b"),195 8 => try self.stream.writeAll("\\b"),
196 12 => try self.stream.write("\\f"),196 12 => try self.stream.writeAll("\\f"),
197 '\\' => try self.stream.write("\\\\"),197 '\\' => try self.stream.writeAll("\\\\"),
198 else => try self.stream.writeByte(s),198 else => try self.stream.writeByte(s),
199 }199 }
200 }200 }
...@@ -231,10 +231,10 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -231,10 +231,10 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
231231
232 fn indent(self: *Self) !void {232 fn indent(self: *Self) !void {
233 assert(self.state_index >= 1);233 assert(self.state_index >= 1);
234 try self.stream.write(self.newline);234 try self.stream.writeAll(self.newline);
235 var i: usize = 0;235 var i: usize = 0;
236 while (i < self.state_index - 1) : (i += 1) {236 while (i < self.state_index - 1) : (i += 1) {
237 try self.stream.write(self.one_indent);237 try self.stream.writeAll(self.one_indent);
238 }238 }
239 }239 }
240240
...@@ -249,15 +249,22 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -249,15 +249,22 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
249 };249 };
250}250}
251251
252pub fn writeStream(
253 out_stream: var,
254 comptime max_depth: usize,
255) WriteStream(@TypeOf(out_stream), max_depth) {
256 return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
257}
258
252test "json write stream" {259test "json write stream" {
253 var out_buf: [1024]u8 = undefined;260 var out_buf: [1024]u8 = undefined;
254 var slice_stream = std.io.SliceOutStream.init(&out_buf);261 var slice_stream = std.io.fixedBufferStream(&out_buf);
255 const out = &slice_stream.stream;262 const out = slice_stream.outStream();
256263
257 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);264 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
258 defer arena_allocator.deinit();265 defer arena_allocator.deinit();
259266
260 var w = std.json.WriteStream(@TypeOf(out).Child, 10).init(out);267 var w = std.json.writeStream(out, 10);
261 try w.emitJson(try getJson(&arena_allocator.allocator));268 try w.emitJson(try getJson(&arena_allocator.allocator));
262269
263 const result = slice_stream.getWritten();270 const result = slice_stream.getWritten();
lib/std/net.zig+2-2
...@@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts(...@@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts(
816 };816 };
817 defer file.close();817 defer file.close();
818818
819 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;819 const stream = std.io.bufferedInStream(file.inStream()).inStream();
820 var line_buf: [512]u8 = undefined;820 var line_buf: [512]u8 = undefined;
821 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {821 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
822 error.StreamTooLong => blk: {822 error.StreamTooLong => blk: {
...@@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {...@@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
1010 };1010 };
1011 defer file.close();1011 defer file.close();
10121012
1013 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;1013 const stream = std.io.bufferedInStream(file.inStream()).inStream();
1014 var line_buf: [512]u8 = undefined;1014 var line_buf: [512]u8 = undefined;
1015 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {1015 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
1016 error.StreamTooLong => blk: {1016 error.StreamTooLong => blk: {
lib/std/net/test.zig+1-1
...@@ -113,6 +113,6 @@ fn testClient(addr: net.Address) anyerror!void {...@@ -113,6 +113,6 @@ fn testClient(addr: net.Address) anyerror!void {
113fn testServer(server: *net.StreamServer) anyerror!void {113fn testServer(server: *net.StreamServer) anyerror!void {
114 var client = try server.accept();114 var client = try server.accept();
115115
116 const stream = &client.file.outStream().stream;116 const stream = client.file.outStream();
117 try stream.print("hello from server\n", .{});117 try stream.print("hello from server\n", .{});
118}118}
lib/std/os.zig+1-1
...@@ -176,7 +176,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {...@@ -176,7 +176,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
176 .io_mode = .blocking,176 .io_mode = .blocking,
177 .async_block_allowed = std.fs.File.async_block_allowed_yes,177 .async_block_allowed = std.fs.File.async_block_allowed_yes,
178 };178 };
179 const stream = &file.inStream().stream;179 const stream = file.inStream();
180 stream.readNoEof(buf) catch return error.Unexpected;180 stream.readNoEof(buf) catch return error.Unexpected;
181}181}
182182
lib/std/os/test.zig+34-9
...@@ -95,15 +95,41 @@ test "sendfile" {...@@ -95,15 +95,41 @@ test "sendfile" {
95 },95 },
96 };96 };
9797
98 var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;98 var written_buf: [100]u8 = undefined;
99 try dest_file.writeFileAll(src_file, .{99 try dest_file.writeFileAll(src_file, .{
100 .in_offset = 1,100 .in_offset = 1,
101 .in_len = 10,101 .in_len = 10,
102 .headers_and_trailers = &hdtr,102 .headers_and_trailers = &hdtr,
103 .header_count = 2,103 .header_count = 2,
104 });104 });
105 try dest_file.preadAll(&written_buf, 0);105 const amt = try dest_file.preadAll(&written_buf, 0);
106 expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));106 expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
107}
108
109test "fs.copyFile" {
110 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
111 const src_file = "tmp_test_copy_file.txt";
112 const dest_file = "tmp_test_copy_file2.txt";
113 const dest_file2 = "tmp_test_copy_file3.txt";
114
115 try fs.cwd().writeFile(src_file, data);
116 defer fs.cwd().deleteFile(src_file) catch {};
117
118 try fs.copyFile(src_file, dest_file);
119 defer fs.cwd().deleteFile(dest_file) catch {};
120
121 try fs.copyFileMode(src_file, dest_file2, File.default_mode);
122 defer fs.cwd().deleteFile(dest_file2) catch {};
123
124 try expectFileContents(dest_file, data);
125 try expectFileContents(dest_file2, data);
126}
127
128fn expectFileContents(file_path: []const u8, data: []const u8) !void {
129 const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000);
130 defer testing.allocator.free(contents);
131
132 testing.expectEqualSlices(u8, data, contents);
107}133}
108134
109test "std.Thread.getCurrentId" {135test "std.Thread.getCurrentId" {
...@@ -354,8 +380,7 @@ test "mmap" {...@@ -354,8 +380,7 @@ test "mmap" {
354 const file = try fs.cwd().createFile(test_out_file, .{});380 const file = try fs.cwd().createFile(test_out_file, .{});
355 defer file.close();381 defer file.close();
356382
357 var out_stream = file.outStream();383 const stream = file.outStream();
358 const stream = &out_stream.stream;
359384
360 var i: u32 = 0;385 var i: u32 = 0;
361 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {386 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
...@@ -378,8 +403,8 @@ test "mmap" {...@@ -378,8 +403,8 @@ test "mmap" {
378 );403 );
379 defer os.munmap(data);404 defer os.munmap(data);
380405
381 var mem_stream = io.SliceInStream.init(data);406 var mem_stream = io.fixedBufferStream(data);
382 const stream = &mem_stream.stream;407 const stream = mem_stream.inStream();
383408
384 var i: u32 = 0;409 var i: u32 = 0;
385 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {410 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
...@@ -402,8 +427,8 @@ test "mmap" {...@@ -402,8 +427,8 @@ test "mmap" {
402 );427 );
403 defer os.munmap(data);428 defer os.munmap(data);
404429
405 var mem_stream = io.SliceInStream.init(data);430 var mem_stream = io.fixedBufferStream(data);
406 const stream = &mem_stream.stream;431 const stream = mem_stream.inStream();
407432
408 var i: u32 = alloc_size / 2 / @sizeOf(u32);433 var i: u32 = alloc_size / 2 / @sizeOf(u32);
409 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {434 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
lib/std/os/windows.zig+1
...@@ -407,6 +407,7 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz...@@ -407,6 +407,7 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz
407 switch (kernel32.GetLastError()) {407 switch (kernel32.GetLastError()) {
408 .OPERATION_ABORTED => continue,408 .OPERATION_ABORTED => continue,
409 .BROKEN_PIPE => return index,409 .BROKEN_PIPE => return index,
410 .HANDLE_EOF => return index,
410 else => |err| return unexpectedError(err),411 else => |err| return unexpectedError(err),
411 }412 }
412 }413 }
lib/std/pdb.zig+8-16
...@@ -495,8 +495,7 @@ const Msf = struct {...@@ -495,8 +495,7 @@ const Msf = struct {
495 streams: []MsfStream,495 streams: []MsfStream,
496496
497 fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void {497 fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void {
498 var file_stream = file.inStream();498 const in = file.inStream();
499 const in = &file_stream.stream;
500499
501 const superblock = try in.readStruct(SuperBlock);500 const superblock = try in.readStruct(SuperBlock);
502501
...@@ -529,7 +528,7 @@ const Msf = struct {...@@ -529,7 +528,7 @@ const Msf = struct {
529 );528 );
530529
531 const begin = self.directory.pos;530 const begin = self.directory.pos;
532 const stream_count = try self.directory.stream.readIntLittle(u32);531 const stream_count = try self.directory.inStream().readIntLittle(u32);
533 const stream_sizes = try allocator.alloc(u32, stream_count);532 const stream_sizes = try allocator.alloc(u32, stream_count);
534 defer allocator.free(stream_sizes);533 defer allocator.free(stream_sizes);
535534
...@@ -538,7 +537,7 @@ const Msf = struct {...@@ -538,7 +537,7 @@ const Msf = struct {
538 // and must be taken into account when resolving stream indices.537 // and must be taken into account when resolving stream indices.
539 const Nil = 0xFFFFFFFF;538 const Nil = 0xFFFFFFFF;
540 for (stream_sizes) |*s, i| {539 for (stream_sizes) |*s, i| {
541 const size = try self.directory.stream.readIntLittle(u32);540 const size = try self.directory.inStream().readIntLittle(u32);
542 s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize);541 s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize);
543 }542 }
544543
...@@ -553,7 +552,7 @@ const Msf = struct {...@@ -553,7 +552,7 @@ const Msf = struct {
553 var blocks = try allocator.alloc(u32, size);552 var blocks = try allocator.alloc(u32, size);
554 var j: u32 = 0;553 var j: u32 = 0;
555 while (j < size) : (j += 1) {554 while (j < size) : (j += 1) {
556 const block_id = try self.directory.stream.readIntLittle(u32);555 const block_id = try self.directory.inStream().readIntLittle(u32);
557 const n = (block_id % superblock.BlockSize);556 const n = (block_id % superblock.BlockSize);
558 // 0 is for SuperBlock, 1 and 2 for FPMs.557 // 0 is for SuperBlock, 1 and 2 for FPMs.
559 if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos())558 if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos())
...@@ -632,11 +631,7 @@ const MsfStream = struct {...@@ -632,11 +631,7 @@ const MsfStream = struct {
632 blocks: []u32 = undefined,631 blocks: []u32 = undefined,
633 block_size: u32 = undefined,632 block_size: u32 = undefined,
634633
635 /// Implementation of InStream trait for Pdb.MsfStream
636 stream: Stream = undefined,
637
638 pub const Error = @TypeOf(read).ReturnType.ErrorSet;634 pub const Error = @TypeOf(read).ReturnType.ErrorSet;
639 pub const Stream = io.InStream(Error);
640635
641 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {636 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
642 const stream = MsfStream{637 const stream = MsfStream{
...@@ -644,7 +639,6 @@ const MsfStream = struct {...@@ -644,7 +639,6 @@ const MsfStream = struct {
644 .pos = 0,639 .pos = 0,
645 .blocks = blocks,640 .blocks = blocks,
646 .block_size = block_size,641 .block_size = block_size,
647 .stream = Stream{ .readFn = readFn },
648 };642 };
649643
650 return stream;644 return stream;
...@@ -653,7 +647,7 @@ const MsfStream = struct {...@@ -653,7 +647,7 @@ const MsfStream = struct {
653 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {647 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
654 var list = ArrayList(u8).init(allocator);648 var list = ArrayList(u8).init(allocator);
655 while (true) {649 while (true) {
656 const byte = try self.stream.readByte();650 const byte = try self.inStream().readByte();
657 if (byte == 0) {651 if (byte == 0) {
658 return list.toSlice();652 return list.toSlice();
659 }653 }
...@@ -667,8 +661,7 @@ const MsfStream = struct {...@@ -667,8 +661,7 @@ const MsfStream = struct {
667 var offset = self.pos % self.block_size;661 var offset = self.pos % self.block_size;
668662
669 try self.in_file.seekTo(block * self.block_size + offset);663 try self.in_file.seekTo(block * self.block_size + offset);
670 var file_stream = self.in_file.inStream();664 const in = self.in_file.inStream();
671 const in = &file_stream.stream;
672665
673 var size: usize = 0;666 var size: usize = 0;
674 var rem_buffer = buffer;667 var rem_buffer = buffer;
...@@ -715,8 +708,7 @@ const MsfStream = struct {...@@ -715,8 +708,7 @@ const MsfStream = struct {
715 return block * self.block_size + offset;708 return block * self.block_size + offset;
716 }709 }
717710
718 fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {711 fn inStream(self: *MsfStream) std.io.InStream(*MsfStream, Error, read) {
719 const self = @fieldParentPtr(MsfStream, "stream", in_stream);712 return .{ .context = self };
720 return self.read(buffer);
721 }713 }
722};714};
lib/std/progress.zig+1-1
...@@ -177,7 +177,7 @@ pub const Progress = struct {...@@ -177,7 +177,7 @@ pub const Progress = struct {
177 pub fn log(self: *Progress, comptime format: []const u8, args: var) void {177 pub fn log(self: *Progress, comptime format: []const u8, args: var) void {
178 const file = self.terminal orelse return;178 const file = self.terminal orelse return;
179 self.refresh();179 self.refresh();
180 file.outStream().stream.print(format, args) catch {180 file.outStream().print(format, args) catch {
181 self.terminal = null;181 self.terminal = null;
182 return;182 return;
183 };183 };
lib/std/special/build_runner.zig+4-4
...@@ -42,8 +42,8 @@ pub fn main() !void {...@@ -42,8 +42,8 @@ pub fn main() !void {
4242
43 var targets = ArrayList([]const u8).init(allocator);43 var targets = ArrayList([]const u8).init(allocator);
4444
45 const stderr_stream = &io.getStdErr().outStream().stream;45 const stderr_stream = io.getStdErr().outStream();
46 const stdout_stream = &io.getStdOut().outStream().stream;46 const stdout_stream = io.getStdOut().outStream();
4747
48 while (nextArg(args, &arg_idx)) |arg| {48 while (nextArg(args, &arg_idx)) |arg| {
49 if (mem.startsWith(u8, arg, "-D")) {49 if (mem.startsWith(u8, arg, "-D")) {
...@@ -159,7 +159,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {...@@ -159,7 +159,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
159 try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });159 try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
160 }160 }
161161
162 try out_stream.write(162 try out_stream.writeAll(
163 \\163 \\
164 \\General Options:164 \\General Options:
165 \\ --help Print this help and exit165 \\ --help Print this help and exit
...@@ -184,7 +184,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {...@@ -184,7 +184,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
184 }184 }
185 }185 }
186186
187 try out_stream.write(187 try out_stream.writeAll(
188 \\188 \\
189 \\Advanced Options:189 \\Advanced Options:
190 \\ --build-file [file] Override path to build.zig190 \\ --build-file [file] Override path to build.zig
lib/std/std.zig-1
...@@ -5,7 +5,6 @@ pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;...@@ -5,7 +5,6 @@ pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
5pub const BufMap = @import("buf_map.zig").BufMap;5pub const BufMap = @import("buf_map.zig").BufMap;
6pub const BufSet = @import("buf_set.zig").BufSet;6pub const BufSet = @import("buf_set.zig").BufSet;
7pub const Buffer = @import("buffer.zig").Buffer;7pub const Buffer = @import("buffer.zig").Buffer;
8pub const BufferOutStream = @import("io.zig").BufferOutStream;
9pub const ChildProcess = @import("child_process.zig").ChildProcess;8pub const ChildProcess = @import("child_process.zig").ChildProcess;
10pub const DynLib = @import("dynamic_library.zig").DynLib;9pub const DynLib = @import("dynamic_library.zig").DynLib;
11pub const HashMap = @import("hash_map.zig").HashMap;10pub const HashMap = @import("hash_map.zig").HashMap;
lib/std/zig/ast.zig+1-1
...@@ -375,7 +375,7 @@ pub const Error = union(enum) {...@@ -375,7 +375,7 @@ pub const Error = union(enum) {
375 token: TokenIndex,375 token: TokenIndex,
376376
377 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {377 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
378 return stream.write(msg);378 return stream.writeAll(msg);
379 }379 }
380 };380 };
381 }381 }
lib/std/zig/parser_test.zig+5-6
...@@ -2809,7 +2809,7 @@ const maxInt = std.math.maxInt;...@@ -2809,7 +2809,7 @@ const maxInt = std.math.maxInt;
2809var fixed_buffer_mem: [100 * 1024]u8 = undefined;2809var fixed_buffer_mem: [100 * 1024]u8 = undefined;
28102810
2811fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {2811fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
2812 const stderr = &io.getStdErr().outStream().stream;2812 const stderr = io.getStdErr().outStream();
28132813
2814 const tree = try std.zig.parse(allocator, source);2814 const tree = try std.zig.parse(allocator, source);
2815 defer tree.deinit();2815 defer tree.deinit();
...@@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b...@@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
2824 {2824 {
2825 var i: usize = 0;2825 var i: usize = 0;
2826 while (i < loc.column) : (i += 1) {2826 while (i < loc.column) : (i += 1) {
2827 try stderr.write(" ");2827 try stderr.writeAll(" ");
2828 }2828 }
2829 }2829 }
2830 {2830 {
2831 const caret_count = token.end - token.start;2831 const caret_count = token.end - token.start;
2832 var i: usize = 0;2832 var i: usize = 0;
2833 while (i < caret_count) : (i += 1) {2833 while (i < caret_count) : (i += 1) {
2834 try stderr.write("~");2834 try stderr.writeAll("~");
2835 }2835 }
2836 }2836 }
2837 try stderr.write("\n");2837 try stderr.writeAll("\n");
2838 }2838 }
2839 if (tree.errors.len != 0) {2839 if (tree.errors.len != 0) {
2840 return error.ParseError;2840 return error.ParseError;
...@@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b...@@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
2843 var buffer = try std.Buffer.initSize(allocator, 0);2843 var buffer = try std.Buffer.initSize(allocator, 0);
2844 errdefer buffer.deinit();2844 errdefer buffer.deinit();
28452845
2846 var buffer_out_stream = io.BufferOutStream.init(&buffer);2846 anything_changed.* = try std.zig.render(allocator, buffer.outStream(), tree);
2847 anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, tree);
2848 return buffer.toOwnedSlice();2847 return buffer.toOwnedSlice();
2849}2848}
28502849
lib/std/zig/render.zig+66-73
...@@ -12,64 +12,58 @@ pub const Error = error{...@@ -12,64 +12,58 @@ pub const Error = error{
12};12};
1313
14/// Returns whether anything changed14/// Returns whether anything changed
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Child.Error || Error)!bool {15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
16 comptime assert(@typeInfo(@TypeOf(stream)) == .Pointer);
17
18 var anything_changed: bool = false;
19
20 // make a passthrough stream that checks whether something changed16 // make a passthrough stream that checks whether something changed
21 const MyStream = struct {17 const MyStream = struct {
22 const MyStream = @This();18 const MyStream = @This();
23 const StreamError = @TypeOf(stream).Child.Error;19 const StreamError = @TypeOf(stream).Error;
24 const Stream = std.io.OutStream(StreamError);
2520
26 anything_changed_ptr: *bool,
27 child_stream: @TypeOf(stream),21 child_stream: @TypeOf(stream),
28 stream: Stream,22 anything_changed: bool,
29 source_index: usize,23 source_index: usize,
30 source: []const u8,24 source: []const u8,
3125
32 fn write(iface_stream: *Stream, bytes: []const u8) StreamError!usize {26 fn write(self: *MyStream, bytes: []const u8) StreamError!usize {
33 const self = @fieldParentPtr(MyStream, "stream", iface_stream);27 if (!self.anything_changed) {
34
35 if (!self.anything_changed_ptr.*) {
36 const end = self.source_index + bytes.len;28 const end = self.source_index + bytes.len;
37 if (end > self.source.len) {29 if (end > self.source.len) {
38 self.anything_changed_ptr.* = true;30 self.anything_changed = true;
39 } else {31 } else {
40 const src_slice = self.source[self.source_index..end];32 const src_slice = self.source[self.source_index..end];
41 self.source_index += bytes.len;33 self.source_index += bytes.len;
42 if (!mem.eql(u8, bytes, src_slice)) {34 if (!mem.eql(u8, bytes, src_slice)) {
43 self.anything_changed_ptr.* = true;35 self.anything_changed = true;
44 }36 }
45 }37 }
46 }38 }
4739
48 return self.child_stream.writeOnce(bytes);40 return self.child_stream.write(bytes);
49 }41 }
50 };42 };
51 var my_stream = MyStream{43 var my_stream = MyStream{
52 .stream = MyStream.Stream{ .writeFn = MyStream.write },
53 .child_stream = stream,44 .child_stream = stream,
54 .anything_changed_ptr = &anything_changed,45 .anything_changed = false,
55 .source_index = 0,46 .source_index = 0,
56 .source = tree.source,47 .source = tree.source,
57 };48 };
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) {55 if (my_stream.source_index != my_stream.source.len) {
62 anything_changed = true;56 my_stream.anything_changed = true;
63 }57 }
6458
65 return anything_changed;59 return my_stream.anything_changed;
66}60}
6761
68fn renderRoot(62fn renderRoot(
69 allocator: *mem.Allocator,63 allocator: *mem.Allocator,
70 stream: var,64 stream: var,
71 tree: *ast.Tree,65 tree: *ast.Tree,
72) (@TypeOf(stream).Child.Error || Error)!void {66) (@TypeOf(stream).Error || Error)!void {
73 var tok_it = tree.tokens.iterator(0);67 var tok_it = tree.tokens.iterator(0);
7468
75 // render all the line comments at the beginning of the file69 // render all the line comments at the beginning of the file
...@@ -189,7 +183,7 @@ fn renderRoot(...@@ -189,7 +183,7 @@ fn renderRoot(
189 }183 }
190}184}
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 {
193 const first_token = node.firstToken();187 const first_token = node.firstToken();
194 var prev_token = first_token;188 var prev_token = first_token;
195 if (prev_token == 0) return;189 if (prev_token == 0) return;
...@@ -204,11 +198,11 @@ fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *as...@@ -204,11 +198,11 @@ fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *as
204 }198 }
205}199}
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 {
208 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);202 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
209}203}
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 {
212 switch (decl.id) {206 switch (decl.id) {
213 .FnProto => {207 .FnProto => {
214 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);208 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
...@@ -343,7 +337,7 @@ fn renderExpression(...@@ -343,7 +337,7 @@ fn renderExpression(
343 start_col: *usize,337 start_col: *usize,
344 base: *ast.Node,338 base: *ast.Node,
345 space: Space,339 space: Space,
346) (@TypeOf(stream).Child.Error || Error)!void {340) (@TypeOf(stream).Error || Error)!void {
347 switch (base.id) {341 switch (base.id) {
348 .Identifier => {342 .Identifier => {
349 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);343 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
...@@ -449,9 +443,9 @@ fn renderExpression(...@@ -449,9 +443,9 @@ fn renderExpression(
449 switch (op_tok_id) {443 switch (op_tok_id) {
450 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),444 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
451 .LBracket => if (tree.tokens.at(prefix_op_node.op_token + 2).id == .Identifier)445 .LBracket => if (tree.tokens.at(prefix_op_node.op_token + 2).id == .Identifier)
452 try stream.write("[*c")446 try stream.writeAll("[*c")
453 else447 else
454 try stream.write("[*"),448 try stream.writeAll("[*"),
455 else => unreachable,449 else => unreachable,
456 }450 }
457 if (ptr_info.sentinel) |sentinel| {451 if (ptr_info.sentinel) |sentinel| {
...@@ -757,7 +751,7 @@ fn renderExpression(...@@ -757,7 +751,7 @@ fn renderExpression(
757 while (it.next()) |field_init| {751 while (it.next()) |field_init| {
758 var find_stream = FindByteOutStream.init('\n');752 var find_stream = FindByteOutStream.init('\n');
759 var dummy_col: usize = 0;753 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);
761 if (find_stream.byte_found) break :blk false;755 if (find_stream.byte_found) break :blk false;
762 }756 }
763 break :blk true;757 break :blk true;
...@@ -909,8 +903,7 @@ fn renderExpression(...@@ -909,8 +903,7 @@ fn renderExpression(
909 var column_widths = widths[widths.len - row_size ..];903 var column_widths = widths[widths.len - row_size ..];
910904
911 // Null stream for counting the printed length of each expression905 // Null stream for counting the printed length of each expression
912 var null_stream = std.io.NullOutStream.init();906 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
913 var counting_stream = std.io.CountingOutStream(std.io.NullOutStream.Error).init(&null_stream.stream);
914907
915 var it = exprs.iterator(0);908 var it = exprs.iterator(0);
916 var i: usize = 0;909 var i: usize = 0;
...@@ -918,7 +911,7 @@ fn renderExpression(...@@ -918,7 +911,7 @@ fn renderExpression(
918 while (it.next()) |expr| : (i += 1) {911 while (it.next()) |expr| : (i += 1) {
919 counting_stream.bytes_written = 0;912 counting_stream.bytes_written = 0;
920 var dummy_col: usize = 0;913 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);
922 const width = @intCast(usize, counting_stream.bytes_written);915 const width = @intCast(usize, counting_stream.bytes_written);
923 const col = i % row_size;916 const col = i % row_size;
924 column_widths[col] = std.math.max(column_widths[col], width);917 column_widths[col] = std.math.max(column_widths[col], width);
...@@ -1336,7 +1329,7 @@ fn renderExpression(...@@ -1336,7 +1329,7 @@ fn renderExpression(
13361329
1337 // TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/13481330 // TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348
1338 if (mem.eql(u8, tree.tokenSlicePtr(tree.tokens.at(builtin_call.builtin_token)), "@typeOf")) {1331 if (mem.eql(u8, tree.tokenSlicePtr(tree.tokens.at(builtin_call.builtin_token)), "@typeOf")) {
1339 try stream.write("@TypeOf");1332 try stream.writeAll("@TypeOf");
1340 } else {1333 } else {
1341 try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name1334 try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name
1342 }1335 }
...@@ -1505,9 +1498,9 @@ fn renderExpression(...@@ -1505,9 +1498,9 @@ fn renderExpression(
1505 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);1498 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
1506 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )1499 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
1507 } else if (cc_rewrite_str) |str| {1500 } else if (cc_rewrite_str) |str| {
1508 try stream.write("callconv(");1501 try stream.writeAll("callconv(");
1509 try stream.write(mem.toSliceConst(u8, str));1502 try stream.writeAll(mem.toSliceConst(u8, str));
1510 try stream.write(") ");1503 try stream.writeAll(") ");
1511 }1504 }
15121505
1513 switch (fn_proto.return_type) {1506 switch (fn_proto.return_type) {
...@@ -1997,11 +1990,11 @@ fn renderExpression(...@@ -1997,11 +1990,11 @@ fn renderExpression(
1997 .AsmInput => {1990 .AsmInput => {
1998 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);1991 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
19991992
2000 try stream.write("[");1993 try stream.writeAll("[");
2001 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);1994 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);
2002 try stream.write("] ");1995 try stream.writeAll("] ");
2003 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);1996 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);
2004 try stream.write(" (");1997 try stream.writeAll(" (");
2005 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);1998 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);
2006 return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )1999 return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )
2007 },2000 },
...@@ -2009,18 +2002,18 @@ fn renderExpression(...@@ -2009,18 +2002,18 @@ fn renderExpression(
2009 .AsmOutput => {2002 .AsmOutput => {
2010 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);2003 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
20112004
2012 try stream.write("[");2005 try stream.writeAll("[");
2013 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);2006 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);
2014 try stream.write("] ");2007 try stream.writeAll("] ");
2015 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);2008 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);
2016 try stream.write(" (");2009 try stream.writeAll(" (");
20172010
2018 switch (asm_output.kind) {2011 switch (asm_output.kind) {
2019 ast.Node.AsmOutput.Kind.Variable => |variable_name| {2012 ast.Node.AsmOutput.Kind.Variable => |variable_name| {
2020 try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None);2013 try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None);
2021 },2014 },
2022 ast.Node.AsmOutput.Kind.Return => |return_type| {2015 ast.Node.AsmOutput.Kind.Return => |return_type| {
2023 try stream.write("-> ");2016 try stream.writeAll("-> ");
2024 try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);2017 try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);
2025 },2018 },
2026 }2019 }
...@@ -2052,7 +2045,7 @@ fn renderVarDecl(...@@ -2052,7 +2045,7 @@ fn renderVarDecl(
2052 indent: usize,2045 indent: usize,
2053 start_col: *usize,2046 start_col: *usize,
2054 var_decl: *ast.Node.VarDecl,2047 var_decl: *ast.Node.VarDecl,
2055) (@TypeOf(stream).Child.Error || Error)!void {2048) (@TypeOf(stream).Error || Error)!void {
2056 if (var_decl.visib_token) |visib_token| {2049 if (var_decl.visib_token) |visib_token| {
2057 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub2050 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
2058 }2051 }
...@@ -2125,7 +2118,7 @@ fn renderParamDecl(...@@ -2125,7 +2118,7 @@ fn renderParamDecl(
2125 start_col: *usize,2118 start_col: *usize,
2126 base: *ast.Node,2119 base: *ast.Node,
2127 space: Space,2120 space: Space,
2128) (@TypeOf(stream).Child.Error || Error)!void {2121) (@TypeOf(stream).Error || Error)!void {
2129 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);2122 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
21302123
2131 try renderDocComments(tree, stream, param_decl, indent, start_col);2124 try renderDocComments(tree, stream, param_decl, indent, start_col);
...@@ -2154,7 +2147,7 @@ fn renderStatement(...@@ -2154,7 +2147,7 @@ fn renderStatement(
2154 indent: usize,2147 indent: usize,
2155 start_col: *usize,2148 start_col: *usize,
2156 base: *ast.Node,2149 base: *ast.Node,
2157) (@TypeOf(stream).Child.Error || Error)!void {2150) (@TypeOf(stream).Error || Error)!void {
2158 switch (base.id) {2151 switch (base.id) {
2159 .VarDecl => {2152 .VarDecl => {
2160 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);2153 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
...@@ -2193,7 +2186,7 @@ fn renderTokenOffset(...@@ -2193,7 +2186,7 @@ fn renderTokenOffset(
2193 start_col: *usize,2186 start_col: *usize,
2194 space: Space,2187 space: Space,
2195 token_skip_bytes: usize,2188 token_skip_bytes: usize,
2196) (@TypeOf(stream).Child.Error || Error)!void {2189) (@TypeOf(stream).Error || Error)!void {
2197 if (space == Space.BlockStart) {2190 if (space == Space.BlockStart) {
2198 if (start_col.* < indent + indent_delta)2191 if (start_col.* < indent + indent_delta)
2199 return renderToken(tree, stream, token_index, indent, start_col, Space.Space);2192 return renderToken(tree, stream, token_index, indent, start_col, Space.Space);
...@@ -2204,7 +2197,7 @@ fn renderTokenOffset(...@@ -2204,7 +2197,7 @@ fn renderTokenOffset(
2204 }2197 }
22052198
2206 var token = tree.tokens.at(token_index);2199 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
2209 if (space == Space.NoComment)2202 if (space == Space.NoComment)
2210 return;2203 return;
...@@ -2214,15 +2207,15 @@ fn renderTokenOffset(...@@ -2214,15 +2207,15 @@ fn renderTokenOffset(
2214 if (space == Space.Comma) switch (next_token.id) {2207 if (space == Space.Comma) switch (next_token.id) {
2215 .Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),2208 .Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),
2216 .LineComment => {2209 .LineComment => {
2217 try stream.write(", ");2210 try stream.writeAll(", ");
2218 return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);2211 return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);
2219 },2212 },
2220 else => {2213 else => {
2221 if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == .MultilineStringLiteralLine) {2214 if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == .MultilineStringLiteralLine) {
2222 try stream.write(",");2215 try stream.writeAll(",");
2223 return;2216 return;
2224 } else {2217 } else {
2225 try stream.write(",\n");2218 try stream.writeAll(",\n");
2226 start_col.* = 0;2219 start_col.* = 0;
2227 return;2220 return;
2228 }2221 }
...@@ -2246,7 +2239,7 @@ fn renderTokenOffset(...@@ -2246,7 +2239,7 @@ fn renderTokenOffset(
2246 if (next_token.id == .MultilineStringLiteralLine) {2239 if (next_token.id == .MultilineStringLiteralLine) {
2247 return;2240 return;
2248 } else {2241 } else {
2249 try stream.write("\n");2242 try stream.writeAll("\n");
2250 start_col.* = 0;2243 start_col.* = 0;
2251 return;2244 return;
2252 }2245 }
...@@ -2309,7 +2302,7 @@ fn renderTokenOffset(...@@ -2309,7 +2302,7 @@ fn renderTokenOffset(
2309 if (next_token.id == .MultilineStringLiteralLine) {2302 if (next_token.id == .MultilineStringLiteralLine) {
2310 return;2303 return;
2311 } else {2304 } else {
2312 try stream.write("\n");2305 try stream.writeAll("\n");
2313 start_col.* = 0;2306 start_col.* = 0;
2314 return;2307 return;
2315 }2308 }
...@@ -2327,7 +2320,7 @@ fn renderTokenOffset(...@@ -2327,7 +2320,7 @@ fn renderTokenOffset(
2327 const newline_count = if (loc.line == 1) @as(u8, 1) else @as(u8, 2);2320 const newline_count = if (loc.line == 1) @as(u8, 1) else @as(u8, 2);
2328 try stream.writeByteNTimes('\n', newline_count);2321 try stream.writeByteNTimes('\n', newline_count);
2329 try stream.writeByteNTimes(' ', indent);2322 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
2332 offset += 1;2325 offset += 1;
2333 token = next_token;2326 token = next_token;
...@@ -2338,7 +2331,7 @@ fn renderTokenOffset(...@@ -2338,7 +2331,7 @@ fn renderTokenOffset(
2338 if (next_token.id == .MultilineStringLiteralLine) {2331 if (next_token.id == .MultilineStringLiteralLine) {
2339 return;2332 return;
2340 } else {2333 } else {
2341 try stream.write("\n");2334 try stream.writeAll("\n");
2342 start_col.* = 0;2335 start_col.* = 0;
2343 return;2336 return;
2344 }2337 }
...@@ -2381,7 +2374,7 @@ fn renderToken(...@@ -2381,7 +2374,7 @@ fn renderToken(
2381 indent: usize,2374 indent: usize,
2382 start_col: *usize,2375 start_col: *usize,
2383 space: Space,2376 space: Space,
2384) (@TypeOf(stream).Child.Error || Error)!void {2377) (@TypeOf(stream).Error || Error)!void {
2385 return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);2378 return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);
2386}2379}
23872380
...@@ -2391,7 +2384,7 @@ fn renderDocComments(...@@ -2391,7 +2384,7 @@ fn renderDocComments(
2391 node: var,2384 node: var,
2392 indent: usize,2385 indent: usize,
2393 start_col: *usize,2386 start_col: *usize,
2394) (@TypeOf(stream).Child.Error || Error)!void {2387) (@TypeOf(stream).Error || Error)!void {
2395 const comment = node.doc_comments orelse return;2388 const comment = node.doc_comments orelse return;
2396 var it = comment.lines.iterator(0);2389 var it = comment.lines.iterator(0);
2397 const first_token = node.firstToken();2390 const first_token = node.firstToken();
...@@ -2401,7 +2394,7 @@ fn renderDocComments(...@@ -2401,7 +2394,7 @@ fn renderDocComments(
2401 try stream.writeByteNTimes(' ', indent);2394 try stream.writeByteNTimes(' ', indent);
2402 } else {2395 } else {
2403 try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.NoComment);2396 try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.NoComment);
2404 try stream.write("\n");2397 try stream.writeAll("\n");
2405 try stream.writeByteNTimes(' ', indent);2398 try stream.writeByteNTimes(' ', indent);
2406 }2399 }
2407 }2400 }
...@@ -2427,27 +2420,23 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {...@@ -2427,27 +2420,23 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
2427 };2420 };
2428}2421}
24292422
2430// An OutStream that returns whether the given character has been written to it.2423/// A `std.io.OutStream` that returns whether the given character has been written to it.
2431// The contents are not written to anything.2424/// The contents are not written to anything.
2432const FindByteOutStream = struct {2425const FindByteOutStream = struct {
2433 const Self = FindByteOutStream;
2434 pub const Error = error{};
2435 pub const Stream = std.io.OutStream(Error);
2436
2437 stream: Stream,
2438 byte_found: bool,2426 byte_found: bool,
2439 byte: u8,2427 byte: u8,
24402428
2441 pub fn init(byte: u8) Self {2429 pub const Error = error{};
2442 return Self{2430 pub const OutStream = std.io.OutStream(*FindByteOutStream, Error, write);
2443 .stream = Stream{ .writeFn = writeFn },2431
2432 pub fn init(byte: u8) FindByteOutStream {
2433 return FindByteOutStream{
2444 .byte = byte,2434 .byte = byte,
2445 .byte_found = false,2435 .byte_found = false,
2446 };2436 };
2447 }2437 }
24482438
2449 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {2439 pub fn write(self: *FindByteOutStream, bytes: []const u8) Error!usize {
2450 const self = @fieldParentPtr(Self, "stream", out_stream);
2451 if (self.byte_found) return bytes.len;2440 if (self.byte_found) return bytes.len;
2452 self.byte_found = blk: {2441 self.byte_found = blk: {
2453 for (bytes) |b|2442 for (bytes) |b|
...@@ -2456,11 +2445,15 @@ const FindByteOutStream = struct {...@@ -2456,11 +2445,15 @@ const FindByteOutStream = struct {
2456 };2445 };
2457 return bytes.len;2446 return bytes.len;
2458 }2447 }
2448
2449 pub fn outStream(self: *FindByteOutStream) OutStream {
2450 return .{ .context = self };
2451 }
2459};2452};
24602453
2461fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Child.Error!void {2454fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void {
2462 for (slice) |byte| switch (byte) {2455 for (slice) |byte| switch (byte) {
2463 '\t' => try stream.write(" "),2456 '\t' => try stream.writeAll(" "),
2464 '\r' => {},2457 '\r' => {},
2465 else => try stream.writeByte(byte),2458 else => try stream.writeByte(byte),
2466 };2459 };
lib/std/zig/system.zig+10-10
...@@ -570,7 +570,7 @@ pub const NativeTargetInfo = struct {...@@ -570,7 +570,7 @@ pub const NativeTargetInfo = struct {
570 cross_target: CrossTarget,570 cross_target: CrossTarget,
571 ) AbiAndDynamicLinkerFromFileError!NativeTargetInfo {571 ) AbiAndDynamicLinkerFromFileError!NativeTargetInfo {
572 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;572 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
573 _ = try preadFull(file, &hdr_buf, 0, hdr_buf.len);573 _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
574 const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);574 const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
575 const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);575 const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
576 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;576 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
...@@ -610,7 +610,7 @@ pub const NativeTargetInfo = struct {...@@ -610,7 +610,7 @@ pub const NativeTargetInfo = struct {
610 // Reserve some bytes so that we can deref the 64-bit struct fields610 // Reserve some bytes so that we can deref the 64-bit struct fields
611 // even when the ELF file is 32-bits.611 // even when the ELF file is 32-bits.
612 const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);612 const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
613 const ph_read_byte_len = try preadFull(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);613 const ph_read_byte_len = try preadMin(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
614 var ph_buf_i: usize = 0;614 var ph_buf_i: usize = 0;
615 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({615 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
616 ph_i += 1;616 ph_i += 1;
...@@ -625,7 +625,7 @@ pub const NativeTargetInfo = struct {...@@ -625,7 +625,7 @@ pub const NativeTargetInfo = struct {
625 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);625 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
626 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);626 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
627 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;627 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
628 _ = try preadFull(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);628 _ = try preadMin(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
629 // PT_INTERP includes a null byte in p_filesz.629 // PT_INTERP includes a null byte in p_filesz.
630 const len = p_filesz - 1;630 const len = p_filesz - 1;
631 // dynamic_linker.max_byte is "max", not "len".631 // dynamic_linker.max_byte is "max", not "len".
...@@ -656,7 +656,7 @@ pub const NativeTargetInfo = struct {...@@ -656,7 +656,7 @@ pub const NativeTargetInfo = struct {
656 // Reserve some bytes so that we can deref the 64-bit struct fields656 // Reserve some bytes so that we can deref the 64-bit struct fields
657 // even when the ELF file is 32-bits.657 // even when the ELF file is 32-bits.
658 const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);658 const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
659 const dyn_read_byte_len = try preadFull(659 const dyn_read_byte_len = try preadMin(
660 file,660 file,
661 dyn_buf[0 .. dyn_buf.len - dyn_reserve],661 dyn_buf[0 .. dyn_buf.len - dyn_reserve],
662 dyn_off,662 dyn_off,
...@@ -701,14 +701,14 @@ pub const NativeTargetInfo = struct {...@@ -701,14 +701,14 @@ pub const NativeTargetInfo = struct {
701 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;701 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
702 if (sh_buf.len < shentsize) return error.InvalidElfFile;702 if (sh_buf.len < shentsize) return error.InvalidElfFile;
703703
704 _ = try preadFull(file, &sh_buf, str_section_off, shentsize);704 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
705 const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));705 const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
706 const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));706 const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
707 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);707 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
708 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);708 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
709 var strtab_buf: [4096:0]u8 = undefined;709 var strtab_buf: [4096:0]u8 = undefined;
710 const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);710 const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
711 const shstrtab_read_len = try preadFull(file, &strtab_buf, shstrtab_off, shstrtab_len);711 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
712 const shstrtab = strtab_buf[0..shstrtab_read_len];712 const shstrtab = strtab_buf[0..shstrtab_read_len];
713713
714 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);714 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
...@@ -717,7 +717,7 @@ pub const NativeTargetInfo = struct {...@@ -717,7 +717,7 @@ pub const NativeTargetInfo = struct {
717 // Reserve some bytes so that we can deref the 64-bit struct fields717 // Reserve some bytes so that we can deref the 64-bit struct fields
718 // even when the ELF file is 32-bits.718 // even when the ELF file is 32-bits.
719 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);719 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
720 const sh_read_byte_len = try preadFull(720 const sh_read_byte_len = try preadMin(
721 file,721 file,
722 sh_buf[0 .. sh_buf.len - sh_reserve],722 sh_buf[0 .. sh_buf.len - sh_reserve],
723 shoff,723 shoff,
...@@ -751,7 +751,7 @@ pub const NativeTargetInfo = struct {...@@ -751,7 +751,7 @@ pub const NativeTargetInfo = struct {
751751
752 if (dynstr) |ds| {752 if (dynstr) |ds| {
753 const strtab_len = std.math.min(ds.size, strtab_buf.len);753 const strtab_len = std.math.min(ds.size, strtab_buf.len);
754 const strtab_read_len = try preadFull(file, &strtab_buf, ds.offset, shstrtab_len);754 const strtab_read_len = try preadMin(file, &strtab_buf, ds.offset, shstrtab_len);
755 const strtab = strtab_buf[0..strtab_read_len];755 const strtab = strtab_buf[0..strtab_read_len];
756 // TODO this pointer cast should not be necessary756 // TODO this pointer cast should not be necessary
757 const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));757 const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
...@@ -813,7 +813,7 @@ pub const NativeTargetInfo = struct {...@@ -813,7 +813,7 @@ pub const NativeTargetInfo = struct {
813 return result;813 return result;
814 }814 }
815815
816 fn preadFull(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {816 fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
817 var i: u64 = 0;817 var i: u64 = 0;
818 while (i < min_read_len) {818 while (i < min_read_len) {
819 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {819 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
...@@ -853,7 +853,7 @@ pub const NativeTargetInfo = struct {...@@ -853,7 +853,7 @@ pub const NativeTargetInfo = struct {
853 abi: Target.Abi,853 abi: Target.Abi,
854 };854 };
855855
856 fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {856 pub fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
857 if (is_64) {857 if (is_64) {
858 if (need_bswap) {858 if (need_bswap) {
859 return @byteSwap(@TypeOf(int_64), int_64);859 return @byteSwap(@TypeOf(int_64), int_64);
src-self-hosted/libc_installation.zig+5-5
...@@ -38,7 +38,7 @@ pub const LibCInstallation = struct {...@@ -38,7 +38,7 @@ pub const LibCInstallation = struct {
38 pub fn parse(38 pub fn parse(
39 allocator: *Allocator,39 allocator: *Allocator,
40 libc_file: []const u8,40 libc_file: []const u8,
41 stderr: *std.io.OutStream(fs.File.WriteError),41 stderr: var,
42 ) !LibCInstallation {42 ) !LibCInstallation {
43 var self: LibCInstallation = .{};43 var self: LibCInstallation = .{};
4444
...@@ -123,7 +123,7 @@ pub const LibCInstallation = struct {...@@ -123,7 +123,7 @@ pub const LibCInstallation = struct {
123 return self;123 return self;
124 }124 }
125125
126 pub fn render(self: LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void {126 pub fn render(self: LibCInstallation, out: var) !void {
127 @setEvalBranchQuota(4000);127 @setEvalBranchQuota(4000);
128 const include_dir = self.include_dir orelse "";128 const include_dir = self.include_dir orelse "";
129 const sys_include_dir = self.sys_include_dir orelse "";129 const sys_include_dir = self.sys_include_dir orelse "";
...@@ -348,7 +348,7 @@ pub const LibCInstallation = struct {...@@ -348,7 +348,7 @@ pub const LibCInstallation = struct {
348348
349 for (searches) |search| {349 for (searches) |search| {
350 result_buf.shrink(0);350 result_buf.shrink(0);
351 const stream = &std.io.BufferOutStream.init(&result_buf).stream;351 const stream = result_buf.outStream();
352 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });352 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
353353
354 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {354 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
...@@ -395,7 +395,7 @@ pub const LibCInstallation = struct {...@@ -395,7 +395,7 @@ pub const LibCInstallation = struct {
395395
396 for (searches) |search| {396 for (searches) |search| {
397 result_buf.shrink(0);397 result_buf.shrink(0);
398 const stream = &std.io.BufferOutStream.init(&result_buf).stream;398 const stream = result_buf.outStream();
399 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });399 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
400400
401 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {401 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
...@@ -459,7 +459,7 @@ pub const LibCInstallation = struct {...@@ -459,7 +459,7 @@ pub const LibCInstallation = struct {
459459
460 for (searches) |search| {460 for (searches) |search| {
461 result_buf.shrink(0);461 result_buf.shrink(0);
462 const stream = &std.io.BufferOutStream.init(&result_buf).stream;462 const stream = result_buf.outStream();
463 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });463 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
464464
465 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {465 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
src-self-hosted/print_targets.zig+7-6
...@@ -52,7 +52,7 @@ const available_libcs = [_][]const u8{...@@ -52,7 +52,7 @@ const available_libcs = [_][]const u8{
52 "sparc-linux-gnu",52 "sparc-linux-gnu",
53 "sparcv9-linux-gnu",53 "sparcv9-linux-gnu",
54 "wasm32-freestanding-musl",54 "wasm32-freestanding-musl",
55 "x86_64-linux-gnu (native)",55 "x86_64-linux-gnu",
56 "x86_64-linux-gnux32",56 "x86_64-linux-gnux32",
57 "x86_64-linux-musl",57 "x86_64-linux-musl",
58 "x86_64-windows-gnu",58 "x86_64-windows-gnu",
...@@ -61,7 +61,8 @@ const available_libcs = [_][]const u8{...@@ -61,7 +61,8 @@ const available_libcs = [_][]const u8{
61pub fn cmdTargets(61pub fn cmdTargets(
62 allocator: *Allocator,62 allocator: *Allocator,
63 args: []const []const u8,63 args: []const []const u8,
64 stdout: *io.OutStream(fs.File.WriteError),64 /// Output stream
65 stdout: var,
65 native_target: Target,66 native_target: Target,
66) !void {67) !void {
67 const available_glibcs = blk: {68 const available_glibcs = blk: {
...@@ -92,9 +93,9 @@ pub fn cmdTargets(...@@ -92,9 +93,9 @@ pub fn cmdTargets(
92 };93 };
93 defer allocator.free(available_glibcs);94 defer allocator.free(available_glibcs);
9495
95 const BOS = io.BufferedOutStream(fs.File.WriteError);96 var bos = io.bufferedOutStream(stdout);
96 var bos = BOS.init(stdout);97 const bos_stream = bos.outStream();
97 var jws = std.json.WriteStream(BOS.Stream, 6).init(&bos.stream);98 var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
9899
99 try jws.beginObject();100 try jws.beginObject();
100101
...@@ -219,6 +220,6 @@ pub fn cmdTargets(...@@ -219,6 +220,6 @@ pub fn cmdTargets(
219220
220 try jws.endObject();221 try jws.endObject();
221222
222 try bos.stream.writeByte('\n');223 try bos_stream.writeByte('\n');
223 return bos.flush();224 return bos.flush();
224}225}
src-self-hosted/stage2.zig+15-15
...@@ -18,8 +18,8 @@ const assert = std.debug.assert;...@@ -18,8 +18,8 @@ const assert = std.debug.assert;
18const LibCInstallation = @import("libc_installation.zig").LibCInstallation;18const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1919
20var stderr_file: fs.File = undefined;20var stderr_file: fs.File = undefined;
21var stderr: *io.OutStream(fs.File.WriteError) = undefined;21var stderr: fs.File.OutStream = undefined;
22var stdout: *io.OutStream(fs.File.WriteError) = undefined;22var stdout: fs.File.OutStream = undefined;
2323
24comptime {24comptime {
25 _ = @import("dep_tokenizer.zig");25 _ = @import("dep_tokenizer.zig");
...@@ -146,7 +146,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error...@@ -146,7 +146,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error
146}146}
147147
148export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {148export 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);
150 _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {150 _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
151 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode151 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
152 error.SystemResources => return .SystemResources,152 error.SystemResources => return .SystemResources,
...@@ -186,9 +186,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {...@@ -186,9 +186,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
186 try args_list.append(mem.toSliceConst(u8, argv[arg_i]));186 try args_list.append(mem.toSliceConst(u8, argv[arg_i]));
187 }187 }
188188
189 stdout = &std.io.getStdOut().outStream().stream;189 stdout = std.io.getStdOut().outStream();
190 stderr_file = std.io.getStdErr();190 stderr_file = std.io.getStdErr();
191 stderr = &stderr_file.outStream().stream;191 stderr = stderr_file.outStream();
192192
193 const args = args_list.toSliceConst()[2..];193 const args = args_list.toSliceConst()[2..];
194194
...@@ -203,11 +203,11 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {...@@ -203,11 +203,11 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
203 const arg = args[i];203 const arg = args[i];
204 if (mem.startsWith(u8, arg, "-")) {204 if (mem.startsWith(u8, arg, "-")) {
205 if (mem.eql(u8, arg, "--help")) {205 if (mem.eql(u8, arg, "--help")) {
206 try stdout.write(self_hosted_main.usage_fmt);206 try stdout.writeAll(self_hosted_main.usage_fmt);
207 process.exit(0);207 process.exit(0);
208 } else if (mem.eql(u8, arg, "--color")) {208 } else if (mem.eql(u8, arg, "--color")) {
209 if (i + 1 >= args.len) {209 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");
211 process.exit(1);211 process.exit(1);
212 }212 }
213 i += 1;213 i += 1;
...@@ -238,14 +238,14 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {...@@ -238,14 +238,14 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
238238
239 if (stdin_flag) {239 if (stdin_flag) {
240 if (input_files.len != 0) {240 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");
242 process.exit(1);242 process.exit(1);
243 }243 }
244244
245 const stdin_file = io.getStdIn();245 const stdin_file = io.getStdIn();
246 var stdin = stdin_file.inStream();246 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);
249 defer allocator.free(source_code);249 defer allocator.free(source_code);
250250
251 const tree = std.zig.parse(allocator, source_code) catch |err| {251 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 {...@@ -272,7 +272,7 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
272 }272 }
273273
274 if (input_files.len == 0) {274 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");
276 process.exit(1);276 process.exit(1);
277 }277 }
278278
...@@ -409,11 +409,11 @@ fn printErrMsgToFile(...@@ -409,11 +409,11 @@ fn printErrMsgToFile(
409 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);409 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
410410
411 var text_buf = try std.Buffer.initSize(allocator, 0);411 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();
413 try parse_error.render(&tree.tokens, out_stream);413 try parse_error.render(&tree.tokens, out_stream);
414 const text = text_buf.toOwnedSlice();414 const text = text_buf.toOwnedSlice();
415415
416 const stream = &file.outStream().stream;416 const stream = &file.outStream();
417 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });417 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
418418
419 if (!color_on) return;419 if (!color_on) return;
...@@ -641,7 +641,7 @@ fn cmdTargets(zig_triple: [*:0]const u8) !void {...@@ -641,7 +641,7 @@ fn cmdTargets(zig_triple: [*:0]const u8) !void {
641 return @import("print_targets.zig").cmdTargets(641 return @import("print_targets.zig").cmdTargets(
642 std.heap.c_allocator,642 std.heap.c_allocator,
643 &[0][]u8{},643 &[0][]u8{},
644 &std.io.getStdOut().outStream().stream,644 std.io.getStdOut().outStream(),
645 target,645 target,
646 );646 );
647}647}
...@@ -808,7 +808,7 @@ const Stage2LibCInstallation = extern struct {...@@ -808,7 +808,7 @@ const Stage2LibCInstallation = extern struct {
808// ABI warning808// ABI warning
809export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {809export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
810 stderr_file = std.io.getStdErr();810 stderr_file = std.io.getStdErr();
811 stderr = &stderr_file.outStream().stream;811 stderr = stderr_file.outStream();
812 const libc_file = mem.toSliceConst(u8, libc_file_z);812 const libc_file = mem.toSliceConst(u8, libc_file_z);
813 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {813 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
814 error.ParseError => return .SemanticAnalyzeFail,814 error.ParseError => return .SemanticAnalyzeFail,
...@@ -870,7 +870,7 @@ export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {...@@ -870,7 +870,7 @@ export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
870// ABI warning870// ABI warning
871export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {871export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {
872 var libc = stage1_libc.toStage2();872 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);
874 libc.render(c_out_stream) catch |err| switch (err) {874 libc.render(c_out_stream) catch |err| switch (err) {
875 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode875 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
876 error.SystemResources => return .SystemResources,876 error.SystemResources => return .SystemResources,
test/compare_output.zig+15-19
...@@ -22,7 +22,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -22,7 +22,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
22 \\22 \\
23 \\pub fn main() void {23 \\pub fn main() void {
24 \\ privateFunction();24 \\ privateFunction();
25 \\ const stdout = &getStdOut().outStream().stream;25 \\ const stdout = getStdOut().outStream();
26 \\ stdout.print("OK 2\n", .{}) catch unreachable;26 \\ stdout.print("OK 2\n", .{}) catch unreachable;
27 \\}27 \\}
28 \\28 \\
...@@ -37,7 +37,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -37,7 +37,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
37 \\// purposefully conflicting function with main.zig37 \\// purposefully conflicting function with main.zig
38 \\// but it's private so it should be OK38 \\// but it's private so it should be OK
39 \\fn privateFunction() void {39 \\fn privateFunction() void {
40 \\ const stdout = &getStdOut().outStream().stream;40 \\ const stdout = getStdOut().outStream();
41 \\ stdout.print("OK 1\n", .{}) catch unreachable;41 \\ stdout.print("OK 1\n", .{}) catch unreachable;
42 \\}42 \\}
43 \\43 \\
...@@ -63,7 +63,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -63,7 +63,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
63 tc.addSourceFile("foo.zig",63 tc.addSourceFile("foo.zig",
64 \\usingnamespace @import("std").io;64 \\usingnamespace @import("std").io;
65 \\pub fn foo_function() void {65 \\pub fn foo_function() void {
66 \\ const stdout = &getStdOut().outStream().stream;66 \\ const stdout = getStdOut().outStream();
67 \\ stdout.print("OK\n", .{}) catch unreachable;67 \\ stdout.print("OK\n", .{}) catch unreachable;
68 \\}68 \\}
69 );69 );
...@@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
74 \\74 \\
75 \\pub fn bar_function() void {75 \\pub fn bar_function() void {
76 \\ if (foo_function()) {76 \\ if (foo_function()) {
77 \\ const stdout = &getStdOut().outStream().stream;77 \\ const stdout = getStdOut().outStream();
78 \\ stdout.print("OK\n", .{}) catch unreachable;78 \\ stdout.print("OK\n", .{}) catch unreachable;
79 \\ }79 \\ }
80 \\}80 \\}
...@@ -106,7 +106,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -106,7 +106,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
106 \\pub const a_text = "OK\n";106 \\pub const a_text = "OK\n";
107 \\107 \\
108 \\pub fn ok() void {108 \\pub fn ok() void {
109 \\ const stdout = &io.getStdOut().outStream().stream;109 \\ const stdout = io.getStdOut().outStream();
110 \\ stdout.print(b_text, .{}) catch unreachable;110 \\ stdout.print(b_text, .{}) catch unreachable;
111 \\}111 \\}
112 );112 );
...@@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
124 \\const io = @import("std").io;124 \\const io = @import("std").io;
125 \\125 \\
126 \\pub fn main() void {126 \\pub fn main() void {
127 \\ const stdout = &io.getStdOut().outStream().stream;127 \\ const stdout = io.getStdOut().outStream();
128 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable;128 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable;
129 \\}129 \\}
130 , "Hello, world!\n 12 12 a\n");130 , "Hello, world!\n 12 12 a\n");
...@@ -267,7 +267,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -267,7 +267,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
267 \\ var x_local : i32 = print_ok(x);267 \\ var x_local : i32 = print_ok(x);
268 \\}268 \\}
269 \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) {269 \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) {
270 \\ const stdout = &io.getStdOut().outStream().stream;270 \\ const stdout = io.getStdOut().outStream();
271 \\ stdout.print("OK\n", .{}) catch unreachable;271 \\ stdout.print("OK\n", .{}) catch unreachable;
272 \\ return 0;272 \\ return 0;
273 \\}273 \\}
...@@ -349,7 +349,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -349,7 +349,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
349 \\pub fn main() void {349 \\pub fn main() void {
350 \\ const bar = Bar {.field2 = 13,};350 \\ const bar = Bar {.field2 = 13,};
351 \\ const foo = Foo {.field1 = bar,};351 \\ const foo = Foo {.field1 = bar,};
352 \\ const stdout = &io.getStdOut().outStream().stream;352 \\ const stdout = io.getStdOut().outStream();
353 \\ if (!foo.method()) {353 \\ if (!foo.method()) {
354 \\ stdout.print("BAD\n", .{}) catch unreachable;354 \\ stdout.print("BAD\n", .{}) catch unreachable;
355 \\ }355 \\ }
...@@ -363,7 +363,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -363,7 +363,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
363 cases.add("defer with only fallthrough",363 cases.add("defer with only fallthrough",
364 \\const io = @import("std").io;364 \\const io = @import("std").io;
365 \\pub fn main() void {365 \\pub fn main() void {
366 \\ const stdout = &io.getStdOut().outStream().stream;366 \\ const stdout = io.getStdOut().outStream();
367 \\ stdout.print("before\n", .{}) catch unreachable;367 \\ stdout.print("before\n", .{}) catch unreachable;
368 \\ defer stdout.print("defer1\n", .{}) catch unreachable;368 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
369 \\ defer stdout.print("defer2\n", .{}) catch unreachable;369 \\ defer stdout.print("defer2\n", .{}) catch unreachable;
...@@ -376,7 +376,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -376,7 +376,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
376 \\const io = @import("std").io;376 \\const io = @import("std").io;
377 \\const os = @import("std").os;377 \\const os = @import("std").os;
378 \\pub fn main() void {378 \\pub fn main() void {
379 \\ const stdout = &io.getStdOut().outStream().stream;379 \\ const stdout = io.getStdOut().outStream();
380 \\ stdout.print("before\n", .{}) catch unreachable;380 \\ stdout.print("before\n", .{}) catch unreachable;
381 \\ defer stdout.print("defer1\n", .{}) catch unreachable;381 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
382 \\ defer stdout.print("defer2\n", .{}) catch unreachable;382 \\ defer stdout.print("defer2\n", .{}) catch unreachable;
...@@ -393,7 +393,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -393,7 +393,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
393 \\ do_test() catch return;393 \\ do_test() catch return;
394 \\}394 \\}
395 \\fn do_test() !void {395 \\fn do_test() !void {
396 \\ const stdout = &io.getStdOut().outStream().stream;396 \\ const stdout = io.getStdOut().outStream();
397 \\ stdout.print("before\n", .{}) catch unreachable;397 \\ stdout.print("before\n", .{}) catch unreachable;
398 \\ defer stdout.print("defer1\n", .{}) catch unreachable;398 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
399 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;399 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
...@@ -412,7 +412,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -412,7 +412,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
412 \\ do_test() catch return;412 \\ do_test() catch return;
413 \\}413 \\}
414 \\fn do_test() !void {414 \\fn do_test() !void {
415 \\ const stdout = &io.getStdOut().outStream().stream;415 \\ const stdout = io.getStdOut().outStream();
416 \\ stdout.print("before\n", .{}) catch unreachable;416 \\ stdout.print("before\n", .{}) catch unreachable;
417 \\ defer stdout.print("defer1\n", .{}) catch unreachable;417 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
418 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;418 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
...@@ -429,7 +429,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -429,7 +429,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
429 \\const io = @import("std").io;429 \\const io = @import("std").io;
430 \\430 \\
431 \\pub fn main() void {431 \\pub fn main() void {
432 \\ const stdout = &io.getStdOut().outStream().stream;432 \\ const stdout = io.getStdOut().outStream();
433 \\ stdout.print(foo_txt, .{}) catch unreachable;433 \\ stdout.print(foo_txt, .{}) catch unreachable;
434 \\}434 \\}
435 , "1234\nabcd\n");435 , "1234\nabcd\n");
...@@ -448,9 +448,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -448,9 +448,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
448 \\448 \\
449 \\pub fn main() !void {449 \\pub fn main() !void {
450 \\ var args_it = std.process.args();450 \\ var args_it = std.process.args();
451 \\ var stdout_file = io.getStdOut();451 \\ const stdout = io.getStdOut().outStream();
452 \\ var stdout_adapter = stdout_file.outStream();
453 \\ const stdout = &stdout_adapter.stream;
454 \\ var index: usize = 0;452 \\ var index: usize = 0;
455 \\ _ = args_it.skip();453 \\ _ = args_it.skip();
456 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {454 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
...@@ -489,9 +487,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -489,9 +487,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
489 \\487 \\
490 \\pub fn main() !void {488 \\pub fn main() !void {
491 \\ var args_it = std.process.args();489 \\ var args_it = std.process.args();
492 \\ var stdout_file = io.getStdOut();490 \\ const stdout = io.getStdOut().outStream();
493 \\ var stdout_adapter = stdout_file.outStream();
494 \\ const stdout = &stdout_adapter.stream;
495 \\ var index: usize = 0;491 \\ var index: usize = 0;
496 \\ _ = args_it.skip();492 \\ _ = args_it.skip();
497 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {493 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
test/standalone/guess_number/main.zig+1-1
...@@ -4,7 +4,7 @@ const io = std.io;...@@ -4,7 +4,7 @@ const io = std.io;
4const fmt = std.fmt;4const fmt = std.fmt;
55
6pub fn main() !void {6pub fn main() !void {
7 const stdout = &io.getStdOut().outStream().stream;7 const stdout = io.getStdOut().outStream();
8 const stdin = io.getStdIn();8 const stdin = io.getStdIn();
99
10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
test/tests.zig+4-10
...@@ -566,12 +566,9 @@ pub const StackTracesContext = struct {...@@ -566,12 +566,9 @@ pub const StackTracesContext = struct {
566 }566 }
567 child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) });567 child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) });
568568
569 var stdout_file_in_stream = child.stdout.?.inStream();569 const stdout = child.stdout.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
570 var stderr_file_in_stream = child.stderr.?.inStream();
571
572 const stdout = stdout_file_in_stream.stream.readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
573 defer b.allocator.free(stdout);570 defer b.allocator.free(stdout);
574 const stderr = stderr_file_in_stream.stream.readAllAlloc(b.allocator, max_stdout_size) catch unreachable;571 const stderr = child.stderr.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
575 defer b.allocator.free(stderr);572 defer b.allocator.free(stderr);
576573
577 const term = child.wait() catch |err| {574 const term = child.wait() catch |err| {
...@@ -798,11 +795,8 @@ pub const CompileErrorContext = struct {...@@ -798,11 +795,8 @@ pub const CompileErrorContext = struct {
798 var stdout_buf = Buffer.initNull(b.allocator);795 var stdout_buf = Buffer.initNull(b.allocator);
799 var stderr_buf = Buffer.initNull(b.allocator);796 var stderr_buf = Buffer.initNull(b.allocator);
800797
801 var stdout_file_in_stream = child.stdout.?.inStream();798 child.stdout.?.inStream().readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
802 var stderr_file_in_stream = child.stderr.?.inStream();799 child.stderr.?.inStream().readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
803
804 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
805 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
806800
807 const term = child.wait() catch |err| {801 const term = child.wait() catch |err| {
808 debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) });802 debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) });