| author | |
| committer | |
| log | 895f67cc6dfe3ade4b635c4c2168843b022edee7 |
| tree | 78f817084e76780c1b3eaab961657b168736e4d3 |
| parent | 571f3ed161455074be5f296b39b24cba554da8e0 |
| parent | 06d2f53ece7328e6beedd5c846a5b25798ba74e3 |
| signature |
rework I/O stream abstractions56 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(); |
| 42 | 42 | ||
| 43 | var file_in_stream = in_file.inStream(); | 43 | const input_file_bytes = try in_file.inStream().readAllAlloc(allocator, max_doc_file_size); |
| 44 | 44 | ||
| 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); | ||
| 49 | 46 | ||
| 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 {}; |
| 55 | 52 | ||
| 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 | } |
| 59 | 56 | ||
| ... | @@ -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(); |
| 329 | 326 | ||
| 330 | var toc_buf_adapter = io.BufferOutStream.init(&toc_buf); | 327 | var toc = toc_buf.outStream(); |
| 331 | var toc = &toc_buf_adapter.stream; | ||
| 332 | 328 | ||
| 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 { |
| 424 | 420 | ||
| 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(); |
| 616 | 612 | ||
| 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(); |
| 636 | 631 | ||
| 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 { |
| 643 | fn writeEscaped(out: var, input: []const u8) !void { | 637 | fn 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(); |
| 683 | 677 | ||
| 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 { |
| 774 | 767 | ||
| 775 | fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void { | 768 | fn 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 | }, |
| 842 | 835 | ||
| 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 | }, |
| 849 | 842 | ||
| ... | @@ -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 | }, |
| 859 | 852 | ||
| 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 | }, |
| 868 | 861 | ||
| 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 | }, |
| 874 | 867 | ||
| 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 | }, |
| 884 | 877 | ||
| 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 | }, |
| 920 | 913 | ||
| 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 | } |
| 988 | 981 | ||
| 989 | fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void { | 982 | fn 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 @@ |
| 230 | const std = @import("std"); | 230 | const std = @import("std"); |
| 231 | 231 | ||
| 232 | pub fn main() !void { | 232 | pub 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 | } |
| 105 | 105 | ||
| 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 | } |
| 113 | 109 | ||
| 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" { |
| 326 | 322 | ||
| 327 | test "std.atomic.Queue dump" { | 323 | test "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); |
| 333 | 328 | ||
| 334 | var queue = Queue(i32).init(); | 329 | var queue = Queue(i32).init(); |
| 335 | 330 | ||
| 336 | // Test empty stream | 331 | // 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); |
| 352 | 347 | ||
| 353 | sos.reset(); | 348 | fbs.reset(); |
| 354 | try queue.dumpToStream(SliceOutStream.Error, &sos.stream); | 349 | try queue.dumpToStream(fbs.outStream()); |
| 355 | 350 | ||
| 356 | var expected = try std.fmt.bufPrint(expected_buffer[0..], | 351 | var expected = try std.fmt.bufPrint(expected_buffer[0..], |
| 357 | \\head: 0x{x}=1 | 352 | \\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)); |
| 364 | 359 | ||
| 365 | // Test a stream with two elements | 360 | // 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); |
| 372 | 367 | ||
| 373 | sos.reset(); | 368 | fbs.reset(); |
| 374 | try queue.dumpToStream(SliceOutStream.Error, &sos.stream); | 369 | try queue.dumpToStream(fbs.outStream()); |
| 375 | 370 | ||
| 376 | expected = try std.fmt.bufPrint(expected_buffer[0..], | 371 | expected = try std.fmt.bufPrint(expected_buffer[0..], |
| 377 | \\head: 0x{x}=1 | 372 | \\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 | }; |
| 161 | 172 | ||
| 162 | test "simple Buffer" { | 173 | test "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 | |||
| 223 | test "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 { |
| 926 | 926 | ||
| 927 | try child.spawn(); | 927 | try child.spawn(); |
| 928 | 928 | ||
| 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); |
| 932 | 931 | ||
| 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; |
| 14 | const sort = std.sort; | 14 | const sort = std.sort; |
| 15 | const warn = std.debug.warn; | 15 | const warn = std.debug.warn; |
| 16 | 16 | ||
| 17 | const BinOutStream = io.OutStream(anyerror); | ||
| 18 | const BinSeekStream = io.SeekableStream(anyerror, anyerror); | ||
| 19 | const ElfSeekStream = io.SeekableStream(anyerror, anyerror); | ||
| 20 | const ElfInStream = io.InStream(anyerror); | ||
| 21 | |||
| 22 | const BinaryElfSection = struct { | 17 | const 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 { |
| 41 | 36 | ||
| 42 | const Self = @This(); | 37 | const Self = @This(); |
| 43 | 38 | ||
| 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 | } |
| 55 | 43 | ||
| 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); | ||
| 58 | 50 | ||
| 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); |
| 62 | 54 | ||
| ... | @@ -69,19 +61,19 @@ const BinaryElfOutput = struct { | ... | @@ -69,19 +61,19 @@ const BinaryElfOutput = struct { |
| 69 | } | 61 | } |
| 70 | } | 62 | } |
| 71 | 63 | ||
| 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); |
| 75 | 67 | ||
| 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; |
| 82 | 74 | ||
| 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 | } |
| 127 | 119 | ||
| 128 | sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare); | 120 | sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare); |
| 121 | |||
| 122 | return self; | ||
| 129 | } | 123 | } |
| 130 | 124 | ||
| 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 | } |
| 134 | 128 | ||
| 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 | } |
| 138 | 133 | ||
| 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 | }; |
| 153 | 148 | ||
| 154 | const WriteContext = struct { | 149 | fn 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 | |||
| 161 | fn 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); | ||
| 167 | 151 | ||
| 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 | } |
| 171 | 157 | ||
| 172 | fn emit_raw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void { | 158 | fn 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 | }; | ||
| 200 | 161 | ||
| 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(); |
| 203 | 164 | ||
| 204 | try binaryElfOutput.parseElf(elfFile); | 165 | var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); |
| 166 | defer binary_elf_output.deinit(); | ||
| 205 | 167 | ||
| 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 | } |
| 210 | 172 | ||
| ... | @@ -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); |
| 251 | 213 | ||
| 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 { |
| 175 | 175 | ||
| 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 { |
| 186 | 185 | ||
| 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 { |
| 217 | 217 | ||
| 218 | try child.spawn(); | 218 | try child.spawn(); |
| 219 | 219 | ||
| 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(); |
| 222 | 222 | ||
| 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); |
| 228 | 228 | ||
| 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(); |
| 782 | 782 | ||
| 783 | var buf_stream = &io.BufferOutStream.init(&buf).stream; | 783 | var buf_stream = buf.outStream(); |
| 784 | 784 | ||
| 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 | } |
| 863 | 862 | ||
| 864 | fn readIntFd(fd: i32) !ErrInt { | 863 | fn 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 | } |
| 873 | 871 | ||
| 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; |
| 58 | 58 | ||
| 59 | var file_stream = self.in_file.inStream(); | 59 | const in = self.in_file.inStream(); |
| 60 | const in = &file_stream.stream; | ||
| 61 | 60 | ||
| 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 | } |
| 91 | 90 | ||
| 92 | try self.loadOptionalHeader(&file_stream); | 91 | try self.loadOptionalHeader(); |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 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 32 | 98 | // 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; |
| 138 | 137 | ||
| 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); |
| 142 | 140 | ||
| 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 { |
| 188 | 186 | ||
| 189 | try self.sections.ensureCapacity(self.coff_header.number_of_sections); | 187 | try self.sections.ensureCapacity(self.coff_header.number_of_sections); |
| 190 | 188 | ||
| 191 | var file_stream = self.in_file.inStream(); | 189 | const in = self.in_file.inStream(); |
| 192 | const in = &file_stream.stream; | ||
| 193 | 190 | ||
| 194 | var name: [8]u8 = undefined; | 191 | var name: [8]u8 = undefined; |
| 195 | 192 |
lib/std/debug.zig+156-124| ... | @@ -55,7 +55,7 @@ pub const LineInfo = struct { | ... | @@ -55,7 +55,7 @@ pub const LineInfo = struct { |
| 55 | var stderr_file: File = undefined; | 55 | var stderr_file: File = undefined; |
| 56 | var stderr_file_out_stream: File.OutStream = undefined; | 56 | var stderr_file_out_stream: File.OutStream = undefined; |
| 57 | 57 | ||
| 58 | var stderr_stream: ?*io.OutStream(File.WriteError) = null; | 58 | var stderr_stream: ?*File.OutStream = null; |
| 59 | var stderr_mutex = std.Mutex.init(); | 59 | var stderr_mutex = std.Mutex.init(); |
| 60 | 60 | ||
| 61 | pub fn warn(comptime fmt: []const u8, args: var) void { | 61 | 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,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 | } |
| 67 | 67 | ||
| 68 | pub fn getStderrStream() *io.OutStream(File.WriteError) { | 68 | pub 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, |
| 409 | 409 | ||
| 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 { |
| 475 | 475 | ||
| 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; |
| 477 | 477 | ||
| 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; |
| 481 | 481 | ||
| 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); |
| 484 | 484 | ||
| 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); |
| 487 | 487 | ||
| 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); | ||
| 569 | 570 | ||
| 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 | } |
| 575 | 576 | ||
| 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"); |
| 582 | 583 | ||
| 583 | // Show the matching source code line if possible | 584 | // 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 char | 588 | // The caret already takes one char |
| 588 | const space_needed = @intCast(usize, li.column - 1); | 589 | const space_needed = @intCast(usize, li.column - 1); |
| 589 | 590 | ||
| 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 | }; |
| 610 | 612 | ||
| 611 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 613 | /// 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. | ||
| 614 | pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { | 614 | pub 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 | } |
| 629 | 631 | ||
| ... | @@ -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); |
| 655 | 657 | ||
| 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 team | 664 | 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. |
| 667 | 669 | ||
| 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); |
| 672 | 674 | ||
| 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; |
| 684 | 686 | ||
| 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; |
| 687 | 689 | ||
| 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); |
| 692 | 694 | ||
| 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; |
| 713 | 715 | ||
| 714 | // Dbi Header | 716 | // 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 team | 718 | 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 Substream | 728 | // 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); |
| 731 | 733 | ||
| 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); |
| 776 | 778 | ||
| 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 { |
| 808 | 810 | ||
| 809 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 811 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 810 | pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { | 812 | pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { |
| 811 | const mapped_mem = try mapWholeFile(elf_file_path); | 813 | noasync { |
| 812 | 814 | 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 | } | ||
| 820 | 860 | ||
| 821 | const debug_info = (try noasync efile.findSection(".debug_info")) orelse | 861 | var di = DW.DwarfInfo{ |
| 822 | return error.MissingDebugInfo; | 862 | .endian = endian, |
| 823 | const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse | 863 | .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")) orelse | 865 | .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")) orelse | 867 | .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 | }; | ||
| 842 | 869 | ||
| 843 | try noasync DW.openDwarfDebugInfo(&di, allocator); | 870 | try DW.openDwarfDebugInfo(&di, allocator); |
| 844 | 871 | ||
| 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 | } |
| 851 | 879 | ||
| 852 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 880 | /// 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 | } |
| 937 | 965 | ||
| 938 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | 966 | fn 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 size | 971 | // TODO fstat and make sure that the file has the correct size |
| 942 | 972 | ||
| ... | @@ -982,22 +1012,24 @@ const MachoSymbol = struct { | ... | @@ -982,22 +1012,24 @@ const MachoSymbol = struct { |
| 982 | } | 1012 | } |
| 983 | }; | 1013 | }; |
| 984 | 1014 | ||
| 985 | fn mapWholeFile(path: []const u8) ![]const u8 { | 1015 | fn 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 }); |
| 988 | 1018 | 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); | ||
| 999 | 1019 | ||
| 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 | } |
| 1002 | 1034 | ||
| 1003 | pub const DebugInfo = struct { | 1035 | pub 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 | } |
| 122 | 122 | ||
| 123 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { | 123 | fn 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 | } |
| 127 | 127 | ||
| 128 | fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T { | 128 | fn 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 | } |
| 132 | 132 | ||
| 133 | fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { | 133 | fn 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 | } |
| 141 | 141 | ||
| 142 | fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { | 142 | fn 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 | } |
| 150 | 150 | ||
| 151 | fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { | 151 | fn 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 | } |
| 161 | 161 | ||
| 162 | fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { | 162 | fn 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; |
| 11 | 11 | ||
| 12 | usingnamespace @import("dwarf_bits.zig"); | 12 | usingnamespace @import("dwarf_bits.zig"); |
| 13 | 13 | ||
| 14 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); | ||
| 15 | pub const DwarfInStream = io.InStream(anyerror); | ||
| 16 | |||
| 17 | const PcRange = struct { | 14 | const 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 | }; |
| 241 | 238 | ||
| 242 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { | 239 | fn 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 | } |
| 415 | 412 | ||
| 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; |
| 419 | 418 | ||
| 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 | }; |
| 425 | 424 | ||
| 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)); |
| 430 | 429 | ||
| 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; |
| 433 | 432 | ||
| 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); |
| 435 | 434 | ||
| 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; |
| 438 | 437 | ||
| 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); |
| 441 | 440 | ||
| 442 | try s.seekable_stream.seekTo(compile_unit_pos); | 441 | try seekable.seekTo(compile_unit_pos); |
| 443 | 442 | ||
| 444 | const next_unit_pos = this_unit_offset + next_offset; | 443 | const next_unit_pos = this_unit_offset + next_offset; |
| 445 | 444 | ||
| 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(); |
| 449 | 448 | ||
| 450 | const after_die_offset = try s.seekable_stream.getPos(); | 449 | const after_die_offset = try seekable.getPos(); |
| 451 | 450 | ||
| 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 repeat | 462 | // 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 repeat | 468 | // 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 | } |
| 513 | 512 | ||
| 514 | try s.seekable_stream.seekTo(after_die_offset); | 513 | try seekable.seekTo(after_die_offset); |
| 515 | } | 514 | } |
| 516 | 515 | ||
| 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 | } |
| 520 | 519 | ||
| 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; |
| 524 | 525 | ||
| 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 | }; |
| 530 | 531 | ||
| 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)); |
| 535 | 536 | ||
| 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; |
| 538 | 539 | ||
| 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); |
| 540 | 541 | ||
| 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; |
| 543 | 544 | ||
| 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); |
| 546 | 547 | ||
| 547 | try s.seekable_stream.seekTo(compile_unit_pos); | 548 | try seekable.seekTo(compile_unit_pos); |
| 548 | 549 | ||
| 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; |
| 551 | 552 | ||
| 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; |
| 553 | 554 | ||
| ... | @@ -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(); | ||
| 597 | 600 | ||
| 598 | // All the addresses in the list are relative to the value | 601 | // All the addresses in the list are relative to the value |
| 599 | // specified by DW_AT_low_pc or to some other value encoded | 602 | // 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 | }; |
| 606 | 609 | ||
| 607 | try s.seekable_stream.seekTo(ranges_offset); | 610 | try seekable.seekTo(ranges_offset); |
| 608 | 611 | ||
| 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 | } |
| 647 | 650 | ||
| 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(); | ||
| 650 | 655 | ||
| 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; |
| 664 | 669 | ||
| 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 | } |
| 696 | 701 | ||
| 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(); | ||
| 699 | 706 | ||
| 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); |
| 702 | 709 | ||
| 703 | try s.seekable_stream.seekTo(line_info_offset); | 710 | try seekable.seekTo(line_info_offset); |
| 704 | 711 | ||
| 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)); |
| 711 | 718 | ||
| 712 | const version = try s.stream.readInt(u16, di.endian); | 719 | const version = try in.readInt(u16, di.endian); |
| 713 | // TODO support 3 and 5 | 720 | // 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; |
| 715 | 722 | ||
| 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; |
| 718 | 725 | ||
| 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; |
| 721 | 728 | ||
| 722 | if (version >= 4) { | 729 | if (version >= 4) { |
| 723 | // maximum_operations_per_instruction | 730 | // maximum_operations_per_instruction |
| 724 | _ = try s.stream.readByte(); | 731 | _ = try in.readByte(); |
| 725 | } | 732 | } |
| 726 | 733 | ||
| 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(); |
| 729 | 736 | ||
| 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; |
| 732 | 739 | ||
| 733 | const opcode_base = try s.stream.readByte(); | 740 | const opcode_base = try in.readByte(); |
| 734 | 741 | ||
| 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 | } |
| 744 | 751 | ||
| 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); |
| 755 | 762 | ||
| 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 | } |
| 769 | 776 | ||
| 770 | try s.seekable_stream.seekTo(prog_start_offset); | 777 | try seekable.seekTo(prog_start_offset); |
| 771 | 778 | ||
| 772 | const next_unit_pos = line_info_offset + next_offset; | 779 | const next_unit_pos = line_info_offset + next_offset; |
| 773 | 780 | ||
| 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(); |
| 776 | 783 | ||
| 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 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const os = std.os; | 4 | const os = std.os; |
| 5 | const math = std.math; | 5 | const 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 | }; |
| 332 | 332 | ||
| 333 | pub const SectionHeader = Elf64_Shdr; | 333 | /// All integers are native endian. |
| 334 | pub const ProgramHeader = Elf64_Phdr; | 334 | const Header = struct { |
| 335 | |||
| 336 | pub 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); | ||
| 383 | 346 | ||
| 384 | elf.file_type = try in.readEnum(ET, elf.endian); | 347 | pub 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 | } | ||
| 386 | 381 | ||
| 387 | const elf_version = try in.readInt(u32, elf.endian); | 382 | /// All integers are native endian. |
| 388 | if (elf_version != 1) return error.InvalidFormat; | 383 | pub const AllHeaders = struct { |
| 384 | header: Header, | ||
| 385 | section_headers: []Elf64_Shdr, | ||
| 386 | program_headers: []Elf64_Phdr, | ||
| 387 | allocator: *mem.Allocator, | ||
| 388 | }; | ||
| 389 | 389 | ||
| 390 | if (elf.is_64) { | 390 | pub 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 | } |
| 399 | 443 | ||
| 400 | // skip over flags | 444 | return hdrs; |
| 401 | try seekable_stream.seekBy(4); | 445 | } |
| 402 | 446 | ||
| 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 | } |
| 407 | 474 | 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.* = .{ |
| 410 | 477 | .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 | } |
| 414 | 487 | } 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]; |
| 417 | 490 | 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 | } |
| 421 | 503 | 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]; |
| 423 | 505 | phdr.* = .{ | |
| 424 | if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat; | 506 | .p_type = o.p_type, |
| 425 | 507 | .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, |
| 430 | 512 | .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 | } | ||
| 435 | 517 | ||
| 436 | try seekable_stream.seekTo(elf.program_header_offset); | 518 | return hdrs; |
| 437 | 519 | } | |
| 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 | } | ||
| 464 | 520 | ||
| 465 | try seekable_stream.seekTo(elf.section_header_offset); | 521 | pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { |
| 466 | 522 | 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 | } |
| 498 | 528 | } 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 | } | ||
| 514 | 532 | ||
| 515 | pub fn close(elf: *Elf) void { | 533 | pub 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 { |
| 519 | 537 | 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 | } | ||
| 540 | 540 | ||
| 541 | pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void { | 541 | fn 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 | } |
| 545 | 559 | ||
| 546 | pub const EI_NIDENT = 16; | 560 | pub const EI_NIDENT = 16; |
| 547 | 561 |
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/1908 | 120 | // https://github.com/ziglang/zig/issues/1908 |
| 121 | if (builtin.single_threaded) return error.SkipZigTest; | 121 | if (builtin.single_threaded) return error.SkipZigTest; |
| 122 | 122 | ||
| 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; |
| 125 | 124 | ||
| 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 | } |
| 128 | 130 |
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/3251 | 125 | // 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; |
| 127 | 127 | ||
| 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(); |
| 130 | 133 |
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 | ||
| 99 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { | 100 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { |
| 100 | const my_cwd = cwd(); | 101 | const my_cwd = cwd(); |
| 101 | 102 | ||
| ... | @@ -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 present | 142 | /// 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 | ||
| 144 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { | 146 | pub 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(); |
| 147 | 149 | ||
| 148 | const mode = try in_file.mode(); | 150 | const stat = try in_file.stat(); |
| 149 | const in_stream = &in_file.inStream().stream; | ||
| 150 | 151 | ||
| 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(); |
| 153 | 154 | ||
| 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 | } |
| 163 | 158 | ||
| 164 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 159 | /// 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 present | 161 | /// 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 | ||
| 167 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | 164 | pub 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(); |
| 173 | 170 | ||
| 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 | } |
| 183 | 174 | ||
| 184 | /// TODO update this API to avoid a getrandom syscall for every operation. It | 175 | /// 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); |
| 1152 | 1143 | ||
| 1153 | try file.inStream().stream.readNoEof(buf); | 1144 | try file.inStream().readNoEof(buf); |
| 1154 | return buf; | 1145 | return buf; |
| 1155 | } | 1146 | } |
| 1156 | 1147 |
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 | } |
| 77 | 77 | ||
| ... | @@ -250,11 +250,16 @@ pub const File = struct { | ... | @@ -250,11 +250,16 @@ pub const File = struct { |
| 250 | } | 250 | } |
| 251 | } | 251 | } |
| 252 | 252 | ||
| 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 | } |
| 259 | 264 | ||
| 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 | } |
| 267 | 272 | ||
| 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 | } |
| 274 | 284 | ||
| 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 | } |
| 282 | 292 | ||
| 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 in | 296 | /// 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; |
| 287 | 300 | ||
| 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 | } |
| 308 | 326 | ||
| 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 in | 330 | /// 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 | } |
| 498 | 522 | ||
| 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 | } |
| 505 | 528 | ||
| 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 | } |
| 512 | 534 | ||
| 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; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
| 11 | 11 | ||
| 12 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; | 12 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; |
| 13 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; | ||
| 13 | 14 | ||
| 14 | const Allocator = mem.Allocator; | 15 | const Allocator = mem.Allocator; |
| 15 | 16 |
lib/std/heap/logging_allocator.zig+51-45| ... | @@ -1,63 +1,69 @@ | ... | @@ -1,63 +1,69 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | ||
| 4 | const AnyErrorOutStream = std.io.OutStream(anyerror); | ||
| 5 | |||
| 6 | /// This allocator is used in front of another allocator and logs to the provided stream | 4 | /// 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. |
| 9 | pub const LoggingAllocator = struct { | 7 | pub 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, | ||
| 13 | 12 | ||
| 14 | const Self = @This(); | 13 | const Self = @This(); |
| 15 | 14 | ||
| 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 | } | ||
| 42 | 41 | ||
| 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 | |
| 55 | pub 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 | } | ||
| 54 | 61 | ||
| 55 | test "LoggingAllocator" { | 62 | test "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; | ||
| 59 | 65 | ||
| 60 | const allocator = &LoggingAllocator.init(std.testing.allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator; | 66 | const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator; |
| 61 | 67 | ||
| 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"); |
| 4 | const c = std.c; | 4 | const c = std.c; |
| 5 | 5 | ||
| 6 | const math = std.math; | 6 | const math = std.math; |
| 7 | const debug = std.debug; | 7 | const assert = std.debug.assert; |
| 8 | const assert = debug.assert; | ||
| 9 | const os = std.os; | 8 | const os = std.os; |
| 10 | const fs = std.fs; | 9 | const fs = std.fs; |
| 11 | const mem = std.mem; | 10 | const mem = std.mem; |
| 12 | const meta = std.meta; | 11 | const meta = std.meta; |
| 13 | const trait = meta.trait; | 12 | const trait = meta.trait; |
| 14 | const Buffer = std.Buffer; | ||
| 15 | const fmt = std.fmt; | ||
| 16 | const File = std.fs.File; | 13 | const File = std.fs.File; |
| 17 | const testing = std.testing; | ||
| 18 | 14 | ||
| 19 | pub const Mode = enum { | 15 | pub 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 | } |
| 94 | 90 | ||
| 95 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | ||
| 96 | pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; | ||
| 97 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; | ||
| 98 | pub const InStream = @import("io/in_stream.zig").InStream; | 91 | pub const InStream = @import("io/in_stream.zig").InStream; |
| 99 | pub const OutStream = @import("io/out_stream.zig").OutStream; | 92 | pub const OutStream = @import("io/out_stream.zig").OutStream; |
| 93 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | ||
| 100 | 94 | ||
| 101 | /// Deprecated; use `std.fs.Dir.writeFile`. | 95 | pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream; |
| 102 | pub fn writeFile(path: []const u8, data: []const u8) !void { | 96 | pub 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`. | ||
| 107 | pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { | ||
| 108 | return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn BufferedInStream(comptime Error: type) type { | ||
| 112 | return BufferedInStreamCustom(mem.page_size, Error); | ||
| 113 | } | ||
| 114 | |||
| 115 | pub 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 | |||
| 159 | test "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. | ||
| 199 | pub 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 | |||
| 263 | pub 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 | ||
| 294 | pub 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 | } | ||
| 326 | 97 | ||
| 327 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | 98 | pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream; |
| 328 | /// containing them in the least significant end. The number of bits successfully | 99 | pub 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)); | ||
| 332 | 100 | ||
| 333 | //by extending the buffer to a minimum of u8 we can cover a number of edge cases | 101 | pub const PeekStream = @import("io/peek_stream.zig").PeekStream; |
| 334 | // related to shifting and casting. | 102 | pub 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); | ||
| 342 | 103 | ||
| 343 | out_bits.* = @as(usize, 0); | 104 | pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream; |
| 344 | if (U == u0 or bits == 0) return 0; | 105 | pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream; |
| 345 | var out_buffer = @as(Buf, 0); | ||
| 346 | 106 | ||
| 347 | if (self.bit_count > 0) { | 107 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; |
| 348 | const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; | 108 | pub 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 | ||
| 371 | 109 | ||
| 372 | //copy bytes until we have enough bits, then leave the rest in bit_buffer | 110 | pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream; |
| 373 | while (out_bits.* < bits) { | 111 | pub 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 | }; | ||
| 383 | 112 | ||
| 384 | switch (endian) { | 113 | pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream; |
| 385 | .Big => { | 114 | pub 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 | } | ||
| 393 | 115 | ||
| 394 | const shift = @intCast(u3, u8_bit_count - n); | 116 | pub const BitOutStream = @import("io/bit_out_stream.zig").BitOutStream; |
| 395 | out_buffer <<= @intCast(BufShift, n); | 117 | pub 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 | } | ||
| 407 | 118 | ||
| 408 | const shift = @intCast(u3, u8_bit_count - n); | 119 | pub 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 | } | ||
| 417 | 120 | ||
| 418 | return @intCast(U, out_buffer); | 121 | pub const Serializer = @import("io/serialization.zig").Serializer; |
| 419 | } | 122 | pub const serializer = @import("io/serialization.zig").serializer; |
| 420 | 123 | ||
| 421 | pub fn alignToByte(self: *Self) void { | 124 | pub const Deserializer = @import("io/serialization.zig").Deserializer; |
| 422 | self.bit_buffer = 0; | 125 | pub const deserializer = @import("io/serialization.zig").deserializer; |
| 423 | self.bit_count = 0; | ||
| 424 | } | ||
| 425 | 126 | ||
| 426 | pub fn read(self_stream: *Stream, buffer: []u8) Error!usize { | 127 | pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile; |
| 427 | var self = @fieldParentPtr(Self, "stream", self_stream); | ||
| 428 | 128 | ||
| 429 | var out_bits: usize = undefined; | 129 | pub 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 | } | ||
| 440 | 130 | ||
| 441 | return self.in_stream.read(buffer); | 131 | /// Deprecated; use `std.fs.Dir.writeFile`. |
| 442 | } | 132 | pub fn writeFile(path: []const u8, data: []const u8) !void { |
| 443 | }; | 133 | return fs.cwd().writeFile(path, data); |
| 444 | } | 134 | } |
| 445 | 135 | ||
| 446 | /// This is a simple OutStream that writes to a fixed buffer. If the returned number | 136 | /// Deprecated; use `std.fs.Dir.readFileAlloc`. |
| 447 | /// of bytes written is less than requested, the buffer is full. | 137 | pub 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)); |
| 449 | pub 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 | |||
| 495 | test "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 | } |
| 503 | 140 | ||
| 504 | var null_out_stream_state = NullOutStream.init(); | ||
| 505 | pub 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. |
| 508 | pub const NullOutStream = struct { | 142 | pub 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 | |||
| 525 | test "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. | ||
| 532 | pub 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 | |||
| 559 | test "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 | } | ||
| 568 | 143 | ||
| 569 | pub fn BufferedOutStream(comptime Error: type) type { | 144 | const NullOutStream = OutStream(void, error{}, dummyWrite); |
| 570 | return BufferedOutStreamCustom(mem.page_size, Error); | 145 | fn dummyWrite(context: void, data: []const u8) error{}!usize { |
| 146 | return data.len; | ||
| 571 | } | 147 | } |
| 572 | 148 | ||
| 573 | pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type { | 149 | test "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 | ||
| 616 | pub 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 | ||
| 638 | pub 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 | |||
| 766 | pub 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 | |||
| 807 | pub 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. | ||
| 824 | pub 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 | } |
| 988 | 152 | ||
| 989 | /// Creates a serializer that serializes types to any stream. | 153 | test "" { |
| 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. | ||
| 1002 | pub 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 | |||
| 1138 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const testing = std.testing; | ||
| 6 | const trait = std.meta.trait; | ||
| 7 | const meta = std.meta; | ||
| 8 | const math = std.math; | ||
| 9 | |||
| 10 | /// Creates a stream which allows for reading bit fields from another stream | ||
| 11 | pub 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 | |||
| 164 | pub 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 | |||
| 171 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | const testing = std.testing; | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | const trait = std.meta.trait; | ||
| 7 | const meta = std.meta; | ||
| 8 | const math = std.math; | ||
| 9 | |||
| 10 | /// Creates a stream which allows for writing bit fields to another stream | ||
| 11 | pub 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 | |||
| 140 | pub 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 | |||
| 147 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const fs = std.fs; | ||
| 4 | const File = std.fs.File; | ||
| 5 | |||
| 6 | pub 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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | pub 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 | |||
| 44 | pub fn bufferedInStream(underlying_stream: var) BufferedInStream(4096, @TypeOf(underlying_stream)) { | ||
| 45 | return .{ .unbuffered_in_stream = underlying_stream }; | ||
| 46 | } | ||
| 47 | |||
| 48 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | |||
| 4 | pub 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 | |||
| 39 | pub 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 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const os = std.os; | 2 | const builtin = std.builtin; |
| 3 | const OutStream = std.io.OutStream; | 3 | const io = std.io; |
| 4 | const builtin = @import("builtin"); | 4 | const testing = std.testing; |
| 5 | 5 | ||
| 6 | /// TODO make a proposal to make `std.fs.File` use *FILE when linking libc and this just becomes | 6 | pub 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. | ||
| 9 | pub const COutStream = struct { | ||
| 10 | pub const Error = std.fs.File.WriteError; | ||
| 11 | pub const Stream = OutStream(Error); | ||
| 12 | 7 | ||
| 13 | stream: Stream, | 8 | pub fn cOutStream(c_file: *std.c.FILE) COutStream { |
| 14 | c_file: *std.c.FILE, | 9 | return .{ .context = c_file }; |
| 10 | } | ||
| 15 | 11 | ||
| 16 | pub fn init(c_file: *std.c.FILE) COutStream { | 12 | fn 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 | } | ||
| 22 | 31 | ||
| 23 | fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize { | 32 | test "" { |
| 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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | /// An OutStream that counts how many bytes has been written to it. | ||
| 6 | pub 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 | |||
| 28 | pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) { | ||
| 29 | return .{ .bytes_written = 0, .child_stream = child_stream }; | ||
| 30 | } | ||
| 31 | |||
| 32 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const testing = std.testing; | ||
| 4 | const mem = std.mem; | ||
| 5 | const 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. | ||
| 9 | pub 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 | |||
| 116 | pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) { | ||
| 117 | return .{ .buffer = mem.span(buffer), .pos = 0 }; | ||
| 118 | } | ||
| 119 | |||
| 120 | fn 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 | |||
| 126 | test "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 | |||
| 135 | test "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 | |||
| 155 | test "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 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = std.builtin; |
| 3 | const root = @import("root"); | ||
| 4 | const math = std.math; | 3 | const math = std.math; |
| 5 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 6 | const mem = std.mem; | 5 | const mem = std.mem; |
| 7 | const Buffer = std.Buffer; | 6 | const Buffer = std.Buffer; |
| 8 | const testing = std.testing; | 7 | const testing = std.testing; |
| 9 | 8 | ||
| 10 | pub const default_stack_size = 1 * 1024 * 1024; | 9 | pub fn InStream( |
| 11 | pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream")) | 10 | comptime Context: type, |
| 12 | root.stack_size_std_io_InStream | 11 | comptime ReadError: type, |
| 13 | else | 12 | /// 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. |
| 15 | 14 | /// End of stream is not an error condition. | |
| 16 | pub 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; | ||
| 24 | 19 | ||
| 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, | ||
| 29 | 23 | ||
| 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 | } |
| 43 | 30 | ||
| 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 error | 32 | /// 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 { |
| 59 | 43 | ||
| 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 | } |
| 66 | 50 | ||
| 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 returned | 60 | /// 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 the | 100 | /// 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`. The | 144 | /// 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 | } |
| 196 | 180 | ||
| 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 | } |
| 204 | 188 | ||
| 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 | } |
| 209 | 193 | ||
| 210 | /// Reads a native-endian integer | 194 | /// 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 | } |
| 216 | 200 | ||
| 217 | /// Reads a foreign-endian integer | 201 | /// 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 | } |
| 223 | 207 | ||
| 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 | } |
| 229 | 213 | ||
| 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 | } |
| 235 | 219 | ||
| 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 | } |
| 241 | 225 | ||
| 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 | } |
| 249 | 233 | ||
| 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 | } |
| 256 | 240 | ||
| 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 matches | 249 | /// 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 order | 251 | /// 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 { |
| 286 | 270 | ||
| 287 | test "InStream" { | 271 | test "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 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = std.builtin; |
| 3 | const root = @import("root"); | ||
| 4 | const mem = std.mem; | 3 | const mem = std.mem; |
| 5 | 4 | ||
| 6 | pub const default_stack_size = 1 * 1024 * 1024; | 5 | pub fn OutStream( |
| 7 | pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream")) | 6 | comptime Context: type, |
| 8 | root.stack_size_std_io_OutStream | 7 | comptime WriteError: type, |
| 9 | else | 8 | comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize, |
| 10 | default_stack_size; | 9 | ) type { |
| 11 | |||
| 12 | pub 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; | ||
| 20 | 15 | ||
| 21 | writeFn: WriteFn, | 16 | pub fn write(self: Self, bytes: []const u8) Error!usize { |
| 22 | 17 | 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 | } |
| 33 | 19 | ||
| 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 | } |
| 40 | 26 | ||
| 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 | } |
| 44 | 30 | ||
| 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 | } |
| 49 | 35 | ||
| 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); |
| 53 | 39 | ||
| 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 | } |
| 61 | 47 | ||
| 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 | } |
| 68 | 55 | ||
| 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 | } |
| 75 | 63 | ||
| 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 | } |
| 81 | 70 | ||
| 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 | } |
| 87 | 77 | ||
| 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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const mem = std.mem; | ||
| 4 | const 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 | ||
| 9 | pub 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 | |||
| 74 | pub 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 | |||
| 81 | test "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 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const InStream = std.io.InStream; | 2 | const InStream = std.io.InStream; |
| 3 | 3 | ||
| 4 | pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { | 4 | pub 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; |
| 9 | 19 | ||
| 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 | } |
| 19 | 23 | ||
| 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 | } |
| 23 | 27 | ||
| 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 | } |
| 27 | 31 | ||
| 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 | |||
| 34 | pub 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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | |||
| 5 | pub 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. | ||
| 22 | pub 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 | |||
| 185 | pub 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. | ||
| 206 | pub 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 | |||
| 341 | pub 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 | |||
| 349 | fn 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 | |||
| 403 | test "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 | |||
| 412 | fn 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 | |||
| 453 | test "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 | |||
| 460 | fn testAlternateSerializer(self: var, serializer: var) !void { | ||
| 461 | try serializer.serialize(self.f_f16); | ||
| 462 | } | ||
| 463 | |||
| 464 | fn 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 | |||
| 560 | test "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 | |||
| 571 | fn 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 | |||
| 601 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const 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. | ||
| 9 | pub 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(); |
| 24 | 24 | ||
| 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); |
| 50 | 49 | ||
| 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); |
| 56 | 54 | ||
| ... | @@ -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 | } |
| 63 | 61 | ||
| 64 | test "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 | |||
| 76 | test "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 | |||
| 94 | test "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 | |||
| 127 | test "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 | |||
| 147 | test "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 | |||
| 219 | test "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 | |||
| 272 | test "BitStreams with File Stream" { | 62 | test "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(); |
| 277 | 67 | ||
| 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); | ||
| 282 | 69 | ||
| 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(); |
| 294 | 81 | ||
| 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); | ||
| 299 | 83 | ||
| 300 | var out_bits: usize = undefined; | 84 | var out_bits: usize = undefined; |
| 301 | 85 | ||
| ... | @@ -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 | } |
| 319 | 103 | ||
| 320 | fn 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 | |||
| 381 | test "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 | |||
| 390 | fn 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 | |||
| 435 | test "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 | |||
| 442 | fn testAlternateSerializer(self: var, serializer: var) !void { | ||
| 443 | try serializer.serialize(self.f_f16); | ||
| 444 | } | ||
| 445 | |||
| 446 | fn 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 | |||
| 546 | test "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 | |||
| 557 | fn 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 | |||
| 591 | test "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 | |||
| 598 | test "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 | |||
| 612 | test "File seek ops" { | 104 | test "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" { |
| 621 | 113 | ||
| 622 | // Seek to the end | 114 | // 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 delta | 117 | // 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 delta | 120 | // 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 position | 123 | // 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 | } |
| 635 | 127 | ||
| 636 | test "updateTimes" { | 128 | test "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; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
| 11 | 11 | ||
| 12 | pub const WriteStream = @import("json/write_stream.zig").WriteStream; | 12 | pub const WriteStream = @import("json/write_stream.zig").WriteStream; |
| 13 | pub const writeStream = @import("json/write_stream.zig").writeStream; | ||
| 13 | 14 | ||
| 14 | const StringEscapes = union(enum) { | 15 | const 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" { |
| 2107 | test "write json then parse it" { | 2108 | test "write json then parse it" { |
| 2108 | var out_buffer: [1000]u8 = undefined; | 2109 | var out_buffer: [1000]u8 = undefined; |
| 2109 | 2110 | ||
| 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); |
| 2113 | 2114 | ||
| 2114 | try jw.beginObject(); | 2115 | try jw.beginObject(); |
| 2115 | 2116 | ||
| ... | @@ -2140,7 +2141,7 @@ test "write json then parse it" { | ... | @@ -2140,7 +2141,7 @@ test "write json then parse it" { |
| 2140 | 2141 | ||
| 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(); |
| 2145 | 2146 | ||
| 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 = " ", |
| 32 | 32 | ||
| 33 | stream: *OutStream, | 33 | stream: OutStream, |
| 34 | state_index: usize, | 34 | state_index: usize, |
| 35 | state: [max_depth]State, | 35 | state: [max_depth]State, |
| 36 | 36 | ||
| 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 { |
| 134 | 134 | ||
| 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 | } |
| 140 | 140 | ||
| 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 { |
| 231 | 231 | ||
| 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 | } |
| 240 | 240 | ||
| ... | @@ -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 | } |
| 251 | 251 | ||
| 252 | pub 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 | |||
| 252 | test "json write stream" { | 259 | test "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(); |
| 256 | 263 | ||
| 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(); |
| 259 | 266 | ||
| 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)); |
| 262 | 269 | ||
| 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(); |
| 818 | 818 | ||
| 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(); |
| 1012 | 1012 | ||
| 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 { |
| 113 | fn testServer(server: *net.StreamServer) anyerror!void { | 113 | fn testServer(server: *net.StreamServer) anyerror!void { |
| 114 | var client = try server.accept(); | 114 | var client = try server.accept(); |
| 115 | 115 | ||
| 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 | } |
| 182 | 182 |
lib/std/os/test.zig+34-9| ... | @@ -95,15 +95,41 @@ test "sendfile" { | ... | @@ -95,15 +95,41 @@ test "sendfile" { |
| 95 | }, | 95 | }, |
| 96 | }; | 96 | }; |
| 97 | 97 | ||
| 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 | |||
| 109 | test "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 | |||
| 128 | fn 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 | } |
| 108 | 134 | ||
| 109 | test "std.Thread.getCurrentId" { | 135 | test "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(); |
| 356 | 382 | ||
| 357 | var out_stream = file.outStream(); | 383 | const stream = file.outStream(); |
| 358 | const stream = &out_stream.stream; | ||
| 359 | 384 | ||
| 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); |
| 380 | 405 | ||
| 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(); |
| 383 | 408 | ||
| 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); |
| 404 | 429 | ||
| 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(); |
| 407 | 432 | ||
| 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, |
| 496 | 496 | ||
| 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; | ||
| 500 | 499 | ||
| 501 | const superblock = try in.readStruct(SuperBlock); | 500 | const superblock = try in.readStruct(SuperBlock); |
| 502 | 501 | ||
| ... | @@ -529,7 +528,7 @@ const Msf = struct { | ... | @@ -529,7 +528,7 @@ const Msf = struct { |
| 529 | ); | 528 | ); |
| 530 | 529 | ||
| 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); |
| 535 | 534 | ||
| ... | @@ -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 | } |
| 544 | 543 | ||
| ... | @@ -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, |
| 634 | 633 | ||
| 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); | ||
| 640 | 635 | ||
| 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 | }; |
| 649 | 643 | ||
| 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; |
| 668 | 662 | ||
| 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; | ||
| 672 | 665 | ||
| 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 | } |
| 717 | 710 | ||
| 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 { |
| 42 | 42 | ||
| 43 | var targets = ArrayList([]const u8).init(allocator); | 43 | var targets = ArrayList([]const u8).init(allocator); |
| 44 | 44 | ||
| 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(); |
| 47 | 47 | ||
| 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 | } |
| 161 | 161 | ||
| 162 | try out_stream.write( | 162 | try out_stream.writeAll( |
| 163 | \\ | 163 | \\ |
| 164 | \\General Options: | 164 | \\General Options: |
| 165 | \\ --help Print this help and exit | 165 | \\ --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 | } |
| 186 | 186 | ||
| 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.zig | 190 | \\ --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; |
| 5 | pub const BufMap = @import("buf_map.zig").BufMap; | 5 | pub const BufMap = @import("buf_map.zig").BufMap; |
| 6 | pub const BufSet = @import("buf_set.zig").BufSet; | 6 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 7 | pub const Buffer = @import("buffer.zig").Buffer; | 7 | pub const Buffer = @import("buffer.zig").Buffer; |
| 8 | pub const BufferOutStream = @import("io.zig").BufferOutStream; | ||
| 9 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | 8 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 10 | pub const DynLib = @import("dynamic_library.zig").DynLib; | 9 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 11 | pub const HashMap = @import("hash_map.zig").HashMap; | 10 | pub 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, |
| 376 | 376 | ||
| 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; |
| 2809 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; | 2809 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 2810 | 2810 | ||
| 2811 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { | 2811 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { |
| 2812 | const stderr = &io.getStdErr().outStream().stream; | 2812 | const stderr = io.getStdErr().outStream(); |
| 2813 | 2813 | ||
| 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(); |
| 2845 | 2845 | ||
| 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 | } |
| 2850 | 2849 |
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 | }; |
| 13 | 13 | ||
| 14 | /// Returns whether anything changed | 14 | /// Returns whether anything changed |
| 15 | pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Child.Error || Error)!bool { | 15 | pub 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 changed | 16 | // 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); | ||
| 25 | 20 | ||
| 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, |
| 31 | 25 | ||
| 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 | } |
| 47 | 39 | ||
| 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 | }; | ||
| 58 | 52 | ||
| 59 | try renderRoot(allocator, &my_stream.stream, tree); | 53 | try renderRoot(allocator, my_stream_stream, tree); |
| 60 | 54 | ||
| 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 | } |
| 64 | 58 | ||
| 65 | return anything_changed; | 59 | return my_stream.anything_changed; |
| 66 | } | 60 | } |
| 67 | 61 | ||
| 68 | fn renderRoot( | 62 | fn 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); |
| 74 | 68 | ||
| 75 | // render all the line comments at the beginning of the file | 69 | // 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 | } |
| 191 | 185 | ||
| 192 | fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Child.Error!void { | 186 | fn 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 | } |
| 206 | 200 | ||
| 207 | fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Child.Error || Error)!void { | 201 | fn 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 | } |
| 210 | 204 | ||
| 211 | fn 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 { | 205 | fn 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 | else | 447 | 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 ..]; |
| 910 | 904 | ||
| 911 | // Null stream for counting the printed length of each expression | 905 | // 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); | ||
| 914 | 907 | ||
| 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( |
| 1336 | 1329 | ||
| 1337 | // TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348 | 1330 | // 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); // @name | 1334 | 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 | } |
| 1512 | 1505 | ||
| 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); |
| 1999 | 1992 | ||
| 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); |
| 2011 | 2004 | ||
| 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(" ("); |
| 2017 | 2010 | ||
| 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); // pub | 2050 | 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); |
| 2130 | 2123 | ||
| 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 | } |
| 2205 | 2198 | ||
| 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..], " ")); |
| 2208 | 2201 | ||
| 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), " ")); |
| 2331 | 2324 | ||
| 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 | } |
| 2387 | 2380 | ||
| ... | @@ -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 | } |
| 2429 | 2422 | ||
| 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. |
| 2432 | const FindByteOutStream = struct { | 2425 | const 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, |
| 2440 | 2428 | ||
| 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 | } |
| 2448 | 2438 | ||
| 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 | }; |
| 2460 | 2453 | ||
| 2461 | fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Child.Error!void { | 2454 | fn 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 fields | 610 | // 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 fields | 656 | // 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; |
| 703 | 703 | ||
| 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]; |
| 713 | 713 | ||
| 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 fields | 717 | // 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 { |
| 751 | 751 | ||
| 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 necessary | 756 | // 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 | } |
| 815 | 815 | ||
| 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 | }; |
| 855 | 855 | ||
| 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 = .{}; |
| 44 | 44 | ||
| ... | @@ -123,7 +123,7 @@ pub const LibCInstallation = struct { | ... | @@ -123,7 +123,7 @@ pub const LibCInstallation = struct { |
| 123 | return self; | 123 | return self; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 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 { |
| 348 | 348 | ||
| 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 }); |
| 353 | 353 | ||
| 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 { |
| 395 | 395 | ||
| 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 }); |
| 400 | 400 | ||
| 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 { |
| 459 | 459 | ||
| 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 }); |
| 464 | 464 | ||
| 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{ |
| 61 | pub fn cmdTargets( | 61 | pub 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); |
| 94 | 95 | ||
| 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); |
| 98 | 99 | ||
| 99 | try jws.beginObject(); | 100 | try jws.beginObject(); |
| 100 | 101 | ||
| ... | @@ -219,6 +220,6 @@ pub fn cmdTargets( | ... | @@ -219,6 +220,6 @@ pub fn cmdTargets( |
| 219 | 220 | ||
| 220 | try jws.endObject(); | 221 | try jws.endObject(); |
| 221 | 222 | ||
| 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; |
| 18 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 18 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 19 | 19 | ||
| 20 | var stderr_file: fs.File = undefined; | 20 | var stderr_file: fs.File = undefined; |
| 21 | var stderr: *io.OutStream(fs.File.WriteError) = undefined; | 21 | var stderr: fs.File.OutStream = undefined; |
| 22 | var stdout: *io.OutStream(fs.File.WriteError) = undefined; | 22 | var stdout: fs.File.OutStream = undefined; |
| 23 | 23 | ||
| 24 | comptime { | 24 | comptime { |
| 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 | } |
| 147 | 147 | ||
| 148 | export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { | 148 | export 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 mode | 151 | 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 | } |
| 188 | 188 | ||
| 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(); |
| 192 | 192 | ||
| 193 | const args = args_list.toSliceConst()[2..]; | 193 | const args = args_list.toSliceConst()[2..]; |
| 194 | 194 | ||
| ... | @@ -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 { |
| 238 | 238 | ||
| 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 | } |
| 244 | 244 | ||
| 245 | const stdin_file = io.getStdIn(); | 245 | const stdin_file = io.getStdIn(); |
| 246 | var stdin = stdin_file.inStream(); | 246 | var stdin = stdin_file.inStream(); |
| 247 | 247 | ||
| 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); |
| 250 | 250 | ||
| 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 | } |
| 273 | 273 | ||
| 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 | } |
| 278 | 278 | ||
| ... | @@ -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); |
| 410 | 410 | ||
| 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(); |
| 415 | 415 | ||
| 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 }); |
| 418 | 418 | ||
| 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 warning | 808 | // ABI warning |
| 809 | export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error { | 809 | export 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 warning | 870 | // ABI warning |
| 871 | export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error { | 871 | export 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 mode | 875 | 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.zig | 37 | \\// purposefully conflicting function with main.zig |
| 38 | \\// but it's private so it should be OK | 38 | \\// 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; |
| 4 | const fmt = std.fmt; | 4 | const fmt = std.fmt; |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub 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(); |
| 9 | 9 | ||
| 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) }); |
| 568 | 568 | ||
| 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); |
| 576 | 573 | ||
| 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); |
| 800 | 797 | ||
| 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; | ||
| 806 | 800 | ||
| 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) }); |