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

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

rework I/O stream abstractions

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

doc/docgen.zig+44-51
......@@ -40,12 +40,9 @@ pub fn main() !void {
4040 var out_file = try fs.cwd().createFile(out_file_name, .{});
4141 defer out_file.close();
4242
43 var file_in_stream = in_file.inStream();
43 const input_file_bytes = try in_file.inStream().readAllAlloc(allocator, max_doc_file_size);
4444
45 const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);
46
47 var file_out_stream = out_file.outStream();
48 var buffered_out_stream = io.BufferedOutStream(fs.File.WriteError).init(&file_out_stream.stream);
45 var buffered_out_stream = io.bufferedOutStream(out_file.outStream());
4946
5047 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
5148 var toc = try genToc(allocator, &tokenizer);
......@@ -53,7 +50,7 @@ pub fn main() !void {
5350 try fs.cwd().makePath(tmp_dir_name);
5451 defer fs.deleteTree(tmp_dir_name) catch {};
5552
56 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
53 try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);
5754 try buffered_out_stream.flush();
5855}
5956
......@@ -327,8 +324,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
327324 var toc_buf = try std.Buffer.initSize(allocator, 0);
328325 defer toc_buf.deinit();
329326
330 var toc_buf_adapter = io.BufferOutStream.init(&toc_buf);
331 var toc = &toc_buf_adapter.stream;
327 var toc = toc_buf.outStream();
332328
333329 var nodes = std.ArrayList(Node).init(allocator);
334330 defer nodes.deinit();
......@@ -342,7 +338,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
342338 if (header_stack_size != 0) {
343339 return parseError(tokenizer, token, "unbalanced headers", .{});
344340 }
345 try toc.write(" </ul>\n");
341 try toc.writeAll(" </ul>\n");
346342 break;
347343 },
348344 Token.Id.Content => {
......@@ -407,7 +403,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
407403 if (last_columns) |n| {
408404 try toc.print("<ul style=\"columns: {}\">\n", .{n});
409405 } else {
410 try toc.write("<ul>\n");
406 try toc.writeAll("<ul>\n");
411407 }
412408 } else {
413409 last_action = Action.Open;
......@@ -424,9 +420,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
424420
425421 if (last_action == Action.Close) {
426422 try toc.writeByteNTimes(' ', 8 + header_stack_size * 4);
427 try toc.write("</ul></li>\n");
423 try toc.writeAll("</ul></li>\n");
428424 } else {
429 try toc.write("</li>\n");
425 try toc.writeAll("</li>\n");
430426 last_action = Action.Close;
431427 }
432428 } else if (mem.eql(u8, tag_name, "see_also")) {
......@@ -614,8 +610,7 @@ fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
614610 var buf = try std.Buffer.initSize(allocator, 0);
615611 defer buf.deinit();
616612
617 var buf_adapter = io.BufferOutStream.init(&buf);
618 var out = &buf_adapter.stream;
613 const out = buf.outStream();
619614 for (input) |c| {
620615 switch (c) {
621616 'a'...'z', 'A'...'Z', '_', '-', '0'...'9' => {
......@@ -634,8 +629,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
634629 var buf = try std.Buffer.initSize(allocator, 0);
635630 defer buf.deinit();
636631
637 var buf_adapter = io.BufferOutStream.init(&buf);
638 var out = &buf_adapter.stream;
632 const out = buf.outStream();
639633 try writeEscaped(out, input);
640634 return buf.toOwnedSlice();
641635}
......@@ -643,10 +637,10 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
643637fn writeEscaped(out: var, input: []const u8) !void {
644638 for (input) |c| {
645639 try switch (c) {
646 '&' => out.write("&amp;"),
647 '<' => out.write("&lt;"),
648 '>' => out.write("&gt;"),
649 '"' => out.write("&quot;"),
640 '&' => out.writeAll("&amp;"),
641 '<' => out.writeAll("&lt;"),
642 '>' => out.writeAll("&gt;"),
643 '"' => out.writeAll("&quot;"),
650644 else => out.writeByte(c),
651645 };
652646 }
......@@ -681,8 +675,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
681675 var buf = try std.Buffer.initSize(allocator, 0);
682676 defer buf.deinit();
683677
684 var buf_adapter = io.BufferOutStream.init(&buf);
685 var out = &buf_adapter.stream;
678 var out = buf.outStream();
686679 var number_start_index: usize = undefined;
687680 var first_number: usize = undefined;
688681 var second_number: usize = undefined;
......@@ -743,7 +736,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
743736 'm' => {
744737 state = TermState.Start;
745738 while (open_span_count != 0) : (open_span_count -= 1) {
746 try out.write("</span>");
739 try out.writeAll("</span>");
747740 }
748741 if (first_number != 0 or second_number != 0) {
749742 try out.print("<span class=\"t{}_{}\">", .{ first_number, second_number });
......@@ -774,7 +767,7 @@ fn isType(name: []const u8) bool {
774767
775768fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void {
776769 const src = mem.trim(u8, raw_src, " \n");
777 try out.write("<code class=\"zig\">");
770 try out.writeAll("<code class=\"zig\">");
778771 var tokenizer = std.zig.Tokenizer.init(src);
779772 var index: usize = 0;
780773 var next_tok_is_fn = false;
......@@ -835,15 +828,15 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
835828 .Keyword_allowzero,
836829 .Keyword_while,
837830 => {
838 try out.write("<span class=\"tok-kw\">");
831 try out.writeAll("<span class=\"tok-kw\">");
839832 try writeEscaped(out, src[token.start..token.end]);
840 try out.write("</span>");
833 try out.writeAll("</span>");
841834 },
842835
843836 .Keyword_fn => {
844 try out.write("<span class=\"tok-kw\">");
837 try out.writeAll("<span class=\"tok-kw\">");
845838 try writeEscaped(out, src[token.start..token.end]);
846 try out.write("</span>");
839 try out.writeAll("</span>");
847840 next_tok_is_fn = true;
848841 },
849842
......@@ -852,24 +845,24 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
852845 .Keyword_true,
853846 .Keyword_false,
854847 => {
855 try out.write("<span class=\"tok-null\">");
848 try out.writeAll("<span class=\"tok-null\">");
856849 try writeEscaped(out, src[token.start..token.end]);
857 try out.write("</span>");
850 try out.writeAll("</span>");
858851 },
859852
860853 .StringLiteral,
861854 .MultilineStringLiteralLine,
862855 .CharLiteral,
863856 => {
864 try out.write("<span class=\"tok-str\">");
857 try out.writeAll("<span class=\"tok-str\">");
865858 try writeEscaped(out, src[token.start..token.end]);
866 try out.write("</span>");
859 try out.writeAll("</span>");
867860 },
868861
869862 .Builtin => {
870 try out.write("<span class=\"tok-builtin\">");
863 try out.writeAll("<span class=\"tok-builtin\">");
871864 try writeEscaped(out, src[token.start..token.end]);
872 try out.write("</span>");
865 try out.writeAll("</span>");
873866 },
874867
875868 .LineComment,
......@@ -877,16 +870,16 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
877870 .ContainerDocComment,
878871 .ShebangLine,
879872 => {
880 try out.write("<span class=\"tok-comment\">");
873 try out.writeAll("<span class=\"tok-comment\">");
881874 try writeEscaped(out, src[token.start..token.end]);
882 try out.write("</span>");
875 try out.writeAll("</span>");
883876 },
884877
885878 .Identifier => {
886879 if (prev_tok_was_fn) {
887 try out.write("<span class=\"tok-fn\">");
880 try out.writeAll("<span class=\"tok-fn\">");
888881 try writeEscaped(out, src[token.start..token.end]);
889 try out.write("</span>");
882 try out.writeAll("</span>");
890883 } else {
891884 const is_int = blk: {
892885 if (src[token.start] != 'i' and src[token.start] != 'u')
......@@ -901,9 +894,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
901894 break :blk true;
902895 };
903896 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\">");
905898 try writeEscaped(out, src[token.start..token.end]);
906 try out.write("</span>");
899 try out.writeAll("</span>");
907900 } else {
908901 try writeEscaped(out, src[token.start..token.end]);
909902 }
......@@ -913,9 +906,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
913906 .IntegerLiteral,
914907 .FloatLiteral,
915908 => {
916 try out.write("<span class=\"tok-number\">");
909 try out.writeAll("<span class=\"tok-number\">");
917910 try writeEscaped(out, src[token.start..token.end]);
918 try out.write("</span>");
911 try out.writeAll("</span>");
919912 },
920913
921914 .Bang,
......@@ -983,7 +976,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
983976 }
984977 index = token.end;
985978 }
986 try out.write("</code>");
979 try out.writeAll("</code>");
987980}
988981
989982fn 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
1002995 for (toc.nodes) |node| {
1003996 switch (node) {
1004997 .Content => |data| {
1005 try out.write(data);
998 try out.writeAll(data);
1006999 },
10071000 .Link => |info| {
10081001 if (!toc.urls.contains(info.url)) {
......@@ -1011,12 +1004,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
10111004 try out.print("<a href=\"#{}\">{}</a>", .{ info.url, info.name });
10121005 },
10131006 .Nav => {
1014 try out.write(toc.toc);
1007 try out.writeAll(toc.toc);
10151008 },
10161009 .Builtin => |tok| {
1017 try out.write("<pre>");
1010 try out.writeAll("<pre>");
10181011 try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code);
1019 try out.write("</pre>");
1012 try out.writeAll("</pre>");
10201013 },
10211014 .HeaderOpen => |info| {
10221015 try out.print(
......@@ -1025,7 +1018,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
10251018 );
10261019 },
10271020 .SeeAlso => |items| {
1028 try out.write("<p>See also:</p><ul>\n");
1021 try out.writeAll("<p>See also:</p><ul>\n");
10291022 for (items) |item| {
10301023 const url = try urlize(allocator, item.name);
10311024 if (!toc.urls.contains(url)) {
......@@ -1033,7 +1026,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
10331026 }
10341027 try out.print("<li><a href=\"#{}\">{}</a></li>\n", .{ url, item.name });
10351028 }
1036 try out.write("</ul>\n");
1029 try out.writeAll("</ul>\n");
10371030 },
10381031 .Syntax => |content_tok| {
10391032 try tokenizeAndPrint(tokenizer, out, content_tok);
......@@ -1047,9 +1040,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
10471040 if (!code.is_inline) {
10481041 try out.print("<p class=\"file\">{}.zig</p>", .{code.name});
10491042 }
1050 try out.write("<pre>");
1043 try out.writeAll("<pre>");
10511044 try tokenizeAndPrint(tokenizer, out, code.source_token);
1052 try out.write("</pre>");
1045 try out.writeAll("</pre>");
10531046 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", .{code.name});
10541047 const tmp_source_file_name = try fs.path.join(
10551048 allocator,
doc/langref.html.in+1-1
......@@ -230,7 +230,7 @@
230230const std = @import("std");
231231
232232pub fn main() !void {
233 const stdout = &std.io.getStdOut().outStream().stream;
233 const stdout = std.io.getStdOut().outStream();
234234 try stdout.print("Hello, {}!\n", .{"world"});
235235}
236236 {#code_end#}
lib/std/atomic/queue.zig+14-19
......@@ -104,21 +104,17 @@ pub fn Queue(comptime T: type) type {
104104 }
105105
106106 pub fn dump(self: *Self) void {
107 var stderr_file = std.io.getStdErr() 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;
107 self.dumpToStream(std.io.getStdErr().outStream()) catch return;
112108 }
113109
114 pub fn dumpToStream(self: *Self, comptime Error: type, stream: *std.io.OutStream(Error)) Error!void {
110 pub fn dumpToStream(self: *Self, stream: var) !void {
115111 const S = struct {
116112 fn dumpRecursive(
117 s: *std.io.OutStream(Error),
113 s: var,
118114 optional_node: ?*Node,
119115 indent: usize,
120116 comptime depth: comptime_int,
121 ) Error!void {
117 ) !void {
122118 try s.writeByteNTimes(' ', indent);
123119 if (optional_node) |node| {
124120 try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data });
......@@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" {
326322
327323test "std.atomic.Queue dump" {
328324 const mem = std.mem;
329 const SliceOutStream = std.io.SliceOutStream;
330325 var buffer: [1024]u8 = undefined;
331326 var expected_buffer: [1024]u8 = undefined;
332 var sos = SliceOutStream.init(buffer[0..]);
327 var fbs = std.io.fixedBufferStream(&buffer);
333328
334329 var queue = Queue(i32).init();
335330
336331 // Test empty stream
337 sos.reset();
338 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
339 expect(mem.eql(u8, buffer[0..sos.pos],
332 fbs.reset();
333 try queue.dumpToStream(fbs.outStream());
334 expect(mem.eql(u8, buffer[0..fbs.pos],
340335 \\head: (null)
341336 \\tail: (null)
342337 \\
......@@ -350,8 +345,8 @@ test "std.atomic.Queue dump" {
350345 };
351346 queue.put(&node_0);
352347
353 sos.reset();
354 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
348 fbs.reset();
349 try queue.dumpToStream(fbs.outStream());
355350
356351 var expected = try std.fmt.bufPrint(expected_buffer[0..],
357352 \\head: 0x{x}=1
......@@ -360,7 +355,7 @@ test "std.atomic.Queue dump" {
360355 \\ (null)
361356 \\
362357 , .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) });
363 expect(mem.eql(u8, buffer[0..sos.pos], expected));
358 expect(mem.eql(u8, buffer[0..fbs.pos], expected));
364359
365360 // Test a stream with two elements
366361 var node_1 = Queue(i32).Node{
......@@ -370,8 +365,8 @@ test "std.atomic.Queue dump" {
370365 };
371366 queue.put(&node_1);
372367
373 sos.reset();
374 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
368 fbs.reset();
369 try queue.dumpToStream(fbs.outStream());
375370
376371 expected = try std.fmt.bufPrint(expected_buffer[0..],
377372 \\head: 0x{x}=1
......@@ -381,5 +376,5 @@ test "std.atomic.Queue dump" {
381376 \\ (null)
382377 \\
383378 , .{ @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));
385380}
lib/std/buffer.zig+23
......@@ -157,6 +157,17 @@ pub const Buffer = struct {
157157 pub fn print(self: *Buffer, comptime fmt: []const u8, args: var) !void {
158158 return std.fmt.format(self, error{OutOfMemory}, Buffer.append, fmt, args);
159159 }
160
161 pub fn outStream(self: *Buffer) std.io.OutStream(*Buffer, error{OutOfMemory}, appendWrite) {
162 return .{ .context = self };
163 }
164
165 /// Same as `append` except it returns the number of bytes written, which is always the same
166 /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
167 pub fn appendWrite(self: *Buffer, m: []const u8) !usize {
168 try self.append(m);
169 return m.len;
170 }
160171};
161172
162173test "simple Buffer" {
......@@ -208,3 +219,15 @@ test "Buffer.print" {
208219 try buf.print("Hello {} the {}", .{ 2, "world" });
209220 testing.expect(buf.eql("Hello 2 the world"));
210221}
222
223test "Buffer.outStream" {
224 var buffer = try Buffer.initSize(testing.allocator, 0);
225 defer buffer.deinit();
226 const buf_stream = buffer.outStream();
227
228 const x: i32 = 42;
229 const y: i32 = 1234;
230 try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
231
232 testing.expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
233}
lib/std/build.zig+1-2
......@@ -926,8 +926,7 @@ pub const Builder = struct {
926926
927927 try child.spawn();
928928
929 var stdout_file_in_stream = child.stdout.?.inStream();
930 const stdout = try stdout_file_in_stream.stream.readAllAlloc(self.allocator, max_output_size);
929 const stdout = try child.stdout.?.inStream().readAllAlloc(self.allocator, max_output_size);
931930 errdefer self.allocator.free(stdout);
932931
933932 const term = try child.wait();
lib/std/build/emit_raw.zig+36-74
......@@ -14,11 +14,6 @@ const io = std.io;
1414const sort = std.sort;
1515const warn = std.debug.warn;
1616
17const BinOutStream = io.OutStream(anyerror);
18const BinSeekStream = io.SeekableStream(anyerror, anyerror);
19const ElfSeekStream = io.SeekableStream(anyerror, anyerror);
20const ElfInStream = io.InStream(anyerror);
21
2217const BinaryElfSection = struct {
2318 elfOffset: u64,
2419 binaryOffset: u64,
......@@ -41,22 +36,19 @@ const BinaryElfOutput = struct {
4136
4237 const Self = @This();
4338
44 pub fn init(allocator: *Allocator) Self {
45 return Self{
46 .segments = ArrayList(*BinaryElfSegment).init(allocator),
47 .sections = ArrayList(*BinaryElfSection).init(allocator),
48 };
49 }
50
5139 pub fn deinit(self: *Self) void {
5240 self.sections.deinit();
5341 self.segments.deinit();
5442 }
5543
56 pub fn parseElf(self: *Self, elfFile: elf.Elf) !void {
57 const allocator = self.segments.allocator;
44 pub fn parse(allocator: *Allocator, elf_file: File) !Self {
45 var self: Self = .{
46 .segments = ArrayList(*BinaryElfSegment).init(allocator),
47 .sections = ArrayList(*BinaryElfSection).init(allocator),
48 };
49 const elf_hdrs = try std.elf.readAllHeaders(allocator, elf_file);
5850
59 for (elfFile.section_headers) |section, i| {
51 for (elf_hdrs.section_headers) |section, i| {
6052 if (sectionValidForOutput(section)) {
6153 const newSection = try allocator.create(BinaryElfSection);
6254
......@@ -69,19 +61,19 @@ const BinaryElfOutput = struct {
6961 }
7062 }
7163
72 for (elfFile.program_headers) |programHeader, i| {
73 if (programHeader.p_type == elf.PT_LOAD) {
64 for (elf_hdrs.program_headers) |phdr, i| {
65 if (phdr.p_type == elf.PT_LOAD) {
7466 const newSegment = try allocator.create(BinaryElfSegment);
7567
76 newSegment.physicalAddress = if (programHeader.p_paddr != 0) programHeader.p_paddr else programHeader.p_vaddr;
77 newSegment.virtualAddress = programHeader.p_vaddr;
78 newSegment.fileSize = @intCast(usize, programHeader.p_filesz);
79 newSegment.elfOffset = programHeader.p_offset;
68 newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr;
69 newSegment.virtualAddress = phdr.p_vaddr;
70 newSegment.fileSize = @intCast(usize, phdr.p_filesz);
71 newSegment.elfOffset = phdr.p_offset;
8072 newSegment.binaryOffset = 0;
8173 newSegment.firstSection = null;
8274
8375 for (self.sections.toSlice()) |section| {
84 if (sectionWithinSegment(section, programHeader)) {
76 if (sectionWithinSegment(section, phdr)) {
8577 if (section.segment) |sectionSegment| {
8678 if (sectionSegment.elfOffset > newSegment.elfOffset) {
8779 section.segment = newSegment;
......@@ -126,14 +118,17 @@ const BinaryElfOutput = struct {
126118 }
127119
128120 sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare);
121
122 return self;
129123 }
130124
131 fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.ProgramHeader) bool {
125 fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool {
132126 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
133127 }
134128
135 fn sectionValidForOutput(section: elf.SectionHeader) bool {
136 return section.sh_size > 0 and section.sh_type != elf.SHT_NOBITS and ((section.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
129 fn sectionValidForOutput(shdr: var) bool {
130 return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and
131 ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
137132 }
138133
139134 fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool {
......@@ -151,60 +146,27 @@ const BinaryElfOutput = struct {
151146 }
152147};
153148
154const WriteContext = struct {
155 inStream: *ElfInStream,
156 inSeekStream: *ElfSeekStream,
157 outStream: *BinOutStream,
158 outSeekStream: *BinSeekStream,
159};
160
161fn writeBinaryElfSection(allocator: *Allocator, context: WriteContext, section: *BinaryElfSection) !void {
162 var readBuffer = try allocator.alloc(u8, section.fileSize);
163 defer allocator.free(readBuffer);
164
165 try context.inSeekStream.seekTo(section.elfOffset);
166 _ = try context.inStream.read(readBuffer);
149fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void {
150 try out_file.seekTo(section.binaryOffset);
167151
168 try context.outSeekStream.seekTo(section.binaryOffset);
169 try context.outStream.write(readBuffer);
152 try out_file.writeFileAll(elf_file, .{
153 .in_offset = section.elfOffset,
154 .in_len = section.fileSize,
155 });
170156}
171157
172fn emit_raw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
173 var arenaAlloc = ArenaAllocator.init(allocator);
174 errdefer arenaAlloc.deinit();
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 };
158fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
159 var elf_file = try fs.cwd().openFile(elf_path, .{});
160 defer elf_file.close();
200161
201 var binaryElfOutput = BinaryElfOutput.init(arena_allocator);
202 defer binaryElfOutput.deinit();
162 var out_file = try fs.cwd().createFile(raw_path, .{});
163 defer out_file.close();
203164
204 try binaryElfOutput.parseElf(elfFile);
165 var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file);
166 defer binary_elf_output.deinit();
205167
206 for (binaryElfOutput.sections.toSlice()) |section| {
207 try writeBinaryElfSection(allocator, writeContext, section);
168 for (binary_elf_output.sections.toSlice()) |section| {
169 try writeBinaryElfSection(elf_file, out_file, section);
208170 }
209171}
210172
......@@ -250,6 +212,6 @@ pub const InstallRawStep = struct {
250212 const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);
251213
252214 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);
254216 }
255217};
lib/std/build/run.zig+2-4
......@@ -175,8 +175,7 @@ pub const RunStep = struct {
175175
176176 switch (self.stdout_action) {
177177 .expect_exact, .expect_matches => {
178 var stdout_file_in_stream = child.stdout.?.inStream();
179 stdout = stdout_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
178 stdout = child.stdout.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
180179 },
181180 .inherit, .ignore => {},
182181 }
......@@ -186,8 +185,7 @@ pub const RunStep = struct {
186185
187186 switch (self.stderr_action) {
188187 .expect_exact, .expect_matches => {
189 var stderr_file_in_stream = child.stderr.?.inStream();
190 stderr = stderr_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
188 stderr = child.stderr.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
191189 },
192190 .inherit, .ignore => {},
193191 }
lib/std/child_process.zig+7-9
......@@ -217,13 +217,13 @@ pub const ChildProcess = struct {
217217
218218 try child.spawn();
219219
220 var stdout_file_in_stream = child.stdout.?.inStream();
221 var stderr_file_in_stream = child.stderr.?.inStream();
220 const stdout_in = child.stdout.?.inStream();
221 const stderr_in = child.stderr.?.inStream();
222222
223223 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
224 const stdout = try stdout_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
224 const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
225225 errdefer args.allocator.free(stdout);
226 const stderr = try stderr_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
226 const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
227227 errdefer args.allocator.free(stderr);
228228
229229 return ExecResult{
......@@ -780,7 +780,7 @@ fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8)
780780 var buf = try Buffer.initSize(allocator, 0);
781781 defer buf.deinit();
782782
783 var buf_stream = &io.BufferOutStream.init(&buf).stream;
783 var buf_stream = buf.outStream();
784784
785785 for (argv) |arg, arg_i| {
786786 if (arg_i != 0) try buf.appendByte(' ');
......@@ -857,8 +857,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
857857 .io_mode = .blocking,
858858 .async_block_allowed = File.async_block_allowed_yes,
859859 };
860 const stream = &file.outStream().stream;
861 stream.writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
860 file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
862861}
863862
864863fn readIntFd(fd: i32) !ErrInt {
......@@ -867,8 +866,7 @@ fn readIntFd(fd: i32) !ErrInt {
867866 .io_mode = .blocking,
868867 .async_block_allowed = File.async_block_allowed_yes,
869868 };
870 const stream = &file.inStream().stream;
871 return @intCast(ErrInt, stream.readIntNative(u64) catch return error.SystemResources);
869 return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);
872870}
873871
874872/// Caller must free result.
lib/std/coff.zig+6-9
......@@ -56,8 +56,7 @@ pub const Coff = struct {
5656 pub fn loadHeader(self: *Coff) !void {
5757 const pe_pointer_offset = 0x3C;
5858
59 var file_stream = self.in_file.inStream();
60 const in = &file_stream.stream;
59 const in = self.in_file.inStream();
6160
6261 var magic: [2]u8 = undefined;
6362 try in.readNoEof(magic[0..]);
......@@ -89,11 +88,11 @@ pub const Coff = struct {
8988 else => return error.InvalidMachine,
9089 }
9190
92 try self.loadOptionalHeader(&file_stream);
91 try self.loadOptionalHeader();
9392 }
9493
95 fn loadOptionalHeader(self: *Coff, file_stream: *File.InStream) !void {
96 const in = &file_stream.stream;
94 fn loadOptionalHeader(self: *Coff) !void {
95 const in = self.in_file.inStream();
9796 self.pe_header.magic = try in.readIntLittle(u16);
9897 // For now we're only interested in finding the reference to the .pdb,
9998 // so we'll skip most of this header, which size is different in 32
......@@ -136,8 +135,7 @@ pub const Coff = struct {
136135 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
137136 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
138137
139 var file_stream = self.in_file.inStream();
140 const in = &file_stream.stream;
138 const in = self.in_file.inStream();
141139 try self.in_file.seekTo(file_offset);
142140
143141 // Find the correct DebugDirectoryEntry, and where its data is stored.
......@@ -188,8 +186,7 @@ pub const Coff = struct {
188186
189187 try self.sections.ensureCapacity(self.coff_header.number_of_sections);
190188
191 var file_stream = self.in_file.inStream();
192 const in = &file_stream.stream;
189 const in = self.in_file.inStream();
193190
194191 var name: [8]u8 = undefined;
195192
lib/std/debug.zig+156-124
......@@ -55,7 +55,7 @@ pub const LineInfo = struct {
5555var stderr_file: File = undefined;
5656var stderr_file_out_stream: File.OutStream = undefined;
5757
58var stderr_stream: ?*io.OutStream(File.WriteError) = null;
58var stderr_stream: ?*File.OutStream = null;
5959var stderr_mutex = std.Mutex.init();
6060
6161pub fn warn(comptime fmt: []const u8, args: var) void {
......@@ -65,13 +65,13 @@ pub fn warn(comptime fmt: []const u8, args: var) void {
6565 noasync stderr.print(fmt, args) catch return;
6666}
6767
68pub fn getStderrStream() *io.OutStream(File.WriteError) {
68pub fn getStderrStream() *File.OutStream {
6969 if (stderr_stream) |st| {
7070 return st;
7171 } else {
7272 stderr_file = io.getStdErr();
7373 stderr_file_out_stream = stderr_file.outStream();
74 const st = &stderr_file_out_stream.stream;
74 const st = &stderr_file_out_stream;
7575 stderr_stream = st;
7676 return st;
7777 }
......@@ -408,15 +408,15 @@ pub const TTY = struct {
408408 windows_api,
409409
410410 fn setColor(conf: Config, out_stream: var, color: Color) void {
411 switch (conf) {
411 noasync switch (conf) {
412412 .no_color => return,
413413 .escape_codes => switch (color) {
414 .Red => noasync out_stream.write(RED) catch return,
415 .Green => noasync out_stream.write(GREEN) catch return,
416 .Cyan => noasync out_stream.write(CYAN) catch return,
417 .White, .Bold => noasync out_stream.write(WHITE) catch return,
418 .Dim => noasync out_stream.write(DIM) catch return,
419 .Reset => noasync out_stream.write(RESET) catch return,
414 .Red => out_stream.writeAll(RED) catch return,
415 .Green => out_stream.writeAll(GREEN) catch return,
416 .Cyan => out_stream.writeAll(CYAN) catch return,
417 .White, .Bold => out_stream.writeAll(WHITE) catch return,
418 .Dim => out_stream.writeAll(DIM) catch return,
419 .Reset => out_stream.writeAll(RESET) catch return,
420420 },
421421 .windows_api => if (builtin.os.tag == .windows) {
422422 const S = struct {
......@@ -455,7 +455,7 @@ pub const TTY = struct {
455455 } else {
456456 unreachable;
457457 },
458 }
458 };
459459 }
460460 };
461461};
......@@ -475,15 +475,15 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
475475
476476 const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
477477
478 const signature = try modi.stream.readIntLittle(u32);
478 const signature = try modi.inStream().readIntLittle(u32);
479479 if (signature != 4)
480480 return error.InvalidDebugInfo;
481481
482482 mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
483 try modi.stream.readNoEof(mod.symbols);
483 try modi.inStream().readNoEof(mod.symbols);
484484
485485 mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
486 try modi.stream.readNoEof(mod.subsect_info);
486 try modi.inStream().readNoEof(mod.subsect_info);
487487
488488 var sect_offset: usize = 0;
489489 var skip_len: usize = undefined;
......@@ -565,38 +565,40 @@ fn printLineInfo(
565565 tty_config: TTY.Config,
566566 comptime printLineFromFile: var,
567567) !void {
568 tty_config.setColor(out_stream, .White);
568 noasync {
569 tty_config.setColor(out_stream, .White);
569570
570 if (line_info) |*li| {
571 try noasync out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
572 } else {
573 try noasync out_stream.write("???:?:?");
574 }
571 if (line_info) |*li| {
572 try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
573 } else {
574 try out_stream.writeAll("???:?:?");
575 }
575576
576 tty_config.setColor(out_stream, .Reset);
577 try noasync out_stream.write(": ");
578 tty_config.setColor(out_stream, .Dim);
579 try noasync out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
580 tty_config.setColor(out_stream, .Reset);
581 try noasync out_stream.write("\n");
582
583 // Show the matching source code line if possible
584 if (line_info) |li| {
585 if (noasync printLineFromFile(out_stream, li)) {
586 if (li.column > 0) {
587 // The caret already takes one char
588 const space_needed = @intCast(usize, li.column - 1);
589
590 try noasync out_stream.writeByteNTimes(' ', space_needed);
591 tty_config.setColor(out_stream, .Green);
592 try noasync out_stream.write("^");
593 tty_config.setColor(out_stream, .Reset);
577 tty_config.setColor(out_stream, .Reset);
578 try out_stream.writeAll(": ");
579 tty_config.setColor(out_stream, .Dim);
580 try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
581 tty_config.setColor(out_stream, .Reset);
582 try out_stream.writeAll("\n");
583
584 // Show the matching source code line if possible
585 if (line_info) |li| {
586 if (printLineFromFile(out_stream, li)) {
587 if (li.column > 0) {
588 // The caret already takes one char
589 const space_needed = @intCast(usize, li.column - 1);
590
591 try out_stream.writeByteNTimes(' ', space_needed);
592 tty_config.setColor(out_stream, .Green);
593 try out_stream.writeAll("^");
594 tty_config.setColor(out_stream, .Reset);
595 }
596 try out_stream.writeAll("\n");
597 } else |err| switch (err) {
598 error.EndOfFile, error.FileNotFound => {},
599 error.BadPathName => {},
600 else => return err,
594601 }
595 try noasync out_stream.write("\n");
596 } else |err| switch (err) {
597 error.EndOfFile, error.FileNotFound => {},
598 error.BadPathName => {},
599 else => return err,
600602 }
601603 }
602604}
......@@ -609,21 +611,21 @@ pub const OpenSelfDebugInfoError = error{
609611};
610612
611613/// TODO resources https://github.com/ziglang/zig/issues/4353
612/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
613/// make this `noasync fn` and remove the individual noasync calls.
614614pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
615 if (builtin.strip_debug_info)
616 return error.MissingDebugInfo;
617 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
618 return noasync root.os.debug.openSelfDebugInfo(allocator);
619 }
620 switch (builtin.os.tag) {
621 .linux,
622 .freebsd,
623 .macosx,
624 .windows,
625 => return DebugInfo.init(allocator),
626 else => @compileError("openSelfDebugInfo unsupported for this platform"),
615 noasync {
616 if (builtin.strip_debug_info)
617 return error.MissingDebugInfo;
618 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
619 return root.os.debug.openSelfDebugInfo(allocator);
620 }
621 switch (builtin.os.tag) {
622 .linux,
623 .freebsd,
624 .macosx,
625 .windows,
626 => return DebugInfo.init(allocator),
627 else => @compileError("openSelfDebugInfo unsupported for this platform"),
628 }
627629 }
628630}
629631
......@@ -654,11 +656,11 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
654656 try di.pdb.openFile(di.coff, path);
655657
656658 var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
657 const version = try pdb_stream.stream.readIntLittle(u32);
658 const signature = try pdb_stream.stream.readIntLittle(u32);
659 const age = try pdb_stream.stream.readIntLittle(u32);
659 const version = try pdb_stream.inStream().readIntLittle(u32);
660 const signature = try pdb_stream.inStream().readIntLittle(u32);
661 const age = try pdb_stream.inStream().readIntLittle(u32);
660662 var guid: [16]u8 = undefined;
661 try pdb_stream.stream.readNoEof(&guid);
663 try pdb_stream.inStream().readNoEof(&guid);
662664 if (version != 20000404) // VC70, only value observed by LLVM team
663665 return error.UnknownPDBVersion;
664666 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) !
666668 // We validated the executable and pdb match.
667669
668670 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);
670672 const name_bytes = try allocator.alloc(u8, name_bytes_len);
671 try pdb_stream.stream.readNoEof(name_bytes);
673 try pdb_stream.inStream().readNoEof(name_bytes);
672674
673675 const HashTableHeader = packed struct {
674676 Size: u32,
......@@ -678,17 +680,17 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
678680 return cap * 2 / 3 + 1;
679681 }
680682 };
681 const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader);
683 const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader);
682684 if (hash_tbl_hdr.Capacity == 0)
683685 return error.InvalidDebugInfo;
684686
685687 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
686688 return error.InvalidDebugInfo;
687689
688 const present = try readSparseBitVector(&pdb_stream.stream, allocator);
690 const present = try readSparseBitVector(&pdb_stream.inStream(), allocator);
689691 if (present.len != hash_tbl_hdr.Size)
690692 return error.InvalidDebugInfo;
691 const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
693 const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator);
692694
693695 const Bucket = struct {
694696 first: u32,
......@@ -696,8 +698,8 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
696698 };
697699 const bucket_list = try allocator.alloc(Bucket, present.len);
698700 for (present) |_| {
699 const name_offset = try pdb_stream.stream.readIntLittle(u32);
700 const name_index = try pdb_stream.stream.readIntLittle(u32);
701 const name_offset = try pdb_stream.inStream().readIntLittle(u32);
702 const name_index = try pdb_stream.inStream().readIntLittle(u32);
701703 const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset));
702704 if (mem.eql(u8, name, "/names")) {
703705 break :str_tab_index name_index;
......@@ -712,7 +714,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
712714 const dbi = di.pdb.dbi;
713715
714716 // Dbi Header
715 const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader);
717 const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader);
716718 if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team
717719 return error.UnknownPDBVersion;
718720 if (dbi_stream_header.Age != age)
......@@ -726,7 +728,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
726728 // Module Info Substream
727729 var mod_info_offset: usize = 0;
728730 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);
730732 var this_record_len: usize = @sizeOf(pdb.ModInfo);
731733
732734 const module_name = try dbi.readNullTermString(allocator);
......@@ -764,14 +766,14 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
764766 var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
765767 var sect_cont_offset: usize = 0;
766768 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));
768770 if (ver != pdb.SectionContrSubstreamVersion.Ver60)
769771 return error.InvalidDebugInfo;
770772 sect_cont_offset += @sizeOf(u32);
771773 }
772774 while (sect_cont_offset != section_contrib_size) {
773775 const entry = try sect_contribs.addOne();
774 entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry);
776 entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry);
775777 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
776778
777779 if (sect_cont_offset > section_contrib_size)
......@@ -808,45 +810,71 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
808810
809811/// TODO resources https://github.com/ziglang/zig/issues/4353
810812pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
811 const mapped_mem = try mapWholeFile(elf_file_path);
812
813 var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
814 var efile = try noasync elf.Elf.openStream(
815 allocator,
816 @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
817 @ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
818 );
819 defer noasync efile.close();
813 noasync {
814 const mapped_mem = try mapWholeFile(elf_file_path);
815 const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
816 if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
817 if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
818
819 const endian: builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
820 elf.ELFDATA2LSB => .Little,
821 elf.ELFDATA2MSB => .Big,
822 else => return error.InvalidElfEndian,
823 };
824 assert(endian == std.builtin.endian); // this is our own debug info
825
826 const shoff = hdr.e_shoff;
827 const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
828 const str_shdr = @ptrCast(
829 *const elf.Shdr,
830 @alignCast(@alignOf(elf.Shdr), &mapped_mem[try math.cast(usize, str_section_off)]),
831 );
832 const header_strings = mapped_mem[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size];
833 const shdrs = @ptrCast(
834 [*]const elf.Shdr,
835 @alignCast(@alignOf(elf.Shdr), &mapped_mem[shoff]),
836 )[0..hdr.e_shnum];
837
838 var opt_debug_info: ?[]const u8 = null;
839 var opt_debug_abbrev: ?[]const u8 = null;
840 var opt_debug_str: ?[]const u8 = null;
841 var opt_debug_line: ?[]const u8 = null;
842 var opt_debug_ranges: ?[]const u8 = null;
843
844 for (shdrs) |*shdr| {
845 if (shdr.sh_type == elf.SHT_NULL) continue;
846
847 const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr));
848 if (mem.eql(u8, name, ".debug_info")) {
849 opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
850 } else if (mem.eql(u8, name, ".debug_abbrev")) {
851 opt_debug_abbrev = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
852 } else if (mem.eql(u8, name, ".debug_str")) {
853 opt_debug_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
854 } else if (mem.eql(u8, name, ".debug_line")) {
855 opt_debug_line = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
856 } else if (mem.eql(u8, name, ".debug_ranges")) {
857 opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
858 }
859 }
820860
821 const debug_info = (try noasync efile.findSection(".debug_info")) orelse
822 return error.MissingDebugInfo;
823 const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse
824 return error.MissingDebugInfo;
825 const debug_str = (try noasync efile.findSection(".debug_str")) orelse
826 return error.MissingDebugInfo;
827 const debug_line = (try noasync efile.findSection(".debug_line")) orelse
828 return error.MissingDebugInfo;
829 const opt_debug_ranges = try noasync efile.findSection(".debug_ranges");
830
831 var di = DW.DwarfInfo{
832 .endian = efile.endian,
833 .debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
834 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
835 .debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
836 .debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
837 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
838 try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size)
839 else
840 null,
841 };
861 var di = DW.DwarfInfo{
862 .endian = endian,
863 .debug_info = opt_debug_info orelse return error.MissingDebugInfo,
864 .debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
865 .debug_str = opt_debug_str orelse return error.MissingDebugInfo,
866 .debug_line = opt_debug_line orelse return error.MissingDebugInfo,
867 .debug_ranges = opt_debug_ranges,
868 };
842869
843 try noasync DW.openDwarfDebugInfo(&di, allocator);
870 try DW.openDwarfDebugInfo(&di, allocator);
844871
845 return ModuleDebugInfo{
846 .base_address = undefined,
847 .dwarf = di,
848 .mapped_memory = mapped_mem,
849 };
872 return ModuleDebugInfo{
873 .base_address = undefined,
874 .dwarf = di,
875 .mapped_memory = mapped_mem,
876 };
877 }
850878}
851879
852880/// TODO resources https://github.com/ziglang/zig/issues/4353
......@@ -936,7 +964,9 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M
936964}
937965
938966fn 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 });
940970 defer f.close();
941971 // TODO fstat and make sure that the file has the correct size
942972
......@@ -982,22 +1012,24 @@ const MachoSymbol = struct {
9821012 }
9831013};
9841014
985fn mapWholeFile(path: []const u8) ![]const u8 {
986 const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true });
987 defer noasync file.close();
988
989 const file_len = try math.cast(usize, try file.getEndPos());
990 const mapped_mem = try os.mmap(
991 null,
992 file_len,
993 os.PROT_READ,
994 os.MAP_SHARED,
995 file.handle,
996 0,
997 );
998 errdefer os.munmap(mapped_mem);
1015fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
1016 noasync {
1017 const file = try fs.openFileAbsolute(path, .{ .always_blocking = true });
1018 defer file.close();
9991019
1000 return mapped_mem;
1020 const file_len = try math.cast(usize, try file.getEndPos());
1021 const mapped_mem = try os.mmap(
1022 null,
1023 file_len,
1024 os.PROT_READ,
1025 os.MAP_SHARED,
1026 file.handle,
1027 0,
1028 );
1029 errdefer os.munmap(mapped_mem);
1030
1031 return mapped_mem;
1032 }
10011033}
10021034
10031035pub const DebugInfo = struct {
lib/std/debug/leb128.zig+12-12
......@@ -121,18 +121,18 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
121121}
122122
123123fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
124 var in_stream = std.io.SliceInStream.init(encoded);
125 return try readILEB128(T, &in_stream.stream);
124 var in_stream = std.io.fixedBufferStream(encoded);
125 return try readILEB128(T, in_stream.inStream());
126126}
127127
128128fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
129 var in_stream = std.io.SliceInStream.init(encoded);
130 return try readULEB128(T, &in_stream.stream);
129 var in_stream = std.io.fixedBufferStream(encoded);
130 return try readULEB128(T, in_stream.inStream());
131131}
132132
133133fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
134 var in_stream = std.io.SliceInStream.init(encoded);
135 const v1 = readILEB128(T, &in_stream.stream);
134 var in_stream = std.io.fixedBufferStream(encoded);
135 const v1 = readILEB128(T, in_stream.inStream());
136136 var in_ptr = encoded.ptr;
137137 const v2 = readILEB128Mem(T, &in_ptr);
138138 testing.expectEqual(v1, v2);
......@@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
140140}
141141
142142fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
143 var in_stream = std.io.SliceInStream.init(encoded);
144 const v1 = readULEB128(T, &in_stream.stream);
143 var in_stream = std.io.fixedBufferStream(encoded);
144 const v1 = readULEB128(T, in_stream.inStream());
145145 var in_ptr = encoded.ptr;
146146 const v2 = readULEB128Mem(T, &in_ptr);
147147 testing.expectEqual(v1, v2);
......@@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
149149}
150150
151151fn 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);
153153 var in_ptr = encoded.ptr;
154154 var i: usize = 0;
155155 while (i < N) : (i += 1) {
156 const v1 = readILEB128(T, &in_stream.stream);
156 const v1 = readILEB128(T, in_stream.inStream());
157157 const v2 = readILEB128Mem(T, &in_ptr);
158158 testing.expectEqual(v1, v2);
159159 }
160160}
161161
162162fn 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);
164164 var in_ptr = encoded.ptr;
165165 var i: usize = 0;
166166 while (i < N) : (i += 1) {
167 const v1 = readULEB128(T, &in_stream.stream);
167 const v1 = readULEB128(T, in_stream.inStream());
168168 const v2 = readULEB128Mem(T, &in_ptr);
169169 testing.expectEqual(v1, v2);
170170 }
lib/std/dwarf.zig+84-77
......@@ -11,9 +11,6 @@ const ArrayList = std.ArrayList;
1111
1212usingnamespace @import("dwarf_bits.zig");
1313
14pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
15pub const DwarfInStream = io.InStream(anyerror);
16
1714const PcRange = struct {
1815 start: u64,
1916 end: u64,
......@@ -239,7 +236,7 @@ const LineNumberProgram = struct {
239236 }
240237};
241238
242fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
239fn readInitialLength(in_stream: var, is_64: *bool) !u64 {
243240 const first_32_bits = try in_stream.readIntLittle(u32);
244241 is_64.* = (first_32_bits == 0xffffffff);
245242 if (is_64.*) {
......@@ -414,40 +411,42 @@ pub const DwarfInfo = struct {
414411 }
415412
416413 fn scanAllFunctions(di: *DwarfInfo) !void {
417 var s = io.SliceSeekableInStream.init(di.debug_info);
414 var stream = io.fixedBufferStream(di.debug_info);
415 const in = &stream.inStream();
416 const seekable = &stream.seekableStream();
418417 var this_unit_offset: u64 = 0;
419418
420 while (this_unit_offset < try s.seekable_stream.getEndPos()) {
421 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
419 while (this_unit_offset < try seekable.getEndPos()) {
420 seekable.seekTo(this_unit_offset) catch |err| switch (err) {
422421 error.EndOfStream => unreachable,
423422 else => return err,
424423 };
425424
426425 var is_64: bool = undefined;
427 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
426 const unit_length = try readInitialLength(in, &is_64);
428427 if (unit_length == 0) return;
429428 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
430429
431 const version = try s.stream.readInt(u16, di.endian);
430 const version = try in.readInt(u16, di.endian);
432431 if (version < 2 or version > 5) return error.InvalidDebugInfo;
433432
434 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
433 const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
435434
436 const address_size = try s.stream.readByte();
435 const address_size = try in.readByte();
437436 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
438437
439 const compile_unit_pos = try s.seekable_stream.getPos();
438 const compile_unit_pos = try seekable.getPos();
440439 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
441440
442 try s.seekable_stream.seekTo(compile_unit_pos);
441 try seekable.seekTo(compile_unit_pos);
443442
444443 const next_unit_pos = this_unit_offset + next_offset;
445444
446 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
447 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
445 while ((try seekable.getPos()) < next_unit_pos) {
446 const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue;
448447 defer die_obj.attrs.deinit();
449448
450 const after_die_offset = try s.seekable_stream.getPos();
449 const after_die_offset = try seekable.getPos();
451450
452451 switch (die_obj.tag_id) {
453452 TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
......@@ -463,14 +462,14 @@ pub const DwarfInfo = struct {
463462 // Follow the DIE it points to and repeat
464463 const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
465464 if (ref_offset > next_offset) return error.InvalidDebugInfo;
466 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
467 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
465 try seekable.seekTo(this_unit_offset + ref_offset);
466 this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
468467 } else if (this_die_obj.getAttr(AT_specification)) |ref| {
469468 // Follow the DIE it points to and repeat
470469 const ref_offset = try this_die_obj.getAttrRef(AT_specification);
471470 if (ref_offset > next_offset) return error.InvalidDebugInfo;
472 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
473 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
471 try seekable.seekTo(this_unit_offset + ref_offset);
472 this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
474473 } else {
475474 break :x null;
476475 }
......@@ -511,7 +510,7 @@ pub const DwarfInfo = struct {
511510 else => {},
512511 }
513512
514 try s.seekable_stream.seekTo(after_die_offset);
513 try seekable.seekTo(after_die_offset);
515514 }
516515
517516 this_unit_offset += next_offset;
......@@ -519,35 +518,37 @@ pub const DwarfInfo = struct {
519518 }
520519
521520 fn scanAllCompileUnits(di: *DwarfInfo) !void {
522 var s = io.SliceSeekableInStream.init(di.debug_info);
521 var stream = io.fixedBufferStream(di.debug_info);
522 const in = &stream.inStream();
523 const seekable = &stream.seekableStream();
523524 var this_unit_offset: u64 = 0;
524525
525 while (this_unit_offset < try s.seekable_stream.getEndPos()) {
526 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
526 while (this_unit_offset < try seekable.getEndPos()) {
527 seekable.seekTo(this_unit_offset) catch |err| switch (err) {
527528 error.EndOfStream => unreachable,
528529 else => return err,
529530 };
530531
531532 var is_64: bool = undefined;
532 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
533 const unit_length = try readInitialLength(in, &is_64);
533534 if (unit_length == 0) return;
534535 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
535536
536 const version = try s.stream.readInt(u16, di.endian);
537 const version = try in.readInt(u16, di.endian);
537538 if (version < 2 or version > 5) return error.InvalidDebugInfo;
538539
539 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
540 const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
540541
541 const address_size = try s.stream.readByte();
542 const address_size = try in.readByte();
542543 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
543544
544 const compile_unit_pos = try s.seekable_stream.getPos();
545 const compile_unit_pos = try seekable.getPos();
545546 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
546547
547 try s.seekable_stream.seekTo(compile_unit_pos);
548 try seekable.seekTo(compile_unit_pos);
548549
549550 const compile_unit_die = try di.allocator().create(Die);
550 compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
551 compile_unit_die.* = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
551552
552553 if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
553554
......@@ -593,7 +594,9 @@ pub const DwarfInfo = struct {
593594 }
594595 if (di.debug_ranges) |debug_ranges| {
595596 if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
596 var s = io.SliceSeekableInStream.init(debug_ranges);
597 var stream = io.fixedBufferStream(debug_ranges);
598 const in = &stream.inStream();
599 const seekable = &stream.seekableStream();
597600
598601 // All the addresses in the list are relative to the value
599602 // specified by DW_AT_low_pc or to some other value encoded
......@@ -604,11 +607,11 @@ pub const DwarfInfo = struct {
604607 else => return err,
605608 };
606609
607 try s.seekable_stream.seekTo(ranges_offset);
610 try seekable.seekTo(ranges_offset);
608611
609612 while (true) {
610 const begin_addr = try s.stream.readIntLittle(usize);
611 const end_addr = try s.stream.readIntLittle(usize);
613 const begin_addr = try in.readIntLittle(usize);
614 const end_addr = try in.readIntLittle(usize);
612615 if (begin_addr == 0 and end_addr == 0) {
613616 break;
614617 }
......@@ -646,25 +649,27 @@ pub const DwarfInfo = struct {
646649 }
647650
648651 fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
649 var s = io.SliceSeekableInStream.init(di.debug_abbrev);
652 var stream = io.fixedBufferStream(di.debug_abbrev);
653 const in = &stream.inStream();
654 const seekable = &stream.seekableStream();
650655
651 try s.seekable_stream.seekTo(offset);
656 try seekable.seekTo(offset);
652657 var result = AbbrevTable.init(di.allocator());
653658 errdefer result.deinit();
654659 while (true) {
655 const abbrev_code = try leb.readULEB128(u64, &s.stream);
660 const abbrev_code = try leb.readULEB128(u64, in);
656661 if (abbrev_code == 0) return result;
657662 try result.append(AbbrevTableEntry{
658663 .abbrev_code = abbrev_code,
659 .tag_id = try leb.readULEB128(u64, &s.stream),
660 .has_children = (try s.stream.readByte()) == CHILDREN_yes,
664 .tag_id = try leb.readULEB128(u64, in),
665 .has_children = (try in.readByte()) == CHILDREN_yes,
661666 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
662667 });
663668 const attrs = &result.items[result.len - 1].attrs;
664669
665670 while (true) {
666 const attr_id = try leb.readULEB128(u64, &s.stream);
667 const form_id = try leb.readULEB128(u64, &s.stream);
671 const attr_id = try leb.readULEB128(u64, in);
672 const form_id = try leb.readULEB128(u64, in);
668673 if (attr_id == 0 and form_id == 0) break;
669674 try attrs.append(AbbrevAttr{
670675 .attr_id = attr_id,
......@@ -695,42 +700,44 @@ pub const DwarfInfo = struct {
695700 }
696701
697702 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
698 var s = io.SliceSeekableInStream.init(di.debug_line);
703 var stream = io.fixedBufferStream(di.debug_line);
704 const in = &stream.inStream();
705 const seekable = &stream.seekableStream();
699706
700707 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
701708 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list);
702709
703 try s.seekable_stream.seekTo(line_info_offset);
710 try seekable.seekTo(line_info_offset);
704711
705712 var is_64: bool = undefined;
706 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
713 const unit_length = try readInitialLength(in, &is_64);
707714 if (unit_length == 0) {
708715 return error.MissingDebugInfo;
709716 }
710717 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
711718
712 const version = try s.stream.readInt(u16, di.endian);
719 const version = try in.readInt(u16, di.endian);
713720 // TODO support 3 and 5
714721 if (version != 2 and version != 4) return error.InvalidDebugInfo;
715722
716 const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
717 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
723 const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
724 const prog_start_offset = (try seekable.getPos()) + prologue_length;
718725
719 const minimum_instruction_length = try s.stream.readByte();
726 const minimum_instruction_length = try in.readByte();
720727 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
721728
722729 if (version >= 4) {
723730 // maximum_operations_per_instruction
724 _ = try s.stream.readByte();
731 _ = try in.readByte();
725732 }
726733
727 const default_is_stmt = (try s.stream.readByte()) != 0;
728 const line_base = try s.stream.readByteSigned();
734 const default_is_stmt = (try in.readByte()) != 0;
735 const line_base = try in.readByteSigned();
729736
730 const line_range = try s.stream.readByte();
737 const line_range = try in.readByte();
731738 if (line_range == 0) return error.InvalidDebugInfo;
732739
733 const opcode_base = try s.stream.readByte();
740 const opcode_base = try in.readByte();
734741
735742 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
736743 defer di.allocator().free(standard_opcode_lengths);
......@@ -738,14 +745,14 @@ pub const DwarfInfo = struct {
738745 {
739746 var i: usize = 0;
740747 while (i < opcode_base - 1) : (i += 1) {
741 standard_opcode_lengths[i] = try s.stream.readByte();
748 standard_opcode_lengths[i] = try in.readByte();
742749 }
743750 }
744751
745752 var include_directories = ArrayList([]const u8).init(di.allocator());
746753 try include_directories.append(compile_unit_cwd);
747754 while (true) {
748 const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
755 const dir = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
749756 if (dir.len == 0) break;
750757 try include_directories.append(dir);
751758 }
......@@ -754,11 +761,11 @@ pub const DwarfInfo = struct {
754761 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
755762
756763 while (true) {
757 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
764 const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
758765 if (file_name.len == 0) break;
759 const dir_index = try leb.readULEB128(usize, &s.stream);
760 const mtime = try leb.readULEB128(usize, &s.stream);
761 const len_bytes = try leb.readULEB128(usize, &s.stream);
766 const dir_index = try leb.readULEB128(usize, in);
767 const mtime = try leb.readULEB128(usize, in);
768 const len_bytes = try leb.readULEB128(usize, in);
762769 try file_entries.append(FileEntry{
763770 .file_name = file_name,
764771 .dir_index = dir_index,
......@@ -767,17 +774,17 @@ pub const DwarfInfo = struct {
767774 });
768775 }
769776
770 try s.seekable_stream.seekTo(prog_start_offset);
777 try seekable.seekTo(prog_start_offset);
771778
772779 const next_unit_pos = line_info_offset + next_offset;
773780
774 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
775 const opcode = try s.stream.readByte();
781 while ((try seekable.getPos()) < next_unit_pos) {
782 const opcode = try in.readByte();
776783
777784 if (opcode == LNS_extended_op) {
778 const op_size = try leb.readULEB128(u64, &s.stream);
785 const op_size = try leb.readULEB128(u64, in);
779786 if (op_size < 1) return error.InvalidDebugInfo;
780 var sub_op = try s.stream.readByte();
787 var sub_op = try in.readByte();
781788 switch (sub_op) {
782789 LNE_end_sequence => {
783790 prog.end_sequence = true;
......@@ -785,14 +792,14 @@ pub const DwarfInfo = struct {
785792 prog.reset();
786793 },
787794 LNE_set_address => {
788 const addr = try s.stream.readInt(usize, di.endian);
795 const addr = try in.readInt(usize, di.endian);
789796 prog.address = addr;
790797 },
791798 LNE_define_file => {
792 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
793 const dir_index = try leb.readULEB128(usize, &s.stream);
794 const mtime = try leb.readULEB128(usize, &s.stream);
795 const len_bytes = try leb.readULEB128(usize, &s.stream);
799 const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
800 const dir_index = try leb.readULEB128(usize, in);
801 const mtime = try leb.readULEB128(usize, in);
802 const len_bytes = try leb.readULEB128(usize, in);
796803 try file_entries.append(FileEntry{
797804 .file_name = file_name,
798805 .dir_index = dir_index,
......@@ -802,7 +809,7 @@ pub const DwarfInfo = struct {
802809 },
803810 else => {
804811 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
805 try s.seekable_stream.seekBy(fwd_amt);
812 try seekable.seekBy(fwd_amt);
806813 },
807814 }
808815 } else if (opcode >= opcode_base) {
......@@ -821,19 +828,19 @@ pub const DwarfInfo = struct {
821828 prog.basic_block = false;
822829 },
823830 LNS_advance_pc => {
824 const arg = try leb.readULEB128(usize, &s.stream);
831 const arg = try leb.readULEB128(usize, in);
825832 prog.address += arg * minimum_instruction_length;
826833 },
827834 LNS_advance_line => {
828 const arg = try leb.readILEB128(i64, &s.stream);
835 const arg = try leb.readILEB128(i64, in);
829836 prog.line += arg;
830837 },
831838 LNS_set_file => {
832 const arg = try leb.readULEB128(usize, &s.stream);
839 const arg = try leb.readULEB128(usize, in);
833840 prog.file = arg;
834841 },
835842 LNS_set_column => {
836 const arg = try leb.readULEB128(u64, &s.stream);
843 const arg = try leb.readULEB128(u64, in);
837844 prog.column = arg;
838845 },
839846 LNS_negate_stmt => {
......@@ -847,14 +854,14 @@ pub const DwarfInfo = struct {
847854 prog.address += inc_addr;
848855 },
849856 LNS_fixed_advance_pc => {
850 const arg = try s.stream.readInt(u16, di.endian);
857 const arg = try in.readInt(u16, di.endian);
851858 prog.address += arg;
852859 },
853860 LNS_set_prologue_end => {},
854861 else => {
855862 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
856863 const len_bytes = standard_opcode_lengths[opcode - 1];
857 try s.seekable_stream.seekBy(len_bytes);
864 try seekable.seekBy(len_bytes);
858865 },
859866 }
860867 }
lib/std/elf.zig+207-193
......@@ -1,5 +1,5 @@
1const builtin = @import("builtin");
21const std = @import("std.zig");
2const builtin = std.builtin;
33const io = std.io;
44const os = std.os;
55const math = std.math;
......@@ -330,218 +330,232 @@ pub const ET = extern enum(u16) {
330330 pub const HIPROC = 0xffff;
331331};
332332
333pub const SectionHeader = Elf64_Shdr;
334pub const ProgramHeader = Elf64_Phdr;
335
336pub const Elf = struct {
337 seekable_stream: *io.SeekableStream(anyerror, anyerror),
338 in_stream: *io.InStream(anyerror),
339 is_64: bool,
333/// All integers are native endian.
334const Header = struct {
340335 endian: builtin.Endian,
341 file_type: ET,
342 arch: EM,
343 entry_addr: u64,
344 program_header_offset: u64,
345 section_header_offset: u64,
346 string_section_index: usize,
347 string_section: *SectionHeader,
348 section_headers: []SectionHeader,
349 program_headers: []ProgramHeader,
350 allocator: *mem.Allocator,
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);
336 is_64: bool,
337 entry: u64,
338 phoff: u64,
339 shoff: u64,
340 phentsize: u16,
341 phnum: u16,
342 shentsize: u16,
343 shnum: u16,
344 shstrndx: u16,
345};
383346
384 elf.file_type = try in.readEnum(ET, elf.endian);
385 elf.arch = try in.readEnum(EM, elf.endian);
347pub fn readHeader(file: File) !Header {
348 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
349 try preadNoEof(file, &hdr_buf, 0);
350 const hdr32 = @ptrCast(*Elf32_Ehdr, &hdr_buf);
351 const hdr64 = @ptrCast(*Elf64_Ehdr, &hdr_buf);
352 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
353 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
354
355 const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) {
356 ELFDATA2LSB => .Little,
357 ELFDATA2MSB => .Big,
358 else => return error.InvalidElfEndian,
359 };
360 const need_bswap = endian != std.builtin.endian;
361
362 const is_64 = switch (hdr32.e_ident[EI_CLASS]) {
363 ELFCLASS32 => false,
364 ELFCLASS64 => true,
365 else => return error.InvalidElfClass,
366 };
367
368 return @as(Header, .{
369 .endian = endian,
370 .is_64 = is_64,
371 .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),
372 .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),
373 .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),
374 .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),
375 .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),
376 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
377 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
378 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
379 });
380}
386381
387 const elf_version = try in.readInt(u32, elf.endian);
388 if (elf_version != 1) return error.InvalidFormat;
382/// All integers are native endian.
383pub const AllHeaders = struct {
384 header: Header,
385 section_headers: []Elf64_Shdr,
386 program_headers: []Elf64_Phdr,
387 allocator: *mem.Allocator,
388};
389389
390 if (elf.is_64) {
391 elf.entry_addr = try in.readInt(u64, elf.endian);
392 elf.program_header_offset = try in.readInt(u64, elf.endian);
393 elf.section_header_offset = try in.readInt(u64, elf.endian);
394 } else {
395 elf.entry_addr = @as(u64, try in.readInt(u32, elf.endian));
396 elf.program_header_offset = @as(u64, try in.readInt(u32, elf.endian));
397 elf.section_header_offset = @as(u64, try in.readInt(u32, elf.endian));
390pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
391 var hdrs: AllHeaders = .{
392 .allocator = allocator,
393 .header = try readHeader(file),
394 .section_headers = undefined,
395 .program_headers = undefined,
396 };
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 }
398442 }
399443
400 // skip over flags
401 try seekable_stream.seekBy(4);
444 return hdrs;
445 }
402446
403 const header_size = try in.readInt(u16, elf.endian);
404 if ((elf.is_64 and header_size != @sizeOf(Elf64_Ehdr)) or (!elf.is_64 and header_size != @sizeOf(Elf32_Ehdr))) {
405 return error.InvalidFormat;
447 const shdrs_32 = try allocator.alloc(Elf32_Shdr, hdrs.header.shnum);
448 defer allocator.free(shdrs_32);
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 };
406473 }
407
408 const ph_entry_size = try in.readInt(u16, elf.endian);
409 const ph_entry_count = try in.readInt(u16, elf.endian);
410
411 if ((elf.is_64 and ph_entry_size != @sizeOf(Elf64_Phdr)) or (!elf.is_64 and ph_entry_size != @sizeOf(Elf32_Phdr))) {
412 return error.InvalidFormat;
474 for (hdrs.program_headers) |*phdr, i| {
475 const o = phdrs_32[i];
476 phdr.* = .{
477 .p_type = @byteSwap(@TypeOf(o.p_type), o.p_type),
478 .p_offset = @byteSwap(@TypeOf(o.p_offset), o.p_offset),
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 };
413486 }
414
415 const sh_entry_size = try in.readInt(u16, elf.endian);
416 const sh_entry_count = try in.readInt(u16, elf.endian);
417
418 if ((elf.is_64 and sh_entry_size != @sizeOf(Elf64_Shdr)) or (!elf.is_64 and sh_entry_size != @sizeOf(Elf32_Shdr))) {
419 return error.InvalidFormat;
487 } else {
488 for (hdrs.section_headers) |*shdr, i| {
489 const o = shdrs_32[i];
490 shdr.* = .{
491 .sh_name = o.sh_name,
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 };
420502 }
421
422 elf.string_section_index = @as(usize, try in.readInt(u16, elf.endian));
423
424 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
425
426 const sh_byte_count = @as(u64, sh_entry_size) * @as(u64, sh_entry_count);
427 const end_sh = try math.add(u64, elf.section_header_offset, sh_byte_count);
428 const ph_byte_count = @as(u64, ph_entry_size) * @as(u64, ph_entry_count);
429 const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count);
430
431 const stream_end = try seekable_stream.getEndPos();
432 if (stream_end < end_sh or stream_end < end_ph) {
433 return error.InvalidFormat;
503 for (hdrs.program_headers) |*phdr, i| {
504 const o = phdrs_32[i];
505 phdr.* = .{
506 .p_type = o.p_type,
507 .p_offset = o.p_offset,
508 .p_vaddr = o.p_vaddr,
509 .p_paddr = o.p_paddr,
510 .p_filesz = o.p_filesz,
511 .p_memsz = o.p_memsz,
512 .p_flags = o.p_flags,
513 .p_align = o.p_align,
514 };
434515 }
516 }
435517
436 try seekable_stream.seekTo(elf.program_header_offset);
437
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 }
518 return hdrs;
519}
464520
465 try seekable_stream.seekTo(elf.section_header_offset);
466
467 elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count);
468 errdefer elf.allocator.free(elf.section_headers);
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 }
521pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
522 if (is_64) {
523 if (need_bswap) {
524 return @byteSwap(@TypeOf(int_64), int_64);
483525 } else {
484 for (elf.section_headers) |*elf_section| {
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 }
526 return int_64;
497527 }
498
499 for (elf.section_headers) |*elf_section| {
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;
528 } else {
529 return int32(need_bswap, int_32, @TypeOf(int_64));
513530 }
531}
514532
515 pub fn close(elf: *Elf) void {
516 elf.allocator.free(elf.section_headers);
517 elf.allocator.free(elf.program_headers);
518 }
519
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;
533pub fn int32(need_bswap: bool, int_32: var, comptime Int64: var) Int64 {
534 if (need_bswap) {
535 return @byteSwap(@TypeOf(int_32), int_32);
536 } else {
537 return int_32;
539538 }
539}
540540
541 pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void {
542 try elf.seekable_stream.seekTo(elf_section.sh_offset);
541fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
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;
543557 }
544};
558}
545559
546560pub const EI_NIDENT = 16;
547561
lib/std/event/group.zig+3-1
......@@ -120,9 +120,11 @@ test "std.event.Group" {
120120 // https://github.com/ziglang/zig/issues/1908
121121 if (builtin.single_threaded) return error.SkipZigTest;
122122
123 // TODO provide a way to run tests in evented I/O mode
124123 if (!std.io.is_async) return error.SkipZigTest;
125124
125 // TODO this file has bit-rotted. repair it
126 if (true) return error.SkipZigTest;
127
126128 const handle = async testGroup(std.heap.page_allocator);
127129}
128130
lib/std/event/lock.zig+3
......@@ -125,6 +125,9 @@ test "std.event.Lock" {
125125 // TODO https://github.com/ziglang/zig/issues/3251
126126 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
127127
128 // TODO this file has bit-rotted. repair it
129 if (true) return error.SkipZigTest;
130
128131 var lock = Lock.init();
129132 defer lock.deinit();
130133
lib/std/fs.zig+13-22
......@@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
9696/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
9797/// Returns the previous status of the file before updating.
9898/// If any of the directories do not exist for dest_path, they are created.
99/// TODO rework this to integrate with Dir
99100pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
100101 const my_cwd = cwd();
101102
......@@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
141142/// there is a possibility of power loss or application termination leaving temporary files present
142143/// in the same directory as dest_path.
143144/// Destination file will have the same mode as the source file.
145/// TODO rework this to integrate with Dir
144146pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
145147 var in_file = try cwd().openFile(source_path, .{});
146148 defer in_file.close();
147149
148 const mode = try in_file.mode();
149 const in_stream = &in_file.inStream().stream;
150 const stat = try in_file.stat();
150151
151 var atomic_file = try AtomicFile.init(dest_path, mode);
152 var atomic_file = try AtomicFile.init(dest_path, stat.mode);
152153 defer atomic_file.deinit();
153154
154 var buf: [mem.page_size]u8 = undefined;
155 while (true) {
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 }
155 try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
156 return atomic_file.finish();
162157}
163158
164/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
165/// merged and readily available,
159/// Guaranteed to be atomic.
160/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
166161/// 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
167164pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
168165 var in_file = try cwd().openFile(source_path, .{});
169166 defer in_file.close();
......@@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
171168 var atomic_file = try AtomicFile.init(dest_path, mode);
172169 defer atomic_file.deinit();
173170
174 var buf: [mem.page_size * 6]u8 = undefined;
175 while (true) {
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 }
171 try atomic_file.file.writeFileAll(in_file, .{});
172 return atomic_file.finish();
182173}
183174
184175/// TODO update this API to avoid a getrandom syscall for every operation. It
......@@ -1150,7 +1141,7 @@ pub const Dir = struct {
11501141 const buf = try allocator.alignedAlloc(u8, A, size);
11511142 errdefer allocator.free(buf);
11521143
1153 try file.inStream().stream.readNoEof(buf);
1144 try file.inStream().readNoEof(buf);
11541145 return buf;
11551146 }
11561147
lib/std/fs/file.zig+52-84
......@@ -71,7 +71,7 @@ pub const File = struct {
7171 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
7272 std.event.Loop.instance.?.close(self.handle);
7373 } else {
74 return os.close(self.handle);
74 os.close(self.handle);
7575 }
7676 }
7777
......@@ -250,11 +250,16 @@ pub const File = struct {
250250 }
251251 }
252252
253 pub fn readAll(self: File, buffer: []u8) ReadError!void {
253 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
254 /// means the file reached the end. Reaching the end of a file is not an error condition.
255 pub fn readAll(self: File, buffer: []u8) ReadError!usize {
254256 var index: usize = 0;
255 while (index < buffer.len) {
256 index += try self.read(buffer[index..]);
257 while (index != buffer.len) {
258 const amt = try self.read(buffer[index..]);
259 if (amt == 0) break;
260 index += amt;
257261 }
262 return index;
258263 }
259264
260265 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
......@@ -265,11 +270,16 @@ pub const File = struct {
265270 }
266271 }
267272
268 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!void {
273 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
274 /// means the file reached the end. Reaching the end of a file is not an error condition.
275 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
269276 var index: usize = 0;
270 while (index < buffer.len) {
271 index += try self.pread(buffer[index..], offset + index);
277 while (index != buffer.len) {
278 const amt = try self.pread(buffer[index..], offset + index);
279 if (amt == 0) break;
280 index += amt;
272281 }
282 return index;
273283 }
274284
275285 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
......@@ -280,19 +290,27 @@ pub const File = struct {
280290 }
281291 }
282292
293 /// Returns the number of bytes read. If the number read is smaller than the total bytes
294 /// from all the buffers, it means the file reached the end. Reaching the end of a file
295 /// is not an error condition.
283296 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
284297 /// 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 {
286299 if (iovecs.len == 0) return;
287300
288301 var i: usize = 0;
302 var off: usize = 0;
289303 while (true) {
290304 var amt = try self.readv(iovecs[i..]);
305 var eof = amt == 0;
306 off += amt;
291307 while (amt >= iovecs[i].iov_len) {
292308 amt -= iovecs[i].iov_len;
293309 i += 1;
294 if (i >= iovecs.len) return;
310 if (i >= iovecs.len) return off;
311 eof = false;
295312 }
313 if (eof) return off;
296314 iovecs[i].iov_base += amt;
297315 iovecs[i].iov_len -= amt;
298316 }
......@@ -306,6 +324,9 @@ pub const File = struct {
306324 }
307325 }
308326
327 /// Returns the number of bytes read. If the number read is smaller than the total bytes
328 /// from all the buffers, it means the file reached the end. Reaching the end of a file
329 /// is not an error condition.
309330 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
310331 /// order to handle partial reads from the underlying OS layer.
311332 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
......@@ -315,12 +336,15 @@ pub const File = struct {
315336 var off: usize = 0;
316337 while (true) {
317338 var amt = try self.preadv(iovecs[i..], offset + off);
339 var eof = amt == 0;
318340 off += amt;
319341 while (amt >= iovecs[i].iov_len) {
320342 amt -= iovecs[i].iov_len;
321343 i += 1;
322 if (i >= iovecs.len) return;
344 if (i >= iovecs.len) return off;
345 eof = false;
323346 }
347 if (eof) return off;
324348 iovecs[i].iov_base += amt;
325349 iovecs[i].iov_len -= amt;
326350 }
......@@ -496,85 +520,29 @@ pub const File = struct {
496520 }
497521 }
498522
499 pub fn inStream(file: File) InStream {
500 return InStream{
501 .file = file,
502 .stream = InStream.Stream{ .readFn = InStream.readFn },
503 };
523 pub const InStream = io.InStream(File, ReadError, read);
524
525 pub fn inStream(file: File) io.InStream(File, ReadError, read) {
526 return .{ .context = file };
504527 }
505528
529 pub const OutStream = io.OutStream(File, WriteError, write);
530
506531 pub fn outStream(file: File) OutStream {
507 return OutStream{
508 .file = file,
509 .stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
510 };
532 return .{ .context = file };
511533 }
512534
535 pub const SeekableStream = io.SeekableStream(
536 File,
537 SeekError,
538 GetPosError,
539 seekTo,
540 seekBy,
541 getPos,
542 getEndPos,
543 );
544
513545 pub fn seekableStream(file: File) SeekableStream {
514 return SeekableStream{
515 .file = file,
516 .stream = SeekableStream.Stream{
517 .seekToFn = SeekableStream.seekToFn,
518 .seekByFn = SeekableStream.seekByFn,
519 .getPosFn = SeekableStream.getPosFn,
520 .getEndPosFn = SeekableStream.getEndPosFn,
521 },
522 };
546 return .{ .context = file };
523547 }
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 };
580548};
lib/std/heap.zig+1
......@@ -10,6 +10,7 @@ const c = std.c;
1010const maxInt = std.math.maxInt;
1111
1212pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
13pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
1314
1415const Allocator = mem.Allocator;
1516
lib/std/heap/logging_allocator.zig+51-45
......@@ -1,63 +1,69 @@
11const std = @import("../std.zig");
22const Allocator = std.mem.Allocator;
33
4const AnyErrorOutStream = std.io.OutStream(anyerror);
5
64/// This allocator is used in front of another allocator and logs to the provided stream
75/// on every call to the allocator. Stream errors are ignored.
86/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.
9pub const LoggingAllocator = struct {
10 allocator: Allocator,
11 parent_allocator: *Allocator,
12 out_stream: *AnyErrorOutStream,
7pub fn LoggingAllocator(comptime OutStreamType: type) type {
8 return struct {
9 allocator: Allocator,
10 parent_allocator: *Allocator,
11 out_stream: OutStreamType,
1312
14 const Self = @This();
13 const Self = @This();
1514
16 pub fn init(parent_allocator: *Allocator, out_stream: *AnyErrorOutStream) Self {
17 return Self{
18 .allocator = Allocator{
19 .reallocFn = realloc,
20 .shrinkFn = shrink,
21 },
22 .parent_allocator = parent_allocator,
23 .out_stream = out_stream,
24 };
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 {};
15 pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
16 return Self{
17 .allocator = Allocator{
18 .reallocFn = realloc,
19 .shrinkFn = shrink,
20 },
21 .parent_allocator = parent_allocator,
22 .out_stream = out_stream,
23 };
3324 }
34 const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
35 if (result) |buff| {
36 self.out_stream.print("success!\n", .{}) catch {};
37 } else |err| {
38 self.out_stream.print("failure!\n", .{}) catch {};
25
26 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
27 const self = @fieldParentPtr(Self, "allocator", allocator);
28 if (old_mem.len == 0) {
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;
3940 }
40 return result;
41 }
4241
43 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
44 const self = @fieldParentPtr(Self, "allocator", allocator);
45 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
46 if (new_size == 0) {
47 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
48 } else {
49 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
42 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
43 const self = @fieldParentPtr(Self, "allocator", allocator);
44 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
45 if (new_size == 0) {
46 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
47 } else {
48 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
49 }
50 return result;
5051 }
51 return result;
52 }
53};
52 };
53}
54
55pub fn loggingAllocator(
56 parent_allocator: *Allocator,
57 out_stream: var,
58) LoggingAllocator(@TypeOf(out_stream)) {
59 return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);
60}
5461
5562test "LoggingAllocator" {
5663 var buf: [255]u8 = undefined;
57 var slice_stream = std.io.SliceOutStream.init(buf[0..]);
58 const stream = &slice_stream.stream;
64 var fbs = std.io.fixedBufferStream(&buf);
5965
60 const allocator = &LoggingAllocator.init(std.testing.allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator;
66 const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator;
6167
6268 const ptr = try allocator.alloc(u8, 10);
6369 allocator.free(ptr);
......@@ -66,5 +72,5 @@ test "LoggingAllocator" {
6672 \\allocation of 10 success!
6773 \\free of 10 bytes success!
6874 \\
69 , slice_stream.getWritten());
75 , fbs.getWritten());
7076}
lib/std/io.zig+39-1026
......@@ -4,17 +4,13 @@ const root = @import("root");
44const c = std.c;
55
66const math = std.math;
7const debug = std.debug;
8const assert = debug.assert;
7const assert = std.debug.assert;
98const os = std.os;
109const fs = std.fs;
1110const mem = std.mem;
1211const meta = std.meta;
1312const trait = meta.trait;
14const Buffer = std.Buffer;
15const fmt = std.fmt;
1613const File = std.fs.File;
17const testing = std.testing;
1814
1915pub const Mode = enum {
2016 /// I/O operates normally, waiting for the operating system syscalls to complete.
......@@ -92,1051 +88,68 @@ pub fn getStdIn() File {
9288 };
9389}
9490
95pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
96pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream;
97pub const COutStream = @import("io/c_out_stream.zig").COutStream;
9891pub const InStream = @import("io/in_stream.zig").InStream;
9992pub const OutStream = @import("io/out_stream.zig").OutStream;
93pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
10094
101/// Deprecated; use `std.fs.Dir.writeFile`.
102pub fn writeFile(path: []const u8, data: []const u8) !void {
103 return fs.cwd().writeFile(path, data);
104}
105
106/// Deprecated; use `std.fs.Dir.readFileAlloc`.
107pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
108 return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
109}
110
111pub fn BufferedInStream(comptime Error: type) type {
112 return BufferedInStreamCustom(mem.page_size, Error);
113}
114
115pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type {
116 return struct {
117 const Self = @This();
118 const Stream = InStream(Error);
119
120 stream: Stream,
121
122 unbuffered_in_stream: *Stream,
123
124 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
125 fifo: FifoType,
126
127 pub fn init(unbuffered_in_stream: *Stream) Self {
128 return Self{
129 .unbuffered_in_stream = unbuffered_in_stream,
130 .fifo = FifoType.init(),
131 .stream = Stream{ .readFn = readFn },
132 };
133 }
134
135 fn readFn(in_stream: *Stream, dest: []u8) !usize {
136 const self = @fieldParentPtr(Self, "stream", in_stream);
137 var dest_index: usize = 0;
138 while (dest_index < dest.len) {
139 const written = self.fifo.read(dest[dest_index..]);
140 if (written == 0) {
141 // fifo empty, fill it
142 const writable = self.fifo.writableSlice(0);
143 assert(writable.len > 0);
144 const n = try self.unbuffered_in_stream.read(writable);
145 if (n == 0) {
146 // reading from the unbuffered stream returned nothing
147 // so we have nothing left to read.
148 return dest_index;
149 }
150 self.fifo.update(n);
151 }
152 dest_index += written;
153 }
154 return dest.len;
155 }
156 };
157}
158
159test "io.BufferedInStream" {
160 const OneByteReadInStream = struct {
161 const Error = error{NoError};
162 const Stream = InStream(Error);
163
164 stream: Stream,
165 str: []const u8,
166 curr: usize,
167
168 fn init(str: []const u8) @This() {
169 return @This(){
170 .stream = Stream{ .readFn = readFn },
171 .str = str,
172 .curr = 0,
173 };
174 }
175
176 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
177 const self = @fieldParentPtr(@This(), "stream", in_stream);
178 if (self.str.len <= self.curr or dest.len == 0)
179 return 0;
180
181 dest[0] = self.str[self.curr];
182 self.curr += 1;
183 return 1;
184 }
185 };
186
187 const str = "This is a test";
188 var one_byte_stream = OneByteReadInStream.init(str);
189 var buf_in_stream = BufferedInStream(OneByteReadInStream.Error).init(&one_byte_stream.stream);
190 const stream = &buf_in_stream.stream;
191
192 const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
193 defer testing.allocator.free(res);
194 testing.expectEqualSlices(u8, str, res);
195}
196
197/// Creates a stream which supports 'un-reading' data, so that it can be read again.
198/// This makes look-ahead style parsing much easier.
199pub fn PeekStream(comptime buffer_type: std.fifo.LinearFifoBufferType, comptime InStreamError: type) type {
200 return struct {
201 const Self = @This();
202 pub const Error = InStreamError;
203 pub const Stream = InStream(Error);
204
205 stream: Stream,
206 base: *Stream,
207
208 const FifoType = std.fifo.LinearFifo(u8, buffer_type);
209 fifo: FifoType,
210
211 pub usingnamespace switch (buffer_type) {
212 .Static => struct {
213 pub fn init(base: *Stream) Self {
214 return .{
215 .base = base,
216 .fifo = FifoType.init(),
217 .stream = Stream{ .readFn = readFn },
218 };
219 }
220 },
221 .Slice => struct {
222 pub fn init(base: *Stream, buf: []u8) Self {
223 return .{
224 .base = base,
225 .fifo = FifoType.init(buf),
226 .stream = Stream{ .readFn = readFn },
227 };
228 }
229 },
230 .Dynamic => struct {
231 pub fn init(base: *Stream, allocator: *mem.Allocator) Self {
232 return .{
233 .base = base,
234 .fifo = FifoType.init(allocator),
235 .stream = Stream{ .readFn = readFn },
236 };
237 }
238 },
239 };
240
241 pub fn putBackByte(self: *Self, byte: u8) !void {
242 try self.putBack(&[_]u8{byte});
243 }
244
245 pub fn putBack(self: *Self, bytes: []const u8) !void {
246 try self.fifo.unget(bytes);
247 }
248
249 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
250 const self = @fieldParentPtr(Self, "stream", in_stream);
251
252 // copy over anything putBack()'d
253 var dest_index = self.fifo.read(dest);
254 if (dest_index == dest.len) return dest_index;
255
256 // ask the backing stream for more
257 dest_index += try self.base.read(dest[dest_index..]);
258 return dest_index;
259 }
260 };
261}
262
263pub const SliceInStream = struct {
264 const Self = @This();
265 pub const Error = error{};
266 pub const Stream = InStream(Error);
267
268 stream: Stream,
269
270 pos: usize,
271 slice: []const u8,
272
273 pub fn init(slice: []const u8) Self {
274 return Self{
275 .slice = slice,
276 .pos = 0,
277 .stream = Stream{ .readFn = readFn },
278 };
279 }
280
281 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
282 const self = @fieldParentPtr(Self, "stream", in_stream);
283 const size = math.min(dest.len, self.slice.len - self.pos);
284 const end = self.pos + size;
285
286 mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
287 self.pos = end;
288
289 return size;
290 }
291};
292
293/// Creates a stream which allows for reading bit fields from another stream
294pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
295 return struct {
296 const Self = @This();
297
298 in_stream: *Stream,
299 bit_buffer: u7,
300 bit_count: u3,
301 stream: Stream,
302
303 pub const Stream = InStream(Error);
304 const u8_bit_count = comptime meta.bitCount(u8);
305 const u7_bit_count = comptime meta.bitCount(u7);
306 const u4_bit_count = comptime meta.bitCount(u4);
307
308 pub fn init(in_stream: *Stream) Self {
309 return Self{
310 .in_stream = in_stream,
311 .bit_buffer = 0,
312 .bit_count = 0,
313 .stream = Stream{ .readFn = read },
314 };
315 }
316
317 /// Reads `bits` bits from the stream and returns a specified unsigned int type
318 /// containing them in the least significant end, returning an error if the
319 /// specified number of bits could not be read.
320 pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
321 var n: usize = undefined;
322 const result = try self.readBits(U, bits, &n);
323 if (n < bits) return error.EndOfStream;
324 return result;
325 }
95pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream;
96pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream;
32697
327 /// Reads `bits` bits from the stream and returns a specified unsigned int type
328 /// containing them in the least significant end. The number of bits successfully
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));
98pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream;
99pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream;
332100
333 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
334 // related to shifting and casting.
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);
101pub const PeekStream = @import("io/peek_stream.zig").PeekStream;
102pub const peekStream = @import("io/peek_stream.zig").peekStream;
342103
343 out_bits.* = @as(usize, 0);
344 if (U == u0 or bits == 0) return 0;
345 var out_buffer = @as(Buf, 0);
104pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream;
105pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream;
346106
347 if (self.bit_count > 0) {
348 const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
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
107pub const COutStream = @import("io/c_out_stream.zig").COutStream;
108pub const cOutStream = @import("io/c_out_stream.zig").cOutStream;
371109
372 //copy bytes until we have enough bits, then leave the rest in bit_buffer
373 while (out_bits.* < bits) {
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 };
110pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream;
111pub const countingOutStream = @import("io/counting_out_stream.zig").countingOutStream;
383112
384 switch (endian) {
385 .Big => {
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 }
113pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream;
114pub const bitInStream = @import("io/bit_in_stream.zig").bitInStream;
393115
394 const shift = @intCast(u3, u8_bit_count - n);
395 out_buffer <<= @intCast(BufShift, n);
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 }
116pub const BitOutStream = @import("io/bit_out_stream.zig").BitOutStream;
117pub const bitOutStream = @import("io/bit_out_stream.zig").bitOutStream;
407118
408 const shift = @intCast(u3, u8_bit_count - n);
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 }
119pub const Packing = @import("io/serialization.zig").Packing;
417120
418 return @intCast(U, out_buffer);
419 }
121pub const Serializer = @import("io/serialization.zig").Serializer;
122pub const serializer = @import("io/serialization.zig").serializer;
420123
421 pub fn alignToByte(self: *Self) void {
422 self.bit_buffer = 0;
423 self.bit_count = 0;
424 }
124pub const Deserializer = @import("io/serialization.zig").Deserializer;
125pub const deserializer = @import("io/serialization.zig").deserializer;
425126
426 pub fn read(self_stream: *Stream, buffer: []u8) Error!usize {
427 var self = @fieldParentPtr(Self, "stream", self_stream);
127pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
428128
429 var out_bits: usize = undefined;
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 }
129pub const StreamSource = @import("io/stream_source.zig").StreamSource;
440130
441 return self.in_stream.read(buffer);
442 }
443 };
131/// Deprecated; use `std.fs.Dir.writeFile`.
132pub fn writeFile(path: []const u8, data: []const u8) !void {
133 return fs.cwd().writeFile(path, data);
444134}
445135
446/// This is a simple OutStream that writes to a fixed buffer. If the returned number
447/// of bytes written is less than requested, the buffer is full.
448/// Returns error.OutOfMemory when no bytes would be written.
449pub const SliceOutStream = struct {
450 pub const Error = error{OutOfMemory};
451 pub const Stream = OutStream(Error);
452
453 stream: Stream,
454
455 pos: usize,
456 slice: []u8,
457
458 pub fn init(slice: []u8) SliceOutStream {
459 return SliceOutStream{
460 .slice = slice,
461 .pos = 0,
462 .stream = Stream{ .writeFn = writeFn },
463 };
464 }
465
466 pub fn getWritten(self: *const SliceOutStream) []const u8 {
467 return self.slice[0..self.pos];
468 }
469
470 pub fn reset(self: *SliceOutStream) void {
471 self.pos = 0;
472 }
473
474 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
475 const self = @fieldParentPtr(SliceOutStream, "stream", out_stream);
476
477 if (bytes.len == 0) return 0;
478
479 assert(self.pos <= self.slice.len);
480
481 const n = if (self.pos + bytes.len <= self.slice.len)
482 bytes.len
483 else
484 self.slice.len - self.pos;
485
486 std.mem.copy(u8, self.slice[self.pos .. self.pos + n], bytes[0..n]);
487 self.pos += n;
488
489 if (n == 0) return error.OutOfMemory;
490
491 return n;
492 }
493};
494
495test "io.SliceOutStream" {
496 var buf: [255]u8 = undefined;
497 var slice_stream = SliceOutStream.init(buf[0..]);
498 const stream = &slice_stream.stream;
499
500 try stream.print("{}{}!", .{ "Hello", "World" });
501 testing.expectEqualSlices(u8, "HelloWorld!", slice_stream.getWritten());
136/// Deprecated; use `std.fs.Dir.readFileAlloc`.
137pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
138 return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
502139}
503140
504var null_out_stream_state = NullOutStream.init();
505pub const null_out_stream = &null_out_stream_state.stream;
506
507141/// An OutStream that doesn't write to anything.
508pub const NullOutStream = struct {
509 pub const Error = error{};
510 pub const Stream = OutStream(Error);
511
512 stream: Stream,
513
514 pub fn init() NullOutStream {
515 return NullOutStream{
516 .stream = Stream{ .writeFn = writeFn },
517 };
518 }
519
520 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
521 return bytes.len;
522 }
523};
524
525test "io.NullOutStream" {
526 var null_stream = NullOutStream.init();
527 const stream = &null_stream.stream;
528 stream.write("yay" ** 10000) catch unreachable;
529}
530
531/// An OutStream that counts how many bytes has been written to it.
532pub fn CountingOutStream(comptime OutStreamError: type) type {
533 return struct {
534 const Self = @This();
535 pub const Stream = OutStream(Error);
536 pub const Error = OutStreamError;
537
538 stream: Stream,
539 bytes_written: u64,
540 child_stream: *Stream,
541
542 pub fn init(child_stream: *Stream) Self {
543 return Self{
544 .stream = Stream{ .writeFn = writeFn },
545 .bytes_written = 0,
546 .child_stream = child_stream,
547 };
548 }
549
550 fn writeFn(out_stream: *Stream, bytes: []const u8) OutStreamError!usize {
551 const self = @fieldParentPtr(Self, "stream", out_stream);
552 try self.child_stream.write(bytes);
553 self.bytes_written += bytes.len;
554 return bytes.len;
555 }
556 };
557}
558
559test "io.CountingOutStream" {
560 var null_stream = NullOutStream.init();
561 var counting_stream = CountingOutStream(NullOutStream.Error).init(&null_stream.stream);
562 const stream = &counting_stream.stream;
563
564 const bytes = "yay" ** 10000;
565 stream.write(bytes) catch unreachable;
566 testing.expect(counting_stream.bytes_written == bytes.len);
567}
142pub const null_out_stream = @as(NullOutStream, .{ .context = {} });
568143
569pub fn BufferedOutStream(comptime Error: type) type {
570 return BufferedOutStreamCustom(mem.page_size, Error);
144const NullOutStream = OutStream(void, error{}, dummyWrite);
145fn dummyWrite(context: void, data: []const u8) error{}!usize {
146 return data.len;
571147}
572148
573pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type {
574 return struct {
575 const Self = @This();
576 pub const Stream = OutStream(Error);
577 pub const Error = OutStreamError;
578
579 stream: Stream,
580
581 unbuffered_out_stream: *Stream,
582
583 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
584 fifo: FifoType,
585
586 pub fn init(unbuffered_out_stream: *Stream) Self {
587 return Self{
588 .unbuffered_out_stream = unbuffered_out_stream,
589 .fifo = FifoType.init(),
590 .stream = Stream{ .writeFn = writeFn },
591 };
592 }
593
594 pub fn flush(self: *Self) !void {
595 while (true) {
596 const slice = self.fifo.readableSlice(0);
597 if (slice.len == 0) break;
598 try self.unbuffered_out_stream.write(slice);
599 self.fifo.discard(slice.len);
600 }
601 }
602
603 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
604 const self = @fieldParentPtr(Self, "stream", out_stream);
605 if (bytes.len >= self.fifo.writableLength()) {
606 try self.flush();
607 return self.unbuffered_out_stream.writeOnce(bytes);
608 }
609 self.fifo.writeAssumeCapacity(bytes);
610 return bytes.len;
611 }
612 };
613}
614
615/// Implementation of OutStream trait for Buffer
616pub const BufferOutStream = struct {
617 buffer: *Buffer,
618 stream: Stream,
619
620 pub const Error = error{OutOfMemory};
621 pub const Stream = OutStream(Error);
622
623 pub fn init(buffer: *Buffer) BufferOutStream {
624 return BufferOutStream{
625 .buffer = buffer,
626 .stream = Stream{ .writeFn = writeFn },
627 };
628 }
629
630 fn writeFn(out_stream: *Stream, bytes: []const u8) !usize {
631 const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
632 try self.buffer.append(bytes);
633 return bytes.len;
634 }
635};
636
637/// Creates a stream which allows for writing bit fields to another stream
638pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
639 return struct {
640 const Self = @This();
641
642 out_stream: *Stream,
643 bit_buffer: u8,
644 bit_count: u4,
645 stream: Stream,
646
647 pub const Stream = OutStream(Error);
648 const u8_bit_count = comptime meta.bitCount(u8);
649 const u4_bit_count = comptime meta.bitCount(u4);
650
651 pub fn init(out_stream: *Stream) Self {
652 return Self{
653 .out_stream = out_stream,
654 .bit_buffer = 0,
655 .bit_count = 0,
656 .stream = Stream{ .writeFn = write },
657 };
658 }
659
660 /// Write the specified number of bits to the stream from the least significant bits of
661 /// the specified unsigned int value. Bits will only be written to the stream when there
662 /// are enough to fill a byte.
663 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
664 if (bits == 0) return;
665
666 const U = @TypeOf(value);
667 comptime assert(trait.isUnsignedInt(U));
668
669 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
670 // related to shifting and casting.
671 const u_bit_count = comptime meta.bitCount(U);
672 const buf_bit_count = bc: {
673 assert(u_bit_count >= bits);
674 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
675 };
676 const Buf = std.meta.IntType(false, buf_bit_count);
677 const BufShift = math.Log2Int(Buf);
678
679 const buf_value = @intCast(Buf, value);
680
681 const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
682 var in_buffer = switch (endian) {
683 .Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
684 .Little => buf_value,
685 };
686 var in_bits = bits;
687
688 if (self.bit_count > 0) {
689 const bits_remaining = u8_bit_count - self.bit_count;
690 const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
691 switch (endian) {
692 .Big => {
693 const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
694 const v = @intCast(u8, in_buffer >> shift);
695 self.bit_buffer |= v;
696 in_buffer <<= n;
697 },
698 .Little => {
699 const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
700 self.bit_buffer |= v;
701 in_buffer >>= n;
702 },
703 }
704 self.bit_count += n;
705 in_bits -= n;
706
707 //if we didn't fill the buffer, it's because bits < bits_remaining;
708 if (self.bit_count != u8_bit_count) return;
709 try self.out_stream.writeByte(self.bit_buffer);
710 self.bit_buffer = 0;
711 self.bit_count = 0;
712 }
713 //at this point we know bit_buffer is empty
714
715 //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
716 while (in_bits >= u8_bit_count) {
717 switch (endian) {
718 .Big => {
719 const v = @intCast(u8, in_buffer >> high_byte_shift);
720 try self.out_stream.writeByte(v);
721 in_buffer <<= @intCast(u3, u8_bit_count - 1);
722 in_buffer <<= 1;
723 },
724 .Little => {
725 const v = @truncate(u8, in_buffer);
726 try self.out_stream.writeByte(v);
727 in_buffer >>= @intCast(u3, u8_bit_count - 1);
728 in_buffer >>= 1;
729 },
730 }
731 in_bits -= u8_bit_count;
732 }
733
734 if (in_bits > 0) {
735 self.bit_count = @intCast(u4, in_bits);
736 self.bit_buffer = switch (endian) {
737 .Big => @truncate(u8, in_buffer >> high_byte_shift),
738 .Little => @truncate(u8, in_buffer),
739 };
740 }
741 }
742
743 /// Flush any remaining bits to the stream.
744 pub fn flushBits(self: *Self) Error!void {
745 if (self.bit_count == 0) return;
746 try self.out_stream.writeByte(self.bit_buffer);
747 self.bit_buffer = 0;
748 self.bit_count = 0;
749 }
750
751 pub fn write(self_stream: *Stream, buffer: []const u8) Error!usize {
752 var self = @fieldParentPtr(Self, "stream", self_stream);
753
754 // TODO: I'm not sure this is a good idea, maybe flushBits should be forced
755 if (self.bit_count > 0) {
756 for (buffer) |b, i|
757 try self.writeBits(b, u8_bit_count);
758 return buffer.len;
759 }
760
761 return self.out_stream.writeOnce(buffer);
762 }
763 };
764}
765
766pub const BufferedAtomicFile = struct {
767 atomic_file: fs.AtomicFile,
768 file_stream: File.OutStream,
769 buffered_stream: BufferedOutStream(File.WriteError),
770 allocator: *mem.Allocator,
771
772 pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
773 // TODO with well defined copy elision we don't need this allocation
774 var self = try allocator.create(BufferedAtomicFile);
775 self.* = BufferedAtomicFile{
776 .atomic_file = undefined,
777 .file_stream = undefined,
778 .buffered_stream = undefined,
779 .allocator = allocator,
780 };
781 errdefer allocator.destroy(self);
782
783 self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode);
784 errdefer self.atomic_file.deinit();
785
786 self.file_stream = self.atomic_file.file.outStream();
787 self.buffered_stream = BufferedOutStream(File.WriteError).init(&self.file_stream.stream);
788 return self;
789 }
790
791 /// always call destroy, even after successful finish()
792 pub fn destroy(self: *BufferedAtomicFile) void {
793 self.atomic_file.deinit();
794 self.allocator.destroy(self);
795 }
796
797 pub fn finish(self: *BufferedAtomicFile) !void {
798 try self.buffered_stream.flush();
799 try self.atomic_file.finish();
800 }
801
802 pub fn stream(self: *BufferedAtomicFile) *OutStream(File.WriteError) {
803 return &self.buffered_stream.stream;
804 }
805};
806
807pub const Packing = enum {
808 /// Pack data to byte alignment
809 Byte,
810
811 /// Pack data to bit alignment
812 Bit,
813};
814
815/// Creates a deserializer that deserializes types from any stream.
816/// If `is_packed` is true, the data stream is treated as bit-packed,
817/// otherwise data is expected to be packed to the smallest byte.
818/// Types may implement a custom deserialization routine with a
819/// function named `deserialize` in the form of:
820/// pub fn deserialize(self: *Self, deserializer: var) !void
821/// which will be called when the deserializer is used to deserialize
822/// that type. It will pass a pointer to the type instance to deserialize
823/// into and a pointer to the deserializer struct.
824pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
825 return struct {
826 const Self = @This();
827
828 in_stream: if (packing == .Bit) BitInStream(endian, Stream.Error) else *Stream,
829
830 pub const Stream = InStream(Error);
831
832 pub fn init(in_stream: *Stream) Self {
833 return Self{
834 .in_stream = switch (packing) {
835 .Bit => BitInStream(endian, Stream.Error).init(in_stream),
836 .Byte => in_stream,
837 },
838 };
839 }
840
841 pub fn alignToByte(self: *Self) void {
842 if (packing == .Byte) return;
843 self.in_stream.alignToByte();
844 }
845
846 //@BUG: inferred error issue. See: #1386
847 fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
848 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
849
850 const u8_bit_count = 8;
851 const t_bit_count = comptime meta.bitCount(T);
852
853 const U = std.meta.IntType(false, t_bit_count);
854 const Log2U = math.Log2Int(U);
855 const int_size = (U.bit_count + 7) / 8;
856
857 if (packing == .Bit) {
858 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
859 return @bitCast(T, result);
860 }
861
862 var buffer: [int_size]u8 = undefined;
863 const read_size = try self.in_stream.read(buffer[0..]);
864 if (read_size < int_size) return error.EndOfStream;
865
866 if (int_size == 1) {
867 if (t_bit_count == 8) return @bitCast(T, buffer[0]);
868 const PossiblySignedByte = std.meta.IntType(T.is_signed, 8);
869 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
870 }
871
872 var result = @as(U, 0);
873 for (buffer) |byte, i| {
874 switch (endian) {
875 .Big => {
876 result = (result << u8_bit_count) | byte;
877 },
878 .Little => {
879 result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
880 },
881 }
882 }
883
884 return @bitCast(T, result);
885 }
886
887 /// Deserializes and returns data of the specified type from the stream
888 pub fn deserialize(self: *Self, comptime T: type) !T {
889 var value: T = undefined;
890 try self.deserializeInto(&value);
891 return value;
892 }
893
894 /// Deserializes data into the type pointed to by `ptr`
895 pub fn deserializeInto(self: *Self, ptr: var) !void {
896 const T = @TypeOf(ptr);
897 comptime assert(trait.is(.Pointer)(T));
898
899 if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
900 for (ptr) |*v|
901 try self.deserializeInto(v);
902 return;
903 }
904
905 comptime assert(trait.isSingleItemPtr(T));
906
907 const C = comptime meta.Child(T);
908 const child_type_id = @typeInfo(C);
909
910 //custom deserializer: fn(self: *Self, deserializer: var) !void
911 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
912
913 if (comptime trait.isPacked(C) and packing != .Bit) {
914 var packed_deserializer = Deserializer(endian, .Bit, Error).init(self.in_stream);
915 return packed_deserializer.deserializeInto(ptr);
916 }
917
918 switch (child_type_id) {
919 .Void => return,
920 .Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
921 .Float, .Int => ptr.* = try self.deserializeInt(C),
922 .Struct => {
923 const info = @typeInfo(C).Struct;
924
925 inline for (info.fields) |*field_info| {
926 const name = field_info.name;
927 const FieldType = field_info.field_type;
928
929 if (FieldType == void or FieldType == u0) continue;
930
931 //it doesn't make any sense to read pointers
932 if (comptime trait.is(.Pointer)(FieldType)) {
933 @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
934 @typeName(C) ++ " because it " ++ "is of pointer-type " ++
935 @typeName(FieldType) ++ ".");
936 }
937
938 try self.deserializeInto(&@field(ptr, name));
939 }
940 },
941 .Union => {
942 const info = @typeInfo(C).Union;
943 if (info.tag_type) |TagType| {
944 //we avoid duplicate iteration over the enum tags
945 // by getting the int directly and casting it without
946 // safety. If it is bad, it will be caught anyway.
947 const TagInt = @TagType(TagType);
948 const tag = try self.deserializeInt(TagInt);
949
950 inline for (info.fields) |field_info| {
951 if (field_info.enum_field.?.value == tag) {
952 const name = field_info.name;
953 const FieldType = field_info.field_type;
954 ptr.* = @unionInit(C, name, undefined);
955 try self.deserializeInto(&@field(ptr, name));
956 return;
957 }
958 }
959 //This is reachable if the enum data is bad
960 return error.InvalidEnumTag;
961 }
962 @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
963 " because it is an untagged union. Use a custom deserialize().");
964 },
965 .Optional => {
966 const OC = comptime meta.Child(C);
967 const exists = (try self.deserializeInt(u1)) > 0;
968 if (!exists) {
969 ptr.* = null;
970 return;
971 }
972
973 ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
974 const val_ptr = &ptr.*.?;
975 try self.deserializeInto(val_ptr);
976 },
977 .Enum => {
978 var value = try self.deserializeInt(@TagType(C));
979 ptr.* = try meta.intToEnum(C, value);
980 },
981 else => {
982 @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
983 },
984 }
985 }
986 };
149test "null_out_stream" {
150 null_out_stream.writeAll("yay" ** 10) catch |err| switch (err) {};
987151}
988152
989/// Creates a serializer that serializes types to any stream.
990/// If `is_packed` is true, the data will be bit-packed into the stream.
991/// Note that the you must call `serializer.flush()` when you are done
992/// writing bit-packed data in order ensure any unwritten bits are committed.
993/// If `is_packed` is false, data is packed to the smallest byte. In the case
994/// of packed structs, the struct will written bit-packed and with the specified
995/// endianess, after which data will resume being written at the next byte boundary.
996/// Types may implement a custom serialization routine with a
997/// function named `serialize` in the form of:
998/// pub fn serialize(self: Self, serializer: var) !void
999/// which will be called when the serializer is used to serialize that type. It will
1000/// pass a const pointer to the type instance to be serialized and a pointer
1001/// to the serializer struct.
1002pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
1003 return struct {
1004 const Self = @This();
1005
1006 out_stream: if (packing == .Bit) BitOutStream(endian, Stream.Error) else *Stream,
1007
1008 pub const Stream = OutStream(Error);
1009
1010 pub fn init(out_stream: *Stream) Self {
1011 return Self{
1012 .out_stream = switch (packing) {
1013 .Bit => BitOutStream(endian, Stream.Error).init(out_stream),
1014 .Byte => out_stream,
1015 },
1016 };
1017 }
1018
1019 /// Flushes any unwritten bits to the stream
1020 pub fn flush(self: *Self) Error!void {
1021 if (packing == .Bit) return self.out_stream.flushBits();
1022 }
1023
1024 fn serializeInt(self: *Self, value: var) Error!void {
1025 const T = @TypeOf(value);
1026 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
1027
1028 const t_bit_count = comptime meta.bitCount(T);
1029 const u8_bit_count = comptime meta.bitCount(u8);
1030
1031 const U = std.meta.IntType(false, t_bit_count);
1032 const Log2U = math.Log2Int(U);
1033 const int_size = (U.bit_count + 7) / 8;
1034
1035 const u_value = @bitCast(U, value);
1036
1037 if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
1038
1039 var buffer: [int_size]u8 = undefined;
1040 if (int_size == 1) buffer[0] = u_value;
1041
1042 for (buffer) |*byte, i| {
1043 const idx = switch (endian) {
1044 .Big => int_size - i - 1,
1045 .Little => i,
1046 };
1047 const shift = @intCast(Log2U, idx * u8_bit_count);
1048 const v = u_value >> shift;
1049 byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
1050 }
1051
1052 try self.out_stream.write(&buffer);
1053 }
1054
1055 /// Serializes the passed value into the stream
1056 pub fn serialize(self: *Self, value: var) Error!void {
1057 const T = comptime @TypeOf(value);
1058
1059 if (comptime trait.isIndexable(T)) {
1060 for (value) |v|
1061 try self.serialize(v);
1062 return;
1063 }
1064
1065 //custom serializer: fn(self: Self, serializer: var) !void
1066 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
1067
1068 if (comptime trait.isPacked(T) and packing != .Bit) {
1069 var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
1070 try packed_serializer.serialize(value);
1071 try packed_serializer.flush();
1072 return;
1073 }
1074
1075 switch (@typeInfo(T)) {
1076 .Void => return,
1077 .Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
1078 .Float, .Int => try self.serializeInt(value),
1079 .Struct => {
1080 const info = @typeInfo(T);
1081
1082 inline for (info.Struct.fields) |*field_info| {
1083 const name = field_info.name;
1084 const FieldType = field_info.field_type;
1085
1086 if (FieldType == void or FieldType == u0) continue;
1087
1088 //It doesn't make sense to write pointers
1089 if (comptime trait.is(.Pointer)(FieldType)) {
1090 @compileError("Will not " ++ "serialize field " ++ name ++
1091 " of struct " ++ @typeName(T) ++ " because it " ++
1092 "is of pointer-type " ++ @typeName(FieldType) ++ ".");
1093 }
1094 try self.serialize(@field(value, name));
1095 }
1096 },
1097 .Union => {
1098 const info = @typeInfo(T).Union;
1099 if (info.tag_type) |TagType| {
1100 const active_tag = meta.activeTag(value);
1101 try self.serialize(active_tag);
1102 //This inline loop is necessary because active_tag is a runtime
1103 // value, but @field requires a comptime value. Our alternative
1104 // is to check each field for a match
1105 inline for (info.fields) |field_info| {
1106 if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
1107 const name = field_info.name;
1108 const FieldType = field_info.field_type;
1109 try self.serialize(@field(value, name));
1110 return;
1111 }
1112 }
1113 unreachable;
1114 }
1115 @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
1116 " because it is an untagged union. Use a custom serialize().");
1117 },
1118 .Optional => {
1119 if (value == null) {
1120 try self.serializeInt(@as(u1, @boolToInt(false)));
1121 return;
1122 }
1123 try self.serializeInt(@as(u1, @boolToInt(true)));
1124
1125 const OC = comptime meta.Child(T);
1126 const val_ptr = &value.?;
1127 try self.serialize(val_ptr.*);
1128 },
1129 .Enum => {
1130 try self.serializeInt(@enumToInt(value));
1131 },
1132 else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
1133 }
1134 }
1135 };
1136}
1137
1138test "import io tests" {
1139 comptime {
1140 _ = @import("io/test.zig");
1141 }
153test "" {
154 _ = @import("io/test.zig");
1142155}
lib/std/io/bit_in_stream.zig created+243
......@@ -0,0 +1,243 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const assert = std.debug.assert;
5const testing = std.testing;
6const trait = std.meta.trait;
7const meta = std.meta;
8const math = std.math;
9
10/// Creates a stream which allows for reading bit fields from another stream
11pub fn BitInStream(endian: builtin.Endian, comptime InStreamType: type) type {
12 return struct {
13 in_stream: InStreamType,
14 bit_buffer: u7,
15 bit_count: u3,
16
17 pub const Error = InStreamType.Error;
18 pub const InStream = io.InStream(*Self, Error, read);
19
20 const Self = @This();
21 const u8_bit_count = comptime meta.bitCount(u8);
22 const u7_bit_count = comptime meta.bitCount(u7);
23 const u4_bit_count = comptime meta.bitCount(u4);
24
25 pub fn init(in_stream: InStreamType) Self {
26 return Self{
27 .in_stream = in_stream,
28 .bit_buffer = 0,
29 .bit_count = 0,
30 };
31 }
32
33 /// Reads `bits` bits from the stream and returns a specified unsigned int type
34 /// containing them in the least significant end, returning an error if the
35 /// specified number of bits could not be read.
36 pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
37 var n: usize = undefined;
38 const result = try self.readBits(U, bits, &n);
39 if (n < bits) return error.EndOfStream;
40 return result;
41 }
42
43 /// Reads `bits` bits from the stream and returns a specified unsigned int type
44 /// containing them in the least significant end. The number of bits successfully
45 /// read is placed in `out_bits`, as reaching the end of the stream is not an error.
46 pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U {
47 comptime assert(trait.isUnsignedInt(U));
48
49 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
50 // related to shifting and casting.
51 const u_bit_count = comptime meta.bitCount(U);
52 const buf_bit_count = bc: {
53 assert(u_bit_count >= bits);
54 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
55 };
56 const Buf = std.meta.IntType(false, buf_bit_count);
57 const BufShift = math.Log2Int(Buf);
58
59 out_bits.* = @as(usize, 0);
60 if (U == u0 or bits == 0) return 0;
61 var out_buffer = @as(Buf, 0);
62
63 if (self.bit_count > 0) {
64 const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
65 const shift = u7_bit_count - n;
66 switch (endian) {
67 .Big => {
68 out_buffer = @as(Buf, self.bit_buffer >> shift);
69 if (n >= u7_bit_count)
70 self.bit_buffer = 0
71 else
72 self.bit_buffer <<= n;
73 },
74 .Little => {
75 const value = (self.bit_buffer << shift) >> shift;
76 out_buffer = @as(Buf, value);
77 if (n >= u7_bit_count)
78 self.bit_buffer = 0
79 else
80 self.bit_buffer >>= n;
81 },
82 }
83 self.bit_count -= n;
84 out_bits.* = n;
85 }
86 //at this point we know bit_buffer is empty
87
88 //copy bytes until we have enough bits, then leave the rest in bit_buffer
89 while (out_bits.* < bits) {
90 const n = bits - out_bits.*;
91 const next_byte = self.in_stream.readByte() catch |err| {
92 if (err == error.EndOfStream) {
93 return @intCast(U, out_buffer);
94 }
95 //@BUG: See #1810. Not sure if the bug is that I have to do this for some
96 // streams, or that I don't for streams with emtpy errorsets.
97 return @errSetCast(Error, err);
98 };
99
100 switch (endian) {
101 .Big => {
102 if (n >= u8_bit_count) {
103 out_buffer <<= @intCast(u3, u8_bit_count - 1);
104 out_buffer <<= 1;
105 out_buffer |= @as(Buf, next_byte);
106 out_bits.* += u8_bit_count;
107 continue;
108 }
109
110 const shift = @intCast(u3, u8_bit_count - n);
111 out_buffer <<= @intCast(BufShift, n);
112 out_buffer |= @as(Buf, next_byte >> shift);
113 out_bits.* += n;
114 self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
115 self.bit_count = shift;
116 },
117 .Little => {
118 if (n >= u8_bit_count) {
119 out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*);
120 out_bits.* += u8_bit_count;
121 continue;
122 }
123
124 const shift = @intCast(u3, u8_bit_count - n);
125 const value = (next_byte << shift) >> shift;
126 out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*);
127 out_bits.* += n;
128 self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n));
129 self.bit_count = shift;
130 },
131 }
132 }
133
134 return @intCast(U, out_buffer);
135 }
136
137 pub fn alignToByte(self: *Self) void {
138 self.bit_buffer = 0;
139 self.bit_count = 0;
140 }
141
142 pub fn read(self: *Self, buffer: []u8) Error!usize {
143 var out_bits: usize = undefined;
144 var out_bits_total = @as(usize, 0);
145 //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced
146 if (self.bit_count > 0) {
147 for (buffer) |*b, i| {
148 b.* = try self.readBits(u8, u8_bit_count, &out_bits);
149 out_bits_total += out_bits;
150 }
151 const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0);
152 return (out_bits_total / u8_bit_count) + incomplete_byte;
153 }
154
155 return self.in_stream.read(buffer);
156 }
157
158 pub fn inStream(self: *Self) InStream {
159 return .{ .context = self };
160 }
161 };
162}
163
164pub fn bitInStream(
165 comptime endian: builtin.Endian,
166 underlying_stream: var,
167) BitInStream(endian, @TypeOf(underlying_stream)) {
168 return BitInStream(endian, @TypeOf(underlying_stream)).init(underlying_stream);
169}
170
171test "api coverage" {
172 const mem_be = [_]u8{ 0b11001101, 0b00001011 };
173 const mem_le = [_]u8{ 0b00011101, 0b10010101 };
174
175 var mem_in_be = io.fixedBufferStream(&mem_be);
176 var bit_stream_be = bitInStream(.Big, mem_in_be.inStream());
177
178 var out_bits: usize = undefined;
179
180 const expect = testing.expect;
181 const expectError = testing.expectError;
182
183 expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
184 expect(out_bits == 1);
185 expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
186 expect(out_bits == 2);
187 expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
188 expect(out_bits == 3);
189 expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
190 expect(out_bits == 4);
191 expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
192 expect(out_bits == 5);
193 expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
194 expect(out_bits == 1);
195
196 mem_in_be.pos = 0;
197 bit_stream_be.bit_count = 0;
198 expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
199 expect(out_bits == 15);
200
201 mem_in_be.pos = 0;
202 bit_stream_be.bit_count = 0;
203 expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
204 expect(out_bits == 16);
205
206 _ = try bit_stream_be.readBits(u0, 0, &out_bits);
207
208 expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
209 expect(out_bits == 0);
210 expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1));
211
212 var mem_in_le = io.fixedBufferStream(&mem_le);
213 var bit_stream_le = bitInStream(.Little, mem_in_le.inStream());
214
215 expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
216 expect(out_bits == 1);
217 expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
218 expect(out_bits == 2);
219 expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
220 expect(out_bits == 3);
221 expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
222 expect(out_bits == 4);
223 expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
224 expect(out_bits == 5);
225 expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
226 expect(out_bits == 1);
227
228 mem_in_le.pos = 0;
229 bit_stream_le.bit_count = 0;
230 expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
231 expect(out_bits == 15);
232
233 mem_in_le.pos = 0;
234 bit_stream_le.bit_count = 0;
235 expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
236 expect(out_bits == 16);
237
238 _ = try bit_stream_le.readBits(u0, 0, &out_bits);
239
240 expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
241 expect(out_bits == 0);
242 expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1));
243}
lib/std/io/bit_out_stream.zig created+197
......@@ -0,0 +1,197 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const testing = std.testing;
5const assert = std.debug.assert;
6const trait = std.meta.trait;
7const meta = std.meta;
8const math = std.math;
9
10/// Creates a stream which allows for writing bit fields to another stream
11pub fn BitOutStream(endian: builtin.Endian, comptime OutStreamType: type) type {
12 return struct {
13 out_stream: OutStreamType,
14 bit_buffer: u8,
15 bit_count: u4,
16
17 pub const Error = OutStreamType.Error;
18 pub const OutStream = io.OutStream(*Self, Error, write);
19
20 const Self = @This();
21 const u8_bit_count = comptime meta.bitCount(u8);
22 const u4_bit_count = comptime meta.bitCount(u4);
23
24 pub fn init(out_stream: OutStreamType) Self {
25 return Self{
26 .out_stream = out_stream,
27 .bit_buffer = 0,
28 .bit_count = 0,
29 };
30 }
31
32 /// Write the specified number of bits to the stream from the least significant bits of
33 /// the specified unsigned int value. Bits will only be written to the stream when there
34 /// are enough to fill a byte.
35 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
36 if (bits == 0) return;
37
38 const U = @TypeOf(value);
39 comptime assert(trait.isUnsignedInt(U));
40
41 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
42 // related to shifting and casting.
43 const u_bit_count = comptime meta.bitCount(U);
44 const buf_bit_count = bc: {
45 assert(u_bit_count >= bits);
46 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
47 };
48 const Buf = std.meta.IntType(false, buf_bit_count);
49 const BufShift = math.Log2Int(Buf);
50
51 const buf_value = @intCast(Buf, value);
52
53 const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
54 var in_buffer = switch (endian) {
55 .Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
56 .Little => buf_value,
57 };
58 var in_bits = bits;
59
60 if (self.bit_count > 0) {
61 const bits_remaining = u8_bit_count - self.bit_count;
62 const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
63 switch (endian) {
64 .Big => {
65 const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
66 const v = @intCast(u8, in_buffer >> shift);
67 self.bit_buffer |= v;
68 in_buffer <<= n;
69 },
70 .Little => {
71 const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
72 self.bit_buffer |= v;
73 in_buffer >>= n;
74 },
75 }
76 self.bit_count += n;
77 in_bits -= n;
78
79 //if we didn't fill the buffer, it's because bits < bits_remaining;
80 if (self.bit_count != u8_bit_count) return;
81 try self.out_stream.writeByte(self.bit_buffer);
82 self.bit_buffer = 0;
83 self.bit_count = 0;
84 }
85 //at this point we know bit_buffer is empty
86
87 //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
88 while (in_bits >= u8_bit_count) {
89 switch (endian) {
90 .Big => {
91 const v = @intCast(u8, in_buffer >> high_byte_shift);
92 try self.out_stream.writeByte(v);
93 in_buffer <<= @intCast(u3, u8_bit_count - 1);
94 in_buffer <<= 1;
95 },
96 .Little => {
97 const v = @truncate(u8, in_buffer);
98 try self.out_stream.writeByte(v);
99 in_buffer >>= @intCast(u3, u8_bit_count - 1);
100 in_buffer >>= 1;
101 },
102 }
103 in_bits -= u8_bit_count;
104 }
105
106 if (in_bits > 0) {
107 self.bit_count = @intCast(u4, in_bits);
108 self.bit_buffer = switch (endian) {
109 .Big => @truncate(u8, in_buffer >> high_byte_shift),
110 .Little => @truncate(u8, in_buffer),
111 };
112 }
113 }
114
115 /// Flush any remaining bits to the stream.
116 pub fn flushBits(self: *Self) Error!void {
117 if (self.bit_count == 0) return;
118 try self.out_stream.writeByte(self.bit_buffer);
119 self.bit_buffer = 0;
120 self.bit_count = 0;
121 }
122
123 pub fn write(self: *Self, buffer: []const u8) Error!usize {
124 // TODO: I'm not sure this is a good idea, maybe flushBits should be forced
125 if (self.bit_count > 0) {
126 for (buffer) |b, i|
127 try self.writeBits(b, u8_bit_count);
128 return buffer.len;
129 }
130
131 return self.out_stream.write(buffer);
132 }
133
134 pub fn outStream(self: *Self) OutStream {
135 return .{ .context = self };
136 }
137 };
138}
139
140pub fn bitOutStream(
141 comptime endian: builtin.Endian,
142 underlying_stream: var,
143) BitOutStream(endian, @TypeOf(underlying_stream)) {
144 return BitOutStream(endian, @TypeOf(underlying_stream)).init(underlying_stream);
145}
146
147test "api coverage" {
148 var mem_be = [_]u8{0} ** 2;
149 var mem_le = [_]u8{0} ** 2;
150
151 var mem_out_be = io.fixedBufferStream(&mem_be);
152 var bit_stream_be = bitOutStream(.Big, mem_out_be.outStream());
153
154 try bit_stream_be.writeBits(@as(u2, 1), 1);
155 try bit_stream_be.writeBits(@as(u5, 2), 2);
156 try bit_stream_be.writeBits(@as(u128, 3), 3);
157 try bit_stream_be.writeBits(@as(u8, 4), 4);
158 try bit_stream_be.writeBits(@as(u9, 5), 5);
159 try bit_stream_be.writeBits(@as(u1, 1), 1);
160
161 testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
162
163 mem_out_be.pos = 0;
164
165 try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15);
166 try bit_stream_be.flushBits();
167 testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
168
169 mem_out_be.pos = 0;
170 try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16);
171 testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
172
173 try bit_stream_be.writeBits(@as(u0, 0), 0);
174
175 var mem_out_le = io.fixedBufferStream(&mem_le);
176 var bit_stream_le = bitOutStream(.Little, mem_out_le.outStream());
177
178 try bit_stream_le.writeBits(@as(u2, 1), 1);
179 try bit_stream_le.writeBits(@as(u5, 2), 2);
180 try bit_stream_le.writeBits(@as(u128, 3), 3);
181 try bit_stream_le.writeBits(@as(u8, 4), 4);
182 try bit_stream_le.writeBits(@as(u9, 5), 5);
183 try bit_stream_le.writeBits(@as(u1, 1), 1);
184
185 testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
186
187 mem_out_le.pos = 0;
188 try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15);
189 try bit_stream_le.flushBits();
190 testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
191
192 mem_out_le.pos = 0;
193 try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16);
194 testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
195
196 try bit_stream_le.writeBits(@as(u0, 0), 0);
197}
lib/std/io/buffered_atomic_file.zig created+50
......@@ -0,0 +1,50 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const File = std.fs.File;
5
6pub const BufferedAtomicFile = struct {
7 atomic_file: fs.AtomicFile,
8 file_stream: File.OutStream,
9 buffered_stream: BufferedOutStream,
10 allocator: *mem.Allocator,
11
12 pub const buffer_size = 4096;
13 pub const BufferedOutStream = std.io.BufferedOutStream(buffer_size, File.OutStream);
14 pub const OutStream = std.io.OutStream(*BufferedOutStream, BufferedOutStream.Error, BufferedOutStream.write);
15
16 /// TODO when https://github.com/ziglang/zig/issues/2761 is solved
17 /// this API will not need an allocator
18 pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
19 var self = try allocator.create(BufferedAtomicFile);
20 self.* = BufferedAtomicFile{
21 .atomic_file = undefined,
22 .file_stream = undefined,
23 .buffered_stream = undefined,
24 .allocator = allocator,
25 };
26 errdefer allocator.destroy(self);
27
28 self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode);
29 errdefer self.atomic_file.deinit();
30
31 self.file_stream = self.atomic_file.file.outStream();
32 self.buffered_stream = .{ .unbuffered_out_stream = self.file_stream };
33 return self;
34 }
35
36 /// always call destroy, even after successful finish()
37 pub fn destroy(self: *BufferedAtomicFile) void {
38 self.atomic_file.deinit();
39 self.allocator.destroy(self);
40 }
41
42 pub fn finish(self: *BufferedAtomicFile) !void {
43 try self.buffered_stream.flush();
44 try self.atomic_file.finish();
45 }
46
47 pub fn stream(self: *BufferedAtomicFile) OutStream {
48 return .{ .context = &self.buffered_stream };
49 }
50};
lib/std/io/buffered_in_stream.zig created+86
......@@ -0,0 +1,86 @@
1const std = @import("../std.zig");
2const io = std.io;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType: type) type {
7 return struct {
8 unbuffered_in_stream: InStreamType,
9 fifo: FifoType = FifoType.init(),
10
11 pub const Error = InStreamType.Error;
12 pub const InStream = io.InStream(*Self, Error, read);
13
14 const Self = @This();
15 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
16
17 pub fn read(self: *Self, dest: []u8) Error!usize {
18 var dest_index: usize = 0;
19 while (dest_index < dest.len) {
20 const written = self.fifo.read(dest[dest_index..]);
21 if (written == 0) {
22 // fifo empty, fill it
23 const writable = self.fifo.writableSlice(0);
24 assert(writable.len > 0);
25 const n = try self.unbuffered_in_stream.read(writable);
26 if (n == 0) {
27 // reading from the unbuffered stream returned nothing
28 // so we have nothing left to read.
29 return dest_index;
30 }
31 self.fifo.update(n);
32 }
33 dest_index += written;
34 }
35 return dest.len;
36 }
37
38 pub fn inStream(self: *Self) InStream {
39 return .{ .context = self };
40 }
41 };
42}
43
44pub fn bufferedInStream(underlying_stream: var) BufferedInStream(4096, @TypeOf(underlying_stream)) {
45 return .{ .unbuffered_in_stream = underlying_stream };
46}
47
48test "io.BufferedInStream" {
49 const OneByteReadInStream = struct {
50 str: []const u8,
51 curr: usize,
52
53 const Error = error{NoError};
54 const Self = @This();
55 const InStream = io.InStream(*Self, Error, read);
56
57 fn init(str: []const u8) Self {
58 return Self{
59 .str = str,
60 .curr = 0,
61 };
62 }
63
64 fn read(self: *Self, dest: []u8) Error!usize {
65 if (self.str.len <= self.curr or dest.len == 0)
66 return 0;
67
68 dest[0] = self.str[self.curr];
69 self.curr += 1;
70 return 1;
71 }
72
73 fn inStream(self: *Self) InStream {
74 return .{ .context = self };
75 }
76 };
77
78 const str = "This is a test";
79 var one_byte_stream = OneByteReadInStream.init(str);
80 var buf_in_stream = bufferedInStream(one_byte_stream.inStream());
81 const stream = buf_in_stream.inStream();
82
83 const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
84 defer testing.allocator.free(res);
85 testing.expectEqualSlices(u8, str, res);
86}
lib/std/io/buffered_out_stream.zig created+41
......@@ -0,0 +1,41 @@
1const std = @import("../std.zig");
2const io = std.io;
3
4pub fn BufferedOutStream(comptime buffer_size: usize, comptime OutStreamType: type) type {
5 return struct {
6 unbuffered_out_stream: OutStreamType,
7 fifo: FifoType = FifoType.init(),
8
9 pub const Error = OutStreamType.Error;
10 pub const OutStream = io.OutStream(*Self, Error, write);
11
12 const Self = @This();
13 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
14
15 pub fn flush(self: *Self) !void {
16 while (true) {
17 const slice = self.fifo.readableSlice(0);
18 if (slice.len == 0) break;
19 try self.unbuffered_out_stream.writeAll(slice);
20 self.fifo.discard(slice.len);
21 }
22 }
23
24 pub fn outStream(self: *Self) OutStream {
25 return .{ .context = self };
26 }
27
28 pub fn write(self: *Self, bytes: []const u8) Error!usize {
29 if (bytes.len >= self.fifo.writableLength()) {
30 try self.flush();
31 return self.unbuffered_out_stream.write(bytes);
32 }
33 self.fifo.writeAssumeCapacity(bytes);
34 return bytes.len;
35 }
36 };
37}
38
39pub fn bufferedOutStream(underlying_stream: var) BufferedOutStream(4096, @TypeOf(underlying_stream)) {
40 return .{ .unbuffered_out_stream = underlying_stream };
41}
lib/std/io/c_out_stream.zig+37-36
......@@ -1,43 +1,44 @@
11const std = @import("../std.zig");
2const os = std.os;
3const OutStream = std.io.OutStream;
4const builtin = @import("builtin");
2const builtin = std.builtin;
3const io = std.io;
4const testing = std.testing;
55
6/// TODO make a proposal to make `std.fs.File` use *FILE when linking libc and this just becomes
7/// std.io.FileOutStream because std.fs.File.write would do this when linking
8/// libc.
9pub const COutStream = struct {
10 pub const Error = std.fs.File.WriteError;
11 pub const Stream = OutStream(Error);
6pub const COutStream = io.OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite);
127
13 stream: Stream,
14 c_file: *std.c.FILE,
8pub fn cOutStream(c_file: *std.c.FILE) COutStream {
9 return .{ .context = c_file };
10}
1511
16 pub fn init(c_file: *std.c.FILE) COutStream {
17 return COutStream{
18 .c_file = c_file,
19 .stream = Stream{ .writeFn = writeFn },
20 };
12fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
13 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
14 if (amt_written >= 0) return amt_written;
15 switch (std.c._errno().*) {
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)),
2129 }
30}
2231
23 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
24 const self = @fieldParentPtr(COutStream, "stream", out_stream);
25 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file);
26 if (amt_written >= 0) return amt_written;
27 switch (std.c._errno().*) {
28 0 => unreachable,
29 os.EINVAL => unreachable,
30 os.EFAULT => unreachable,
31 os.EAGAIN => unreachable, // this is a blocking API
32 os.EBADF => unreachable, // always a race condition
33 os.EDESTADDRREQ => unreachable, // connect was never called
34 os.EDQUOT => return error.DiskQuota,
35 os.EFBIG => return error.FileTooBig,
36 os.EIO => return error.InputOutput,
37 os.ENOSPC => return error.NoSpaceLeft,
38 os.EPERM => return error.AccessDenied,
39 os.EPIPE => return error.BrokenPipe,
40 else => |err| return os.unexpectedErrno(@intCast(usize, err)),
41 }
32test "" {
33 if (!builtin.link_libc) return error.SkipZigTest;
34
35 const filename = "tmp_io_test_file.txt";
36 const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
37 defer {
38 _ = std.c.fclose(out_file);
39 fs.cwd().deleteFileC(filename) catch {};
4240 }
43};
41
42 const out_stream = &io.COutStream.init(out_file).stream;
43 try out_stream.print("hi: {}\n", .{@as(i32, 123)});
44}
lib/std/io/counting_out_stream.zig created+39
......@@ -0,0 +1,39 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4
5/// An OutStream that counts how many bytes has been written to it.
6pub fn CountingOutStream(comptime OutStreamType: type) type {
7 return struct {
8 bytes_written: u64,
9 child_stream: OutStreamType,
10
11 pub const Error = OutStreamType.Error;
12 pub const OutStream = io.OutStream(*Self, Error, write);
13
14 const Self = @This();
15
16 pub fn write(self: *Self, bytes: []const u8) Error!usize {
17 const amt = try self.child_stream.write(bytes);
18 self.bytes_written += amt;
19 return amt;
20 }
21
22 pub fn outStream(self: *Self) OutStream {
23 return .{ .context = self };
24 }
25 };
26}
27
28pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) {
29 return .{ .bytes_written = 0, .child_stream = child_stream };
30}
31
32test "io.CountingOutStream" {
33 var counting_stream = countingOutStream(std.io.null_out_stream);
34 const stream = counting_stream.outStream();
35
36 const bytes = "yay" ** 100;
37 stream.writeAll(bytes) catch unreachable;
38 testing.expect(counting_stream.bytes_written == bytes.len);
39}
lib/std/io/fixed_buffer_stream.zig created+171
......@@ -0,0 +1,171 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
6
7/// This turns a byte buffer into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`.
8/// If the supplied byte buffer is const, then `io.OutStream` is not available.
9pub fn FixedBufferStream(comptime Buffer: type) type {
10 return struct {
11 /// `Buffer` is either a `[]u8` or `[]const u8`.
12 buffer: Buffer,
13 pos: usize,
14
15 pub const ReadError = error{};
16 pub const WriteError = error{NoSpaceLeft};
17 pub const SeekError = error{};
18 pub const GetSeekPosError = error{};
19
20 pub const InStream = io.InStream(*Self, ReadError, read);
21 pub const OutStream = io.OutStream(*Self, WriteError, write);
22
23 pub const SeekableStream = io.SeekableStream(
24 *Self,
25 SeekError,
26 GetSeekPosError,
27 seekTo,
28 seekBy,
29 getPos,
30 getEndPos,
31 );
32
33 const Self = @This();
34
35 pub fn inStream(self: *Self) InStream {
36 return .{ .context = self };
37 }
38
39 pub fn outStream(self: *Self) OutStream {
40 return .{ .context = self };
41 }
42
43 pub fn seekableStream(self: *Self) SeekableStream {
44 return .{ .context = self };
45 }
46
47 pub fn read(self: *Self, dest: []u8) ReadError!usize {
48 const size = std.math.min(dest.len, self.buffer.len - self.pos);
49 const end = self.pos + size;
50
51 mem.copy(u8, dest[0..size], self.buffer[self.pos..end]);
52 self.pos = end;
53
54 return size;
55 }
56
57 /// If the returned number of bytes written is less than requested, the
58 /// buffer is full. Returns `error.NoSpaceLeft` when no bytes would be written.
59 /// Note: `error.NoSpaceLeft` matches the corresponding error from
60 /// `std.fs.File.WriteError`.
61 pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
62 if (bytes.len == 0) return 0;
63 if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
64
65 const n = if (self.pos + bytes.len <= self.buffer.len)
66 bytes.len
67 else
68 self.buffer.len - self.pos;
69
70 mem.copy(u8, self.buffer[self.pos .. self.pos + n], bytes[0..n]);
71 self.pos += n;
72
73 if (n == 0) return error.NoSpaceLeft;
74
75 return n;
76 }
77
78 pub fn seekTo(self: *Self, pos: u64) SeekError!void {
79 self.pos = if (std.math.cast(usize, pos)) |x| x else |_| self.buffer.len;
80 }
81
82 pub fn seekBy(self: *Self, amt: i64) SeekError!void {
83 if (amt < 0) {
84 const abs_amt = std.math.absCast(amt);
85 const abs_amt_usize = std.math.cast(usize, abs_amt) catch std.math.maxInt(usize);
86 if (abs_amt_usize > self.pos) {
87 self.pos = 0;
88 } else {
89 self.pos -= abs_amt_usize;
90 }
91 } else {
92 const amt_usize = std.math.cast(usize, amt) catch std.math.maxInt(usize);
93 const new_pos = std.math.add(usize, self.pos, amt_usize) catch std.math.maxInt(usize);
94 self.pos = std.math.min(self.buffer.len, new_pos);
95 }
96 }
97
98 pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
99 return self.buffer.len;
100 }
101
102 pub fn getPos(self: *Self) GetSeekPosError!u64 {
103 return self.pos;
104 }
105
106 pub fn getWritten(self: Self) []const u8 {
107 return self.buffer[0..self.pos];
108 }
109
110 pub fn reset(self: *Self) void {
111 self.pos = 0;
112 }
113 };
114}
115
116pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
117 return .{ .buffer = mem.span(buffer), .pos = 0 };
118}
119
120fn NonSentinelSpan(comptime T: type) type {
121 var ptr_info = @typeInfo(mem.Span(T)).Pointer;
122 ptr_info.sentinel = null;
123 return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info });
124}
125
126test "FixedBufferStream output" {
127 var buf: [255]u8 = undefined;
128 var fbs = fixedBufferStream(&buf);
129 const stream = fbs.outStream();
130
131 try stream.print("{}{}!", .{ "Hello", "World" });
132 testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
133}
134
135test "FixedBufferStream output 2" {
136 var buffer: [10]u8 = undefined;
137 var fbs = fixedBufferStream(&buffer);
138
139 try fbs.outStream().writeAll("Hello");
140 testing.expect(mem.eql(u8, fbs.getWritten(), "Hello"));
141
142 try fbs.outStream().writeAll("world");
143 testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
144
145 testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("!"));
146 testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
147
148 fbs.reset();
149 testing.expect(fbs.getWritten().len == 0);
150
151 testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("Hello world!"));
152 testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl"));
153}
154
155test "FixedBufferStream input" {
156 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
157 var fbs = fixedBufferStream(&bytes);
158
159 var dest: [4]u8 = undefined;
160
161 var read = try fbs.inStream().read(dest[0..4]);
162 testing.expect(read == 4);
163 testing.expect(mem.eql(u8, dest[0..4], bytes[0..4]));
164
165 read = try fbs.inStream().read(dest[0..4]);
166 testing.expect(read == 3);
167 testing.expect(mem.eql(u8, dest[0..3], bytes[4..7]));
168
169 read = try fbs.inStream().read(dest[0..4]);
170 testing.expect(read == 0);
171}
lib/std/io/in_stream.zig+36-53
......@@ -1,53 +1,37 @@
11const std = @import("../std.zig");
2const builtin = @import("builtin");
3const root = @import("root");
2const builtin = std.builtin;
43const math = std.math;
54const assert = std.debug.assert;
65const mem = std.mem;
76const Buffer = std.Buffer;
87const testing = std.testing;
98
10pub const default_stack_size = 1 * 1024 * 1024;
11pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))
12 root.stack_size_std_io_InStream
13else
14 default_stack_size;
15
16pub fn InStream(comptime ReadError: type) type {
9pub fn InStream(
10 comptime Context: type,
11 comptime ReadError: type,
12 /// Returns the number of bytes read. It may be less than buffer.len.
13 /// If the number of bytes read is 0, it means end of stream.
14 /// End of stream is not an error condition.
15 comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
16) type {
1717 return struct {
18 const Self = @This();
1918 pub const Error = ReadError;
20 pub const ReadFn = if (std.io.is_async)
21 async fn (self: *Self, buffer: []u8) Error!usize
22 else
23 fn (self: *Self, buffer: []u8) Error!usize;
2419
25 /// Returns the number of bytes read. It may be less than buffer.len.
26 /// If the number of bytes read is 0, it means end of stream.
27 /// End of stream is not an error condition.
28 readFn: ReadFn,
20 context: Context,
21
22 const Self = @This();
2923
3024 /// Returns the number of bytes read. It may be less than buffer.len.
3125 /// If the number of bytes read is 0, it means end of stream.
3226 /// End of stream is not an error condition.
33 pub fn read(self: *Self, buffer: []u8) Error!usize {
34 if (std.io.is_async) {
35 // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read.
36 @setRuntimeSafety(false);
37 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
38 return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer);
39 } else {
40 return self.readFn(self, buffer);
41 }
27 pub fn read(self: Self, buffer: []u8) Error!usize {
28 return readFn(self.context, buffer);
4229 }
4330
44 /// Deprecated: use `readAll`.
45 pub const readFull = readAll;
46
47 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
31 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
4832 /// means the stream reached the end. Reaching the end of a stream is not an error
4933 /// condition.
50 pub fn readAll(self: *Self, buffer: []u8) Error!usize {
34 pub fn readAll(self: Self, buffer: []u8) Error!usize {
5135 var index: usize = 0;
5236 while (index != buffer.len) {
5337 const amt = try self.read(buffer[index..]);
......@@ -59,13 +43,13 @@ pub fn InStream(comptime ReadError: type) type {
5943
6044 /// Returns the number of bytes read. If the number read would be smaller than buf.len,
6145 /// error.EndOfStream is returned instead.
62 pub fn readNoEof(self: *Self, buf: []u8) !void {
46 pub fn readNoEof(self: Self, buf: []u8) !void {
6347 const amt_read = try self.readAll(buf);
6448 if (amt_read < buf.len) return error.EndOfStream;
6549 }
6650
6751 /// 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 {
6953 buffer.list.shrink(0);
7054 try self.readAllArrayList(&buffer.list, max_size);
7155 errdefer buffer.shrink(0);
......@@ -75,7 +59,7 @@ pub fn InStream(comptime ReadError: type) type {
7559 /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found.
7660 /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned
7761 /// 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 {
7963 try array_list.ensureCapacity(math.min(max_append_size, 4096));
8064 const original_len = array_list.len;
8165 var start_index: usize = original_len;
......@@ -104,7 +88,7 @@ pub fn InStream(comptime ReadError: type) type {
10488 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
10589 /// Caller owns returned memory.
10690 /// 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 {
10892 var array_list = std.ArrayList(u8).init(allocator);
10993 defer array_list.deinit();
11094 try self.readAllArrayList(&array_list, max_size);
......@@ -116,7 +100,7 @@ pub fn InStream(comptime ReadError: type) type {
116100 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
117101 /// `std.ArrayList` is populated with `max_size` bytes from the stream.
118102 pub fn readUntilDelimiterArrayList(
119 self: *Self,
103 self: Self,
120104 array_list: *std.ArrayList(u8),
121105 delimiter: u8,
122106 max_size: usize,
......@@ -142,7 +126,7 @@ pub fn InStream(comptime ReadError: type) type {
142126 /// Caller owns returned memory.
143127 /// If this function returns an error, the contents from the stream read so far are lost.
144128 pub fn readUntilDelimiterAlloc(
145 self: *Self,
129 self: Self,
146130 allocator: *mem.Allocator,
147131 delimiter: u8,
148132 max_size: usize,
......@@ -159,7 +143,7 @@ pub fn InStream(comptime ReadError: type) type {
159143 /// function is called again after that, returns null.
160144 /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
161145 /// 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 {
163147 var index: usize = 0;
164148 while (true) {
165149 const byte = self.readByte() catch |err| switch (err) {
......@@ -184,7 +168,7 @@ pub fn InStream(comptime ReadError: type) type {
184168 /// Reads from the stream until specified byte is found, discarding all data,
185169 /// including the delimiter.
186170 /// 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 {
188172 while (true) {
189173 const byte = self.readByte() catch |err| switch (err) {
190174 error.EndOfStream => return,
......@@ -195,7 +179,7 @@ pub fn InStream(comptime ReadError: type) type {
195179 }
196180
197181 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
198 pub fn readByte(self: *Self) !u8 {
182 pub fn readByte(self: Self) !u8 {
199183 var result: [1]u8 = undefined;
200184 const amt_read = try self.read(result[0..]);
201185 if (amt_read < 1) return error.EndOfStream;
......@@ -203,43 +187,43 @@ pub fn InStream(comptime ReadError: type) type {
203187 }
204188
205189 /// Same as `readByte` except the returned byte is signed.
206 pub fn readByteSigned(self: *Self) !i8 {
190 pub fn readByteSigned(self: Self) !i8 {
207191 return @bitCast(i8, try self.readByte());
208192 }
209193
210194 /// 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 {
212196 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
213197 try self.readNoEof(bytes[0..]);
214198 return mem.readIntNative(T, &bytes);
215199 }
216200
217201 /// 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 {
219203 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
220204 try self.readNoEof(bytes[0..]);
221205 return mem.readIntForeign(T, &bytes);
222206 }
223207
224 pub fn readIntLittle(self: *Self, comptime T: type) !T {
208 pub fn readIntLittle(self: Self, comptime T: type) !T {
225209 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
226210 try self.readNoEof(bytes[0..]);
227211 return mem.readIntLittle(T, &bytes);
228212 }
229213
230 pub fn readIntBig(self: *Self, comptime T: type) !T {
214 pub fn readIntBig(self: Self, comptime T: type) !T {
231215 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
232216 try self.readNoEof(bytes[0..]);
233217 return mem.readIntBig(T, &bytes);
234218 }
235219
236 pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
220 pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T {
237221 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
238222 try self.readNoEof(bytes[0..]);
239223 return mem.readInt(T, &bytes, endian);
240224 }
241225
242 pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
226 pub fn readVarInt(self: Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
243227 assert(size <= @sizeOf(ReturnType));
244228 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
245229 const bytes = bytes_buf[0..size];
......@@ -247,14 +231,14 @@ pub fn InStream(comptime ReadError: type) type {
247231 return mem.readVarInt(ReturnType, bytes, endian);
248232 }
249233
250 pub fn skipBytes(self: *Self, num_bytes: u64) !void {
234 pub fn skipBytes(self: Self, num_bytes: u64) !void {
251235 var i: u64 = 0;
252236 while (i < num_bytes) : (i += 1) {
253237 _ = try self.readByte();
254238 }
255239 }
256240
257 pub fn readStruct(self: *Self, comptime T: type) !T {
241 pub fn readStruct(self: Self, comptime T: type) !T {
258242 // Only extern and packed structs have defined in-memory layout.
259243 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
260244 var res: [1]T = undefined;
......@@ -265,7 +249,7 @@ pub fn InStream(comptime ReadError: type) type {
265249 /// Reads an integer with the same size as the given enum's tag type. If the integer matches
266250 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error.
267251 /// 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 {
269253 const E = error{
270254 /// An integer was read, but it did not match any of the tags in the supplied enum.
271255 InvalidValue,
......@@ -286,8 +270,7 @@ pub fn InStream(comptime ReadError: type) type {
286270
287271test "InStream" {
288272 var buf = "a\x02".*;
289 var slice_stream = std.io.SliceInStream.init(&buf);
290 const in_stream = &slice_stream.stream;
273 const in_stream = std.io.fixedBufferStream(&buf).inStream();
291274 testing.expect((try in_stream.readByte()) == 'a');
292275 testing.expect((try in_stream.readEnum(enum(u8) {
293276 a = 0,
lib/std/io/out_stream.zig+33-42
......@@ -1,94 +1,85 @@
11const std = @import("../std.zig");
2const builtin = @import("builtin");
3const root = @import("root");
2const builtin = std.builtin;
43const mem = std.mem;
54
6pub const default_stack_size = 1 * 1024 * 1024;
7pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))
8 root.stack_size_std_io_OutStream
9else
10 default_stack_size;
11
12pub fn OutStream(comptime WriteError: type) type {
5pub fn OutStream(
6 comptime Context: type,
7 comptime WriteError: type,
8 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
9) type {
1310 return struct {
11 context: Context,
12
1413 const Self = @This();
1514 pub const Error = WriteError;
16 pub const WriteFn = if (std.io.is_async)
17 async fn (self: *Self, bytes: []const u8) Error!usize
18 else
19 fn (self: *Self, bytes: []const u8) Error!usize;
2015
21 writeFn: WriteFn,
22
23 pub fn writeOnce(self: *Self, bytes: []const u8) Error!usize {
24 if (std.io.is_async) {
25 // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write.
26 @setRuntimeSafety(false);
27 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
28 return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes);
29 } else {
30 return self.writeFn(self, bytes);
31 }
16 pub fn write(self: Self, bytes: []const u8) Error!usize {
17 return writeFn(self.context, bytes);
3218 }
3319
34 pub fn write(self: *Self, bytes: []const u8) Error!void {
20 pub fn writeAll(self: Self, bytes: []const u8) Error!void {
3521 var index: usize = 0;
3622 while (index != bytes.len) {
37 index += try self.writeOnce(bytes[index..]);
23 index += try self.write(bytes[index..]);
3824 }
3925 }
4026
41 pub fn print(self: *Self, comptime format: []const u8, args: var) Error!void {
42 return std.fmt.format(self, Error, write, format, args);
27 pub fn print(self: Self, comptime format: []const u8, args: var) Error!void {
28 return std.fmt.format(self, Error, writeAll, format, args);
4329 }
4430
45 pub fn writeByte(self: *Self, byte: u8) Error!void {
31 pub fn writeByte(self: Self, byte: u8) Error!void {
4632 const array = [1]u8{byte};
47 return self.write(&array);
33 return self.writeAll(&array);
4834 }
4935
50 pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
36 pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
5137 var bytes: [256]u8 = undefined;
5238 mem.set(u8, bytes[0..], byte);
5339
5440 var remaining: usize = n;
5541 while (remaining > 0) {
5642 const to_write = std.math.min(remaining, bytes.len);
57 try self.write(bytes[0..to_write]);
43 try self.writeAll(bytes[0..to_write]);
5844 remaining -= to_write;
5945 }
6046 }
6147
6248 /// Write a native-endian integer.
63 pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
49 /// TODO audit non-power-of-two int sizes
50 pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void {
6451 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
6552 mem.writeIntNative(T, &bytes, value);
66 return self.write(&bytes);
53 return self.writeAll(&bytes);
6754 }
6855
6956 /// Write a foreign-endian integer.
70 pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
57 /// TODO audit non-power-of-two int sizes
58 pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void {
7159 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
7260 mem.writeIntForeign(T, &bytes, value);
73 return self.write(&bytes);
61 return self.writeAll(&bytes);
7462 }
7563
76 pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
64 /// TODO audit non-power-of-two int sizes
65 pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void {
7766 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
7867 mem.writeIntLittle(T, &bytes, value);
79 return self.write(&bytes);
68 return self.writeAll(&bytes);
8069 }
8170
82 pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
71 /// TODO audit non-power-of-two int sizes
72 pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void {
8373 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
8474 mem.writeIntBig(T, &bytes, value);
85 return self.write(&bytes);
75 return self.writeAll(&bytes);
8676 }
8777
88 pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
78 /// TODO audit non-power-of-two int sizes
79 pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
8980 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
9081 mem.writeInt(T, &bytes, value, endian);
91 return self.write(&bytes);
82 return self.writeAll(&bytes);
9283 }
9384 };
9485}
lib/std/io/peek_stream.zig created+112
......@@ -0,0 +1,112 @@
1const std = @import("../std.zig");
2const io = std.io;
3const mem = std.mem;
4const testing = std.testing;
5
6/// Creates a stream which supports 'un-reading' data, so that it can be read again.
7/// This makes look-ahead style parsing much easier.
8/// TODO merge this with `std.io.BufferedInStream`: https://github.com/ziglang/zig/issues/4501
9pub fn PeekStream(
10 comptime buffer_type: std.fifo.LinearFifoBufferType,
11 comptime InStreamType: type,
12) type {
13 return struct {
14 unbuffered_in_stream: InStreamType,
15 fifo: FifoType,
16
17 pub const Error = InStreamType.Error;
18 pub const InStream = io.InStream(*Self, Error, read);
19
20 const Self = @This();
21 const FifoType = std.fifo.LinearFifo(u8, buffer_type);
22
23 pub usingnamespace switch (buffer_type) {
24 .Static => struct {
25 pub fn init(base: InStreamType) Self {
26 return .{
27 .base = base,
28 .fifo = FifoType.init(),
29 };
30 }
31 },
32 .Slice => struct {
33 pub fn init(base: InStreamType, buf: []u8) Self {
34 return .{
35 .base = base,
36 .fifo = FifoType.init(buf),
37 };
38 }
39 },
40 .Dynamic => struct {
41 pub fn init(base: InStreamType, allocator: *mem.Allocator) Self {
42 return .{
43 .base = base,
44 .fifo = FifoType.init(allocator),
45 };
46 }
47 },
48 };
49
50 pub fn putBackByte(self: *Self, byte: u8) !void {
51 try self.putBack(&[_]u8{byte});
52 }
53
54 pub fn putBack(self: *Self, bytes: []const u8) !void {
55 try self.fifo.unget(bytes);
56 }
57
58 pub fn read(self: *Self, dest: []u8) Error!usize {
59 // copy over anything putBack()'d
60 var dest_index = self.fifo.read(dest);
61 if (dest_index == dest.len) return dest_index;
62
63 // ask the backing stream for more
64 dest_index += try self.base.read(dest[dest_index..]);
65 return dest_index;
66 }
67
68 pub fn inStream(self: *Self) InStream {
69 return .{ .context = self };
70 }
71 };
72}
73
74pub fn peekStream(
75 comptime lookahead: comptime_int,
76 underlying_stream: var,
77) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) {
78 return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream);
79}
80
81test "PeekStream" {
82 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
83 var fbs = io.fixedBufferStream(&bytes);
84 var ps = peekStream(2, fbs.inStream());
85
86 var dest: [4]u8 = undefined;
87
88 try ps.putBackByte(9);
89 try ps.putBackByte(10);
90
91 var read = try ps.inStream().read(dest[0..4]);
92 testing.expect(read == 4);
93 testing.expect(dest[0] == 10);
94 testing.expect(dest[1] == 9);
95 testing.expect(mem.eql(u8, dest[2..4], bytes[0..2]));
96
97 read = try ps.inStream().read(dest[0..4]);
98 testing.expect(read == 4);
99 testing.expect(mem.eql(u8, dest[0..4], bytes[2..6]));
100
101 read = try ps.inStream().read(dest[0..4]);
102 testing.expect(read == 2);
103 testing.expect(mem.eql(u8, dest[0..2], bytes[6..8]));
104
105 try ps.putBackByte(11);
106 try ps.putBackByte(12);
107
108 read = try ps.inStream().read(dest[0..4]);
109 testing.expect(read == 2);
110 testing.expect(dest[0] == 12);
111 testing.expect(dest[1] == 11);
112}
lib/std/io/seekable_stream.zig+19-86
......@@ -1,103 +1,36 @@
11const std = @import("../std.zig");
22const InStream = std.io.InStream;
33
4pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type {
4pub fn SeekableStream(
5 comptime Context: type,
6 comptime SeekErrorType: type,
7 comptime GetSeekPosErrorType: type,
8 comptime seekToFn: fn (context: Context, pos: u64) SeekErrorType!void,
9 comptime seekByFn: fn (context: Context, pos: i64) SeekErrorType!void,
10 comptime getPosFn: fn (context: Context) GetSeekPosErrorType!u64,
11 comptime getEndPosFn: fn (context: Context) GetSeekPosErrorType!u64,
12) type {
513 return struct {
14 context: Context,
15
616 const Self = @This();
717 pub const SeekError = SeekErrorType;
818 pub const GetSeekPosError = GetSeekPosErrorType;
919
10 seekToFn: fn (self: *Self, pos: u64) SeekError!void,
11 seekByFn: fn (self: *Self, pos: i64) SeekError!void,
12
13 getPosFn: fn (self: *Self) GetSeekPosError!u64,
14 getEndPosFn: fn (self: *Self) GetSeekPosError!u64,
15
16 pub fn seekTo(self: *Self, pos: u64) SeekError!void {
17 return self.seekToFn(self, pos);
20 pub fn seekTo(self: Self, pos: u64) SeekError!void {
21 return seekToFn(self.context, pos);
1822 }
1923
20 pub fn seekBy(self: *Self, amt: i64) SeekError!void {
21 return self.seekByFn(self, amt);
24 pub fn seekBy(self: Self, amt: i64) SeekError!void {
25 return seekByFn(self.context, amt);
2226 }
2327
24 pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
25 return self.getEndPosFn(self);
28 pub fn getEndPos(self: Self) GetSeekPosError!u64 {
29 return getEndPosFn(self.context);
2630 }
2731
28 pub fn getPos(self: *Self) GetSeekPosError!u64 {
29 return self.getPosFn(self);
32 pub fn getPos(self: Self) GetSeekPosError!u64 {
33 return getPosFn(self.context);
3034 }
3135 };
3236}
33
34pub const SliceSeekableInStream = struct {
35 const Self = @This();
36 pub const Error = error{};
37 pub const SeekError = error{EndOfStream};
38 pub const GetSeekPosError = error{};
39 pub const Stream = InStream(Error);
40 pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError);
41
42 stream: Stream,
43 seekable_stream: SeekableInStream,
44
45 pos: usize,
46 slice: []const u8,
47
48 pub fn init(slice: []const u8) Self {
49 return Self{
50 .slice = slice,
51 .pos = 0,
52 .stream = Stream{ .readFn = readFn },
53 .seekable_stream = SeekableInStream{
54 .seekToFn = seekToFn,
55 .seekByFn = seekByFn,
56 .getEndPosFn = getEndPosFn,
57 .getPosFn = getPosFn,
58 },
59 };
60 }
61
62 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
63 const self = @fieldParentPtr(Self, "stream", in_stream);
64 const size = std.math.min(dest.len, self.slice.len - self.pos);
65 const end = self.pos + size;
66
67 std.mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
68 self.pos = end;
69
70 return size;
71 }
72
73 fn seekToFn(in_stream: *SeekableInStream, pos: u64) SeekError!void {
74 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
75 const usize_pos = @intCast(usize, pos);
76 if (usize_pos > self.slice.len) return error.EndOfStream;
77 self.pos = usize_pos;
78 }
79
80 fn seekByFn(in_stream: *SeekableInStream, amt: i64) SeekError!void {
81 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
82
83 if (amt < 0) {
84 const abs_amt = @intCast(usize, -amt);
85 if (abs_amt > self.pos) return error.EndOfStream;
86 self.pos -= abs_amt;
87 } else {
88 const usize_amt = @intCast(usize, amt);
89 if (self.pos + usize_amt > self.slice.len) return error.EndOfStream;
90 self.pos += usize_amt;
91 }
92 }
93
94 fn getEndPosFn(in_stream: *SeekableInStream) GetSeekPosError!u64 {
95 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
96 return @intCast(u64, self.slice.len);
97 }
98
99 fn getPosFn(in_stream: *SeekableInStream) GetSeekPosError!u64 {
100 const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
101 return @intCast(u64, self.pos);
102 }
103};
lib/std/io/serialization.zig created+606
......@@ -0,0 +1,606 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4
5pub const Packing = enum {
6 /// Pack data to byte alignment
7 Byte,
8
9 /// Pack data to bit alignment
10 Bit,
11};
12
13/// Creates a deserializer that deserializes types from any stream.
14/// If `is_packed` is true, the data stream is treated as bit-packed,
15/// otherwise data is expected to be packed to the smallest byte.
16/// Types may implement a custom deserialization routine with a
17/// function named `deserialize` in the form of:
18/// pub fn deserialize(self: *Self, deserializer: var) !void
19/// which will be called when the deserializer is used to deserialize
20/// that type. It will pass a pointer to the type instance to deserialize
21/// into and a pointer to the deserializer struct.
22pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime InStreamType: type) type {
23 return struct {
24 in_stream: if (packing == .Bit) io.BitInStream(endian, InStreamType) else InStreamType,
25
26 const Self = @This();
27
28 pub fn init(in_stream: InStreamType) Self {
29 return Self{
30 .in_stream = switch (packing) {
31 .Bit => io.bitInStream(endian, in_stream),
32 .Byte => in_stream,
33 },
34 };
35 }
36
37 pub fn alignToByte(self: *Self) void {
38 if (packing == .Byte) return;
39 self.in_stream.alignToByte();
40 }
41
42 //@BUG: inferred error issue. See: #1386
43 fn deserializeInt(self: *Self, comptime T: type) (InStreamType.Error || error{EndOfStream})!T {
44 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
45
46 const u8_bit_count = 8;
47 const t_bit_count = comptime meta.bitCount(T);
48
49 const U = std.meta.IntType(false, t_bit_count);
50 const Log2U = math.Log2Int(U);
51 const int_size = (U.bit_count + 7) / 8;
52
53 if (packing == .Bit) {
54 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
55 return @bitCast(T, result);
56 }
57
58 var buffer: [int_size]u8 = undefined;
59 const read_size = try self.in_stream.read(buffer[0..]);
60 if (read_size < int_size) return error.EndOfStream;
61
62 if (int_size == 1) {
63 if (t_bit_count == 8) return @bitCast(T, buffer[0]);
64 const PossiblySignedByte = std.meta.IntType(T.is_signed, 8);
65 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
66 }
67
68 var result = @as(U, 0);
69 for (buffer) |byte, i| {
70 switch (endian) {
71 .Big => {
72 result = (result << u8_bit_count) | byte;
73 },
74 .Little => {
75 result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
76 },
77 }
78 }
79
80 return @bitCast(T, result);
81 }
82
83 /// Deserializes and returns data of the specified type from the stream
84 pub fn deserialize(self: *Self, comptime T: type) !T {
85 var value: T = undefined;
86 try self.deserializeInto(&value);
87 return value;
88 }
89
90 /// Deserializes data into the type pointed to by `ptr`
91 pub fn deserializeInto(self: *Self, ptr: var) !void {
92 const T = @TypeOf(ptr);
93 comptime assert(trait.is(.Pointer)(T));
94
95 if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
96 for (ptr) |*v|
97 try self.deserializeInto(v);
98 return;
99 }
100
101 comptime assert(trait.isSingleItemPtr(T));
102
103 const C = comptime meta.Child(T);
104 const child_type_id = @typeInfo(C);
105
106 //custom deserializer: fn(self: *Self, deserializer: var) !void
107 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
108
109 if (comptime trait.isPacked(C) and packing != .Bit) {
110 var packed_deserializer = deserializer(endian, .Bit, self.in_stream);
111 return packed_deserializer.deserializeInto(ptr);
112 }
113
114 switch (child_type_id) {
115 .Void => return,
116 .Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
117 .Float, .Int => ptr.* = try self.deserializeInt(C),
118 .Struct => {
119 const info = @typeInfo(C).Struct;
120
121 inline for (info.fields) |*field_info| {
122 const name = field_info.name;
123 const FieldType = field_info.field_type;
124
125 if (FieldType == void or FieldType == u0) continue;
126
127 //it doesn't make any sense to read pointers
128 if (comptime trait.is(.Pointer)(FieldType)) {
129 @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
130 @typeName(C) ++ " because it " ++ "is of pointer-type " ++
131 @typeName(FieldType) ++ ".");
132 }
133
134 try self.deserializeInto(&@field(ptr, name));
135 }
136 },
137 .Union => {
138 const info = @typeInfo(C).Union;
139 if (info.tag_type) |TagType| {
140 //we avoid duplicate iteration over the enum tags
141 // by getting the int directly and casting it without
142 // safety. If it is bad, it will be caught anyway.
143 const TagInt = @TagType(TagType);
144 const tag = try self.deserializeInt(TagInt);
145
146 inline for (info.fields) |field_info| {
147 if (field_info.enum_field.?.value == tag) {
148 const name = field_info.name;
149 const FieldType = field_info.field_type;
150 ptr.* = @unionInit(C, name, undefined);
151 try self.deserializeInto(&@field(ptr, name));
152 return;
153 }
154 }
155 //This is reachable if the enum data is bad
156 return error.InvalidEnumTag;
157 }
158 @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
159 " because it is an untagged union. Use a custom deserialize().");
160 },
161 .Optional => {
162 const OC = comptime meta.Child(C);
163 const exists = (try self.deserializeInt(u1)) > 0;
164 if (!exists) {
165 ptr.* = null;
166 return;
167 }
168
169 ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
170 const val_ptr = &ptr.*.?;
171 try self.deserializeInto(val_ptr);
172 },
173 .Enum => {
174 var value = try self.deserializeInt(@TagType(C));
175 ptr.* = try meta.intToEnum(C, value);
176 },
177 else => {
178 @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
179 },
180 }
181 }
182 };
183}
184
185pub fn deserializer(
186 comptime endian: builtin.Endian,
187 comptime packing: Packing,
188 in_stream: var,
189) Deserializer(endian, packing, @TypeOf(in_stream)) {
190 return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
191}
192
193/// Creates a serializer that serializes types to any stream.
194/// If `is_packed` is true, the data will be bit-packed into the stream.
195/// Note that the you must call `serializer.flush()` when you are done
196/// writing bit-packed data in order ensure any unwritten bits are committed.
197/// If `is_packed` is false, data is packed to the smallest byte. In the case
198/// of packed structs, the struct will written bit-packed and with the specified
199/// endianess, after which data will resume being written at the next byte boundary.
200/// Types may implement a custom serialization routine with a
201/// function named `serialize` in the form of:
202/// pub fn serialize(self: Self, serializer: var) !void
203/// which will be called when the serializer is used to serialize that type. It will
204/// pass a const pointer to the type instance to be serialized and a pointer
205/// to the serializer struct.
206pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
207 return struct {
208 out_stream: if (packing == .Bit) BitOutStream(endian, OutStreamType) else OutStreamType,
209
210 const Self = @This();
211 pub const Error = OutStreamType.Error;
212
213 pub fn init(out_stream: OutStreamType) Self {
214 return Self{
215 .out_stream = switch (packing) {
216 .Bit => io.bitOutStream(endian, out_stream),
217 .Byte => out_stream,
218 },
219 };
220 }
221
222 /// Flushes any unwritten bits to the stream
223 pub fn flush(self: *Self) Error!void {
224 if (packing == .Bit) return self.out_stream.flushBits();
225 }
226
227 fn serializeInt(self: *Self, value: var) Error!void {
228 const T = @TypeOf(value);
229 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
230
231 const t_bit_count = comptime meta.bitCount(T);
232 const u8_bit_count = comptime meta.bitCount(u8);
233
234 const U = std.meta.IntType(false, t_bit_count);
235 const Log2U = math.Log2Int(U);
236 const int_size = (U.bit_count + 7) / 8;
237
238 const u_value = @bitCast(U, value);
239
240 if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
241
242 var buffer: [int_size]u8 = undefined;
243 if (int_size == 1) buffer[0] = u_value;
244
245 for (buffer) |*byte, i| {
246 const idx = switch (endian) {
247 .Big => int_size - i - 1,
248 .Little => i,
249 };
250 const shift = @intCast(Log2U, idx * u8_bit_count);
251 const v = u_value >> shift;
252 byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
253 }
254
255 try self.out_stream.write(&buffer);
256 }
257
258 /// Serializes the passed value into the stream
259 pub fn serialize(self: *Self, value: var) Error!void {
260 const T = comptime @TypeOf(value);
261
262 if (comptime trait.isIndexable(T)) {
263 for (value) |v|
264 try self.serialize(v);
265 return;
266 }
267
268 //custom serializer: fn(self: Self, serializer: var) !void
269 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
270
271 if (comptime trait.isPacked(T) and packing != .Bit) {
272 var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
273 try packed_serializer.serialize(value);
274 try packed_serializer.flush();
275 return;
276 }
277
278 switch (@typeInfo(T)) {
279 .Void => return,
280 .Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
281 .Float, .Int => try self.serializeInt(value),
282 .Struct => {
283 const info = @typeInfo(T);
284
285 inline for (info.Struct.fields) |*field_info| {
286 const name = field_info.name;
287 const FieldType = field_info.field_type;
288
289 if (FieldType == void or FieldType == u0) continue;
290
291 //It doesn't make sense to write pointers
292 if (comptime trait.is(.Pointer)(FieldType)) {
293 @compileError("Will not " ++ "serialize field " ++ name ++
294 " of struct " ++ @typeName(T) ++ " because it " ++
295 "is of pointer-type " ++ @typeName(FieldType) ++ ".");
296 }
297 try self.serialize(@field(value, name));
298 }
299 },
300 .Union => {
301 const info = @typeInfo(T).Union;
302 if (info.tag_type) |TagType| {
303 const active_tag = meta.activeTag(value);
304 try self.serialize(active_tag);
305 //This inline loop is necessary because active_tag is a runtime
306 // value, but @field requires a comptime value. Our alternative
307 // is to check each field for a match
308 inline for (info.fields) |field_info| {
309 if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
310 const name = field_info.name;
311 const FieldType = field_info.field_type;
312 try self.serialize(@field(value, name));
313 return;
314 }
315 }
316 unreachable;
317 }
318 @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
319 " because it is an untagged union. Use a custom serialize().");
320 },
321 .Optional => {
322 if (value == null) {
323 try self.serializeInt(@as(u1, @boolToInt(false)));
324 return;
325 }
326 try self.serializeInt(@as(u1, @boolToInt(true)));
327
328 const OC = comptime meta.Child(T);
329 const val_ptr = &value.?;
330 try self.serialize(val_ptr.*);
331 },
332 .Enum => {
333 try self.serializeInt(@enumToInt(value));
334 },
335 else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
336 }
337 }
338 };
339}
340
341pub fn serializer(
342 comptime endian: builtin.Endian,
343 comptime packing: Packing,
344 out_stream: var,
345) Serializer(endian, packing, @TypeOf(out_stream)) {
346 return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
347}
348
349fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
350 @setEvalBranchQuota(1500);
351 //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
352 const max_test_bitsize = 128;
353
354 const total_bytes = comptime blk: {
355 var bytes = 0;
356 comptime var i = 0;
357 while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
358 break :blk bytes * 2;
359 };
360
361 var data_mem: [total_bytes]u8 = undefined;
362 var out = io.fixedBufferStream(&data_mem);
363 var serializer = serializer(endian, packing, out.outStream());
364
365 var in = io.fixedBufferStream(&data_mem);
366 var deserializer = Deserializer(endian, packing, in.inStream());
367
368 comptime var i = 0;
369 inline while (i <= max_test_bitsize) : (i += 1) {
370 const U = std.meta.IntType(false, i);
371 const S = std.meta.IntType(true, i);
372 try serializer.serializeInt(@as(U, i));
373 if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0));
374 }
375 try serializer.flush();
376
377 i = 0;
378 inline while (i <= max_test_bitsize) : (i += 1) {
379 const U = std.meta.IntType(false, i);
380 const S = std.meta.IntType(true, i);
381 const x = try deserializer.deserializeInt(U);
382 const y = try deserializer.deserializeInt(S);
383 expect(x == @as(U, i));
384 if (i != 0) expect(y == @as(S, -1)) else expect(y == 0);
385 }
386
387 const u8_bit_count = comptime meta.bitCount(u8);
388 //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
389 //and we have each for unsigned and signed, so * 2
390 const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
391 const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
392 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
393
394 expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
395
396 //Verify that empty error set works with serializer.
397 //deserializer is covered by FixedBufferStream
398 var null_serializer = io.serializer(endian, packing, std.io.null_out_stream);
399 try null_serializer.serialize(data_mem[0..]);
400 try null_serializer.flush();
401}
402
403test "Serializer/Deserializer Int" {
404 try testIntSerializerDeserializer(.Big, .Byte);
405 try testIntSerializerDeserializer(.Little, .Byte);
406 // TODO these tests are disabled due to tripping an LLVM assertion
407 // https://github.com/ziglang/zig/issues/2019
408 //try testIntSerializerDeserializer(builtin.Endian.Big, true);
409 //try testIntSerializerDeserializer(builtin.Endian.Little, true);
410}
411
412fn testIntSerializerDeserializerInfNaN(
413 comptime endian: builtin.Endian,
414 comptime packing: io.Packing,
415) !void {
416 const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
417 var data_mem: [mem_size]u8 = undefined;
418
419 var out = io.fixedBufferStream(&data_mem);
420 var serializer = serializer(endian, packing, out.outStream());
421
422 var in = io.fixedBufferStream(&data_mem);
423 var deserializer = deserializer(endian, packing, in.inStream());
424
425 //@TODO: isInf/isNan not currently implemented for f128.
426 try serializer.serialize(std.math.nan(f16));
427 try serializer.serialize(std.math.inf(f16));
428 try serializer.serialize(std.math.nan(f32));
429 try serializer.serialize(std.math.inf(f32));
430 try serializer.serialize(std.math.nan(f64));
431 try serializer.serialize(std.math.inf(f64));
432 //try serializer.serialize(std.math.nan(f128));
433 //try serializer.serialize(std.math.inf(f128));
434 const nan_check_f16 = try deserializer.deserialize(f16);
435 const inf_check_f16 = try deserializer.deserialize(f16);
436 const nan_check_f32 = try deserializer.deserialize(f32);
437 deserializer.alignToByte();
438 const inf_check_f32 = try deserializer.deserialize(f32);
439 const nan_check_f64 = try deserializer.deserialize(f64);
440 const inf_check_f64 = try deserializer.deserialize(f64);
441 //const nan_check_f128 = try deserializer.deserialize(f128);
442 //const inf_check_f128 = try deserializer.deserialize(f128);
443 expect(std.math.isNan(nan_check_f16));
444 expect(std.math.isInf(inf_check_f16));
445 expect(std.math.isNan(nan_check_f32));
446 expect(std.math.isInf(inf_check_f32));
447 expect(std.math.isNan(nan_check_f64));
448 expect(std.math.isInf(inf_check_f64));
449 //expect(std.math.isNan(nan_check_f128));
450 //expect(std.math.isInf(inf_check_f128));
451}
452
453test "Serializer/Deserializer Int: Inf/NaN" {
454 try testIntSerializerDeserializerInfNaN(.Big, .Byte);
455 try testIntSerializerDeserializerInfNaN(.Little, .Byte);
456 try testIntSerializerDeserializerInfNaN(.Big, .Bit);
457 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
458}
459
460fn testAlternateSerializer(self: var, serializer: var) !void {
461 try serializer.serialize(self.f_f16);
462}
463
464fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
465 const ColorType = enum(u4) {
466 RGB8 = 1,
467 RA16 = 2,
468 R32 = 3,
469 };
470
471 const TagAlign = union(enum(u32)) {
472 A: u8,
473 B: u8,
474 C: u8,
475 };
476
477 const Color = union(ColorType) {
478 RGB8: struct {
479 r: u8,
480 g: u8,
481 b: u8,
482 a: u8,
483 },
484 RA16: struct {
485 r: u16,
486 a: u16,
487 },
488 R32: u32,
489 };
490
491 const PackedStruct = packed struct {
492 f_i3: i3,
493 f_u2: u2,
494 };
495
496 //to test custom serialization
497 const Custom = struct {
498 f_f16: f16,
499 f_unused_u32: u32,
500
501 pub fn deserialize(self: *@This(), deserializer: var) !void {
502 try deserializer.deserializeInto(&self.f_f16);
503 self.f_unused_u32 = 47;
504 }
505
506 pub const serialize = testAlternateSerializer;
507 };
508
509 const MyStruct = struct {
510 f_i3: i3,
511 f_u8: u8,
512 f_tag_align: TagAlign,
513 f_u24: u24,
514 f_i19: i19,
515 f_void: void,
516 f_f32: f32,
517 f_f128: f128,
518 f_packed_0: PackedStruct,
519 f_i7arr: [10]i7,
520 f_of64n: ?f64,
521 f_of64v: ?f64,
522 f_color_type: ColorType,
523 f_packed_1: PackedStruct,
524 f_custom: Custom,
525 f_color: Color,
526 };
527
528 const my_inst = MyStruct{
529 .f_i3 = -1,
530 .f_u8 = 8,
531 .f_tag_align = TagAlign{ .B = 148 },
532 .f_u24 = 24,
533 .f_i19 = 19,
534 .f_void = {},
535 .f_f32 = 32.32,
536 .f_f128 = 128.128,
537 .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
538 .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
539 .f_of64n = null,
540 .f_of64v = 64.64,
541 .f_color_type = ColorType.R32,
542 .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
543 .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
544 .f_color = Color{ .R32 = 123822 },
545 };
546
547 var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
548 var out = io.fixedBufferStream(&data_mem);
549 var serializer = serializer(endian, packing, out.outStream());
550
551 var in = io.fixedBufferStream(&data_mem);
552 var deserializer = deserializer(endian, packing, in.inStream());
553
554 try serializer.serialize(my_inst);
555
556 const my_copy = try deserializer.deserialize(MyStruct);
557 expect(meta.eql(my_copy, my_inst));
558}
559
560test "Serializer/Deserializer generic" {
561 if (std.Target.current.os.tag == .windows) {
562 // TODO https://github.com/ziglang/zig/issues/508
563 return error.SkipZigTest;
564 }
565 try testSerializerDeserializer(builtin.Endian.Big, .Byte);
566 try testSerializerDeserializer(builtin.Endian.Little, .Byte);
567 try testSerializerDeserializer(builtin.Endian.Big, .Bit);
568 try testSerializerDeserializer(builtin.Endian.Little, .Bit);
569}
570
571fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
572 const E = enum(u14) {
573 One = 1,
574 Two = 2,
575 };
576
577 const A = struct {
578 e: E,
579 };
580
581 const C = union(E) {
582 One: u14,
583 Two: f16,
584 };
585
586 var data_mem: [4]u8 = undefined;
587 var out = io.fixedBufferStream.init(&data_mem);
588 var serializer = serializer(endian, packing, out.outStream());
589
590 var in = io.fixedBufferStream(&data_mem);
591 var deserializer = deserializer(endian, packing, in.inStream());
592
593 try serializer.serialize(@as(u14, 3));
594 expectError(error.InvalidEnumTag, deserializer.deserialize(A));
595 out.pos = 0;
596 try serializer.serialize(@as(u14, 3));
597 try serializer.serialize(@as(u14, 88));
598 expectError(error.InvalidEnumTag, deserializer.deserialize(C));
599}
600
601test "Deserializer bad data" {
602 try testBadData(.Big, .Byte);
603 try testBadData(.Little, .Byte);
604 try testBadData(.Big, .Bit);
605 try testBadData(.Little, .Bit);
606}
lib/std/io/stream_source.zig created+90
......@@ -0,0 +1,90 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4
5/// Provides `io.InStream`, `io.OutStream`, and `io.SeekableStream` for in-memory buffers as
6/// well as files.
7/// For memory sources, if the supplied byte buffer is const, then `io.OutStream` is not available.
8/// The error set of the stream functions is the error set of the corresponding file functions.
9pub const StreamSource = union(enum) {
10 buffer: io.FixedBufferStream([]u8),
11 const_buffer: io.FixedBufferStream([]const u8),
12 file: std.fs.File,
13
14 pub const ReadError = std.fs.File.ReadError;
15 pub const WriteError = std.fs.File.WriteError;
16 pub const SeekError = std.fs.File.SeekError;
17 pub const GetSeekPosError = std.fs.File.GetPosError;
18
19 pub const InStream = io.InStream(*StreamSource, ReadError, read);
20 pub const OutStream = io.OutStream(*StreamSource, WriteError, write);
21 pub const SeekableStream = io.SeekableStream(
22 *StreamSource,
23 SeekError,
24 GetSeekPosError,
25 seekTo,
26 seekBy,
27 getPos,
28 getEndPos,
29 );
30
31 pub fn read(self: *StreamSource, dest: []u8) ReadError!usize {
32 switch (self.*) {
33 .buffer => |*x| return x.read(dest),
34 .const_buffer => |*x| return x.read(dest),
35 .file => |x| return x.read(dest),
36 }
37 }
38
39 pub fn write(self: *StreamSource, bytes: []const u8) WriteError!usize {
40 switch (self.*) {
41 .buffer => |*x| return x.write(bytes),
42 .const_buffer => |*x| return x.write(bytes),
43 .file => |x| return x.write(bytes),
44 }
45 }
46
47 pub fn seekTo(self: *StreamSource, pos: u64) SeekError!void {
48 switch (self.*) {
49 .buffer => |*x| return x.seekTo(pos),
50 .const_buffer => |*x| return x.seekTo(pos),
51 .file => |x| return x.seekTo(pos),
52 }
53 }
54
55 pub fn seekBy(self: *StreamSource, amt: i64) SeekError!void {
56 switch (self.*) {
57 .buffer => |*x| return x.seekBy(amt),
58 .const_buffer => |*x| return x.seekBy(amt),
59 .file => |x| return x.seekBy(amt),
60 }
61 }
62
63 pub fn getEndPos(self: *StreamSource) GetSeekPosError!u64 {
64 switch (self.*) {
65 .buffer => |*x| return x.getEndPos(),
66 .const_buffer => |*x| return x.getEndPos(),
67 .file => |x| return x.getEndPos(),
68 }
69 }
70
71 pub fn getPos(self: *StreamSource) GetSeekPosError!u64 {
72 switch (self.*) {
73 .buffer => |*x| return x.getPos(),
74 .const_buffer => |*x| return x.getPos(),
75 .file => |x| return x.getPos(),
76 }
77 }
78
79 pub fn inStream(self: *StreamSource) InStream {
80 return .{ .context = self };
81 }
82
83 pub fn outStream(self: *StreamSource) OutStream {
84 return .{ .context = self };
85 }
86
87 pub fn seekableStream(self: *StreamSource) SeekableStream {
88 return .{ .context = self };
89 }
90};
lib/std/io/test.zig+13-521
......@@ -22,11 +22,10 @@ test "write a file, read it, then delete it" {
2222 var file = try cwd.createFile(tmp_file_name, .{});
2323 defer file.close();
2424
25 var file_out_stream = file.outStream();
26 var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream);
27 const st = &buf_stream.stream;
25 var buf_stream = io.bufferedOutStream(file.outStream());
26 const st = buf_stream.outStream();
2827 try st.print("begin", .{});
29 try st.write(data[0..]);
28 try st.writeAll(data[0..]);
3029 try st.print("end", .{});
3130 try buf_stream.flush();
3231 }
......@@ -48,9 +47,8 @@ test "write a file, read it, then delete it" {
4847 const expected_file_size: u64 = "begin".len + data.len + "end".len;
4948 expectEqual(expected_file_size, file_size);
5049
51 var file_in_stream = file.inStream();
52 var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);
53 const st = &buf_stream.stream;
50 var buf_stream = io.bufferedInStream(file.inStream());
51 const st = buf_stream.inStream();
5452 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
5553 defer std.testing.allocator.free(contents);
5654
......@@ -61,224 +59,13 @@ test "write a file, read it, then delete it" {
6159 try cwd.deleteFile(tmp_file_name);
6260}
6361
64test "BufferOutStream" {
65 var buffer = try std.Buffer.initSize(std.testing.allocator, 0);
66 defer buffer.deinit();
67 var buf_stream = &std.io.BufferOutStream.init(&buffer).stream;
68
69 const x: i32 = 42;
70 const y: i32 = 1234;
71 try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
72
73 expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
74}
75
76test "SliceInStream" {
77 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
78 var ss = io.SliceInStream.init(&bytes);
79
80 var dest: [4]u8 = undefined;
81
82 var read = try ss.stream.read(dest[0..4]);
83 expect(read == 4);
84 expect(mem.eql(u8, dest[0..4], bytes[0..4]));
85
86 read = try ss.stream.read(dest[0..4]);
87 expect(read == 3);
88 expect(mem.eql(u8, dest[0..3], bytes[4..7]));
89
90 read = try ss.stream.read(dest[0..4]);
91 expect(read == 0);
92}
93
94test "PeekStream" {
95 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
96 var ss = io.SliceInStream.init(&bytes);
97 var ps = io.PeekStream(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream);
98
99 var dest: [4]u8 = undefined;
100
101 try ps.putBackByte(9);
102 try ps.putBackByte(10);
103
104 var read = try ps.stream.read(dest[0..4]);
105 expect(read == 4);
106 expect(dest[0] == 10);
107 expect(dest[1] == 9);
108 expect(mem.eql(u8, dest[2..4], bytes[0..2]));
109
110 read = try ps.stream.read(dest[0..4]);
111 expect(read == 4);
112 expect(mem.eql(u8, dest[0..4], bytes[2..6]));
113
114 read = try ps.stream.read(dest[0..4]);
115 expect(read == 2);
116 expect(mem.eql(u8, dest[0..2], bytes[6..8]));
117
118 try ps.putBackByte(11);
119 try ps.putBackByte(12);
120
121 read = try ps.stream.read(dest[0..4]);
122 expect(read == 2);
123 expect(dest[0] == 12);
124 expect(dest[1] == 11);
125}
126
127test "SliceOutStream" {
128 var buffer: [10]u8 = undefined;
129 var ss = io.SliceOutStream.init(buffer[0..]);
130
131 try ss.stream.write("Hello");
132 expect(mem.eql(u8, ss.getWritten(), "Hello"));
133
134 try ss.stream.write("world");
135 expect(mem.eql(u8, ss.getWritten(), "Helloworld"));
136
137 expectError(error.OutOfMemory, ss.stream.write("!"));
138 expect(mem.eql(u8, ss.getWritten(), "Helloworld"));
139
140 ss.reset();
141 expect(ss.getWritten().len == 0);
142
143 expectError(error.OutOfMemory, ss.stream.write("Hello world!"));
144 expect(mem.eql(u8, ss.getWritten(), "Hello worl"));
145}
146
147test "BitInStream" {
148 const mem_be = [_]u8{ 0b11001101, 0b00001011 };
149 const mem_le = [_]u8{ 0b00011101, 0b10010101 };
150
151 var mem_in_be = io.SliceInStream.init(mem_be[0..]);
152 const InError = io.SliceInStream.Error;
153 var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream);
154
155 var out_bits: usize = undefined;
156
157 expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
158 expect(out_bits == 1);
159 expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
160 expect(out_bits == 2);
161 expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
162 expect(out_bits == 3);
163 expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
164 expect(out_bits == 4);
165 expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
166 expect(out_bits == 5);
167 expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
168 expect(out_bits == 1);
169
170 mem_in_be.pos = 0;
171 bit_stream_be.bit_count = 0;
172 expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
173 expect(out_bits == 15);
174
175 mem_in_be.pos = 0;
176 bit_stream_be.bit_count = 0;
177 expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
178 expect(out_bits == 16);
179
180 _ = try bit_stream_be.readBits(u0, 0, &out_bits);
181
182 expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
183 expect(out_bits == 0);
184 expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1));
185
186 var mem_in_le = io.SliceInStream.init(mem_le[0..]);
187 var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream);
188
189 expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
190 expect(out_bits == 1);
191 expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
192 expect(out_bits == 2);
193 expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
194 expect(out_bits == 3);
195 expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
196 expect(out_bits == 4);
197 expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
198 expect(out_bits == 5);
199 expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
200 expect(out_bits == 1);
201
202 mem_in_le.pos = 0;
203 bit_stream_le.bit_count = 0;
204 expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
205 expect(out_bits == 15);
206
207 mem_in_le.pos = 0;
208 bit_stream_le.bit_count = 0;
209 expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
210 expect(out_bits == 16);
211
212 _ = try bit_stream_le.readBits(u0, 0, &out_bits);
213
214 expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
215 expect(out_bits == 0);
216 expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1));
217}
218
219test "BitOutStream" {
220 var mem_be = [_]u8{0} ** 2;
221 var mem_le = [_]u8{0} ** 2;
222
223 var mem_out_be = io.SliceOutStream.init(mem_be[0..]);
224 const OutError = io.SliceOutStream.Error;
225 var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream);
226
227 try bit_stream_be.writeBits(@as(u2, 1), 1);
228 try bit_stream_be.writeBits(@as(u5, 2), 2);
229 try bit_stream_be.writeBits(@as(u128, 3), 3);
230 try bit_stream_be.writeBits(@as(u8, 4), 4);
231 try bit_stream_be.writeBits(@as(u9, 5), 5);
232 try bit_stream_be.writeBits(@as(u1, 1), 1);
233
234 expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
235
236 mem_out_be.pos = 0;
237
238 try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15);
239 try bit_stream_be.flushBits();
240 expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
241
242 mem_out_be.pos = 0;
243 try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16);
244 expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
245
246 try bit_stream_be.writeBits(@as(u0, 0), 0);
247
248 var mem_out_le = io.SliceOutStream.init(mem_le[0..]);
249 var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream);
250
251 try bit_stream_le.writeBits(@as(u2, 1), 1);
252 try bit_stream_le.writeBits(@as(u5, 2), 2);
253 try bit_stream_le.writeBits(@as(u128, 3), 3);
254 try bit_stream_le.writeBits(@as(u8, 4), 4);
255 try bit_stream_le.writeBits(@as(u9, 5), 5);
256 try bit_stream_le.writeBits(@as(u1, 1), 1);
257
258 expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
259
260 mem_out_le.pos = 0;
261 try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15);
262 try bit_stream_le.flushBits();
263 expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
264
265 mem_out_le.pos = 0;
266 try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16);
267 expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
268
269 try bit_stream_le.writeBits(@as(u0, 0), 0);
270}
271
27262test "BitStreams with File Stream" {
27363 const tmp_file_name = "temp_test_file.txt";
27464 {
27565 var file = try fs.cwd().createFile(tmp_file_name, .{});
27666 defer file.close();
27767
278 var file_out = 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);
68 var bit_stream = io.bitOutStream(builtin.endian, file.outStream());
28269
28370 try bit_stream.writeBits(@as(u2, 1), 1);
28471 try bit_stream.writeBits(@as(u5, 2), 2);
......@@ -292,10 +79,7 @@ test "BitStreams with File Stream" {
29279 var file = try fs.cwd().openFile(tmp_file_name, .{});
29380 defer file.close();
29481
295 var file_in = 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);
82 var bit_stream = io.bitInStream(builtin.endian, file.inStream());
29983
30084 var out_bits: usize = undefined;
30185
......@@ -317,298 +101,6 @@ test "BitStreams with File Stream" {
317101 try fs.cwd().deleteFile(tmp_file_name);
318102}
319103
320fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
321 @setEvalBranchQuota(1500);
322 //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
323 const max_test_bitsize = 128;
324
325 const total_bytes = comptime blk: {
326 var bytes = 0;
327 comptime var i = 0;
328 while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
329 break :blk bytes * 2;
330 };
331
332 var data_mem: [total_bytes]u8 = undefined;
333 var out = io.SliceOutStream.init(data_mem[0..]);
334 const OutError = io.SliceOutStream.Error;
335 var out_stream = &out.stream;
336 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
337
338 var in = io.SliceInStream.init(data_mem[0..]);
339 const InError = io.SliceInStream.Error;
340 var in_stream = &in.stream;
341 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
342
343 comptime var i = 0;
344 inline while (i <= max_test_bitsize) : (i += 1) {
345 const U = std.meta.IntType(false, i);
346 const S = std.meta.IntType(true, i);
347 try serializer.serializeInt(@as(U, i));
348 if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0));
349 }
350 try serializer.flush();
351
352 i = 0;
353 inline while (i <= max_test_bitsize) : (i += 1) {
354 const U = std.meta.IntType(false, i);
355 const S = std.meta.IntType(true, i);
356 const x = try deserializer.deserializeInt(U);
357 const y = try deserializer.deserializeInt(S);
358 expect(x == @as(U, i));
359 if (i != 0) expect(y == @as(S, -1)) else expect(y == 0);
360 }
361
362 const u8_bit_count = comptime meta.bitCount(u8);
363 //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
364 //and we have each for unsigned and signed, so * 2
365 const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
366 const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
367 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
368
369 expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
370
371 //Verify that empty error set works with serializer.
372 //deserializer is covered by SliceInStream
373 const NullError = io.NullOutStream.Error;
374 var null_out = io.NullOutStream.init();
375 var null_out_stream = &null_out.stream;
376 var null_serializer = io.Serializer(endian, packing, NullError).init(null_out_stream);
377 try null_serializer.serialize(data_mem[0..]);
378 try null_serializer.flush();
379}
380
381test "Serializer/Deserializer Int" {
382 try testIntSerializerDeserializer(.Big, .Byte);
383 try testIntSerializerDeserializer(.Little, .Byte);
384 // TODO these tests are disabled due to tripping an LLVM assertion
385 // https://github.com/ziglang/zig/issues/2019
386 //try testIntSerializerDeserializer(builtin.Endian.Big, true);
387 //try testIntSerializerDeserializer(builtin.Endian.Little, true);
388}
389
390fn testIntSerializerDeserializerInfNaN(
391 comptime endian: builtin.Endian,
392 comptime packing: io.Packing,
393) !void {
394 const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
395 var data_mem: [mem_size]u8 = undefined;
396
397 var out = io.SliceOutStream.init(data_mem[0..]);
398 const OutError = io.SliceOutStream.Error;
399 var out_stream = &out.stream;
400 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
401
402 var in = io.SliceInStream.init(data_mem[0..]);
403 const InError = io.SliceInStream.Error;
404 var in_stream = &in.stream;
405 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
406
407 //@TODO: isInf/isNan not currently implemented for f128.
408 try serializer.serialize(std.math.nan(f16));
409 try serializer.serialize(std.math.inf(f16));
410 try serializer.serialize(std.math.nan(f32));
411 try serializer.serialize(std.math.inf(f32));
412 try serializer.serialize(std.math.nan(f64));
413 try serializer.serialize(std.math.inf(f64));
414 //try serializer.serialize(std.math.nan(f128));
415 //try serializer.serialize(std.math.inf(f128));
416 const nan_check_f16 = try deserializer.deserialize(f16);
417 const inf_check_f16 = try deserializer.deserialize(f16);
418 const nan_check_f32 = try deserializer.deserialize(f32);
419 deserializer.alignToByte();
420 const inf_check_f32 = try deserializer.deserialize(f32);
421 const nan_check_f64 = try deserializer.deserialize(f64);
422 const inf_check_f64 = try deserializer.deserialize(f64);
423 //const nan_check_f128 = try deserializer.deserialize(f128);
424 //const inf_check_f128 = try deserializer.deserialize(f128);
425 expect(std.math.isNan(nan_check_f16));
426 expect(std.math.isInf(inf_check_f16));
427 expect(std.math.isNan(nan_check_f32));
428 expect(std.math.isInf(inf_check_f32));
429 expect(std.math.isNan(nan_check_f64));
430 expect(std.math.isInf(inf_check_f64));
431 //expect(std.math.isNan(nan_check_f128));
432 //expect(std.math.isInf(inf_check_f128));
433}
434
435test "Serializer/Deserializer Int: Inf/NaN" {
436 try testIntSerializerDeserializerInfNaN(.Big, .Byte);
437 try testIntSerializerDeserializerInfNaN(.Little, .Byte);
438 try testIntSerializerDeserializerInfNaN(.Big, .Bit);
439 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
440}
441
442fn testAlternateSerializer(self: var, serializer: var) !void {
443 try serializer.serialize(self.f_f16);
444}
445
446fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
447 const ColorType = enum(u4) {
448 RGB8 = 1,
449 RA16 = 2,
450 R32 = 3,
451 };
452
453 const TagAlign = union(enum(u32)) {
454 A: u8,
455 B: u8,
456 C: u8,
457 };
458
459 const Color = union(ColorType) {
460 RGB8: struct {
461 r: u8,
462 g: u8,
463 b: u8,
464 a: u8,
465 },
466 RA16: struct {
467 r: u16,
468 a: u16,
469 },
470 R32: u32,
471 };
472
473 const PackedStruct = packed struct {
474 f_i3: i3,
475 f_u2: u2,
476 };
477
478 //to test custom serialization
479 const Custom = struct {
480 f_f16: f16,
481 f_unused_u32: u32,
482
483 pub fn deserialize(self: *@This(), deserializer: var) !void {
484 try deserializer.deserializeInto(&self.f_f16);
485 self.f_unused_u32 = 47;
486 }
487
488 pub const serialize = testAlternateSerializer;
489 };
490
491 const MyStruct = struct {
492 f_i3: i3,
493 f_u8: u8,
494 f_tag_align: TagAlign,
495 f_u24: u24,
496 f_i19: i19,
497 f_void: void,
498 f_f32: f32,
499 f_f128: f128,
500 f_packed_0: PackedStruct,
501 f_i7arr: [10]i7,
502 f_of64n: ?f64,
503 f_of64v: ?f64,
504 f_color_type: ColorType,
505 f_packed_1: PackedStruct,
506 f_custom: Custom,
507 f_color: Color,
508 };
509
510 const my_inst = MyStruct{
511 .f_i3 = -1,
512 .f_u8 = 8,
513 .f_tag_align = TagAlign{ .B = 148 },
514 .f_u24 = 24,
515 .f_i19 = 19,
516 .f_void = {},
517 .f_f32 = 32.32,
518 .f_f128 = 128.128,
519 .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
520 .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
521 .f_of64n = null,
522 .f_of64v = 64.64,
523 .f_color_type = ColorType.R32,
524 .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
525 .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
526 .f_color = Color{ .R32 = 123822 },
527 };
528
529 var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
530 var out = io.SliceOutStream.init(data_mem[0..]);
531 const OutError = io.SliceOutStream.Error;
532 var out_stream = &out.stream;
533 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
534
535 var in = io.SliceInStream.init(data_mem[0..]);
536 const InError = io.SliceInStream.Error;
537 var in_stream = &in.stream;
538 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
539
540 try serializer.serialize(my_inst);
541
542 const my_copy = try deserializer.deserialize(MyStruct);
543 expect(meta.eql(my_copy, my_inst));
544}
545
546test "Serializer/Deserializer generic" {
547 if (std.Target.current.os.tag == .windows) {
548 // TODO https://github.com/ziglang/zig/issues/508
549 return error.SkipZigTest;
550 }
551 try testSerializerDeserializer(builtin.Endian.Big, .Byte);
552 try testSerializerDeserializer(builtin.Endian.Little, .Byte);
553 try testSerializerDeserializer(builtin.Endian.Big, .Bit);
554 try testSerializerDeserializer(builtin.Endian.Little, .Bit);
555}
556
557fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
558 const E = enum(u14) {
559 One = 1,
560 Two = 2,
561 };
562
563 const A = struct {
564 e: E,
565 };
566
567 const C = union(E) {
568 One: u14,
569 Two: f16,
570 };
571
572 var data_mem: [4]u8 = undefined;
573 var out = io.SliceOutStream.init(data_mem[0..]);
574 const OutError = io.SliceOutStream.Error;
575 var out_stream = &out.stream;
576 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
577
578 var in = io.SliceInStream.init(data_mem[0..]);
579 const InError = io.SliceInStream.Error;
580 var in_stream = &in.stream;
581 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
582
583 try serializer.serialize(@as(u14, 3));
584 expectError(error.InvalidEnumTag, deserializer.deserialize(A));
585 out.pos = 0;
586 try serializer.serialize(@as(u14, 3));
587 try serializer.serialize(@as(u14, 88));
588 expectError(error.InvalidEnumTag, deserializer.deserialize(C));
589}
590
591test "Deserializer bad data" {
592 try testBadData(.Big, .Byte);
593 try testBadData(.Little, .Byte);
594 try testBadData(.Big, .Bit);
595 try testBadData(.Little, .Bit);
596}
597
598test "c out stream" {
599 if (!builtin.link_libc) return error.SkipZigTest;
600
601 const filename = "tmp_io_test_file.txt";
602 const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
603 defer {
604 _ = std.c.fclose(out_file);
605 fs.cwd().deleteFileC(filename) catch {};
606 }
607
608 const out_stream = &io.COutStream.init(out_file).stream;
609 try out_stream.print("hi: {}\n", .{@as(i32, 123)});
610}
611
612104test "File seek ops" {
613105 const tmp_file_name = "temp_test_file.txt";
614106 var file = try fs.cwd().createFile(tmp_file_name, .{});
......@@ -621,16 +113,16 @@ test "File seek ops" {
621113
622114 // Seek to the end
623115 try file.seekFromEnd(0);
624 std.testing.expect((try file.getPos()) == try file.getEndPos());
116 expect((try file.getPos()) == try file.getEndPos());
625117 // Negative delta
626118 try file.seekBy(-4096);
627 std.testing.expect((try file.getPos()) == 4096);
119 expect((try file.getPos()) == 4096);
628120 // Positive delta
629121 try file.seekBy(10);
630 std.testing.expect((try file.getPos()) == 4106);
122 expect((try file.getPos()) == 4106);
631123 // Absolute position
632124 try file.seekTo(1234);
633 std.testing.expect((try file.getPos()) == 1234);
125 expect((try file.getPos()) == 1234);
634126}
635127
636128test "updateTimes" {
......@@ -647,6 +139,6 @@ test "updateTimes" {
647139 stat_old.mtime - 5 * std.time.ns_per_s,
648140 );
649141 var stat_new = try file.stat();
650 std.testing.expect(stat_new.atime < stat_old.atime);
651 std.testing.expect(stat_new.mtime < stat_old.mtime);
142 expect(stat_new.atime < stat_old.atime);
143 expect(stat_new.mtime < stat_old.mtime);
652144}
lib/std/json.zig+5-4
......@@ -10,6 +10,7 @@ const mem = std.mem;
1010const maxInt = std.math.maxInt;
1111
1212pub const WriteStream = @import("json/write_stream.zig").WriteStream;
13pub const writeStream = @import("json/write_stream.zig").writeStream;
1314
1415const StringEscapes = union(enum) {
1516 None,
......@@ -2107,9 +2108,9 @@ test "import more json tests" {
21072108test "write json then parse it" {
21082109 var out_buffer: [1000]u8 = undefined;
21092110
2110 var slice_out_stream = std.io.SliceOutStream.init(&out_buffer);
2111 const out_stream = &slice_out_stream.stream;
2112 var jw = WriteStream(@TypeOf(out_stream).Child, 4).init(out_stream);
2111 var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer);
2112 const out_stream = fixed_buffer_stream.outStream();
2113 var jw = writeStream(out_stream, 4);
21132114
21142115 try jw.beginObject();
21152116
......@@ -2140,7 +2141,7 @@ test "write json then parse it" {
21402141
21412142 var parser = Parser.init(testing.allocator, false);
21422143 defer parser.deinit();
2143 var tree = try parser.parse(slice_out_stream.getWritten());
2144 var tree = try parser.parse(fixed_buffer_stream.getWritten());
21442145 defer tree.deinit();
21452146
21462147 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 {
3030 /// The string used as spacing.
3131 space: []const u8 = " ",
3232
33 stream: *OutStream,
33 stream: OutStream,
3434 state_index: usize,
3535 state: [max_depth]State,
3636
37 pub fn init(stream: *OutStream) Self {
37 pub fn init(stream: OutStream) Self {
3838 var self = Self{
3939 .stream = stream,
4040 .state_index = 1,
......@@ -90,8 +90,8 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
9090 self.pushState(.Value);
9191 try self.indent();
9292 try self.writeEscapedString(name);
93 try self.stream.write(":");
94 try self.stream.write(self.space);
93 try self.stream.writeAll(":");
94 try self.stream.writeAll(self.space);
9595 },
9696 }
9797 }
......@@ -134,16 +134,16 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
134134
135135 pub fn emitNull(self: *Self) !void {
136136 assert(self.state[self.state_index] == State.Value);
137 try self.stream.write("null");
137 try self.stream.writeAll("null");
138138 self.popState();
139139 }
140140
141141 pub fn emitBool(self: *Self, value: bool) !void {
142142 assert(self.state[self.state_index] == State.Value);
143143 if (value) {
144 try self.stream.write("true");
144 try self.stream.writeAll("true");
145145 } else {
146 try self.stream.write("false");
146 try self.stream.writeAll("false");
147147 }
148148 self.popState();
149149 }
......@@ -188,13 +188,13 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
188188 try self.stream.writeByte('"');
189189 for (string) |s| {
190190 switch (s) {
191 '"' => try self.stream.write("\\\""),
192 '\t' => try self.stream.write("\\t"),
193 '\r' => try self.stream.write("\\r"),
194 '\n' => try self.stream.write("\\n"),
195 8 => try self.stream.write("\\b"),
196 12 => try self.stream.write("\\f"),
197 '\\' => try self.stream.write("\\\\"),
191 '"' => try self.stream.writeAll("\\\""),
192 '\t' => try self.stream.writeAll("\\t"),
193 '\r' => try self.stream.writeAll("\\r"),
194 '\n' => try self.stream.writeAll("\\n"),
195 8 => try self.stream.writeAll("\\b"),
196 12 => try self.stream.writeAll("\\f"),
197 '\\' => try self.stream.writeAll("\\\\"),
198198 else => try self.stream.writeByte(s),
199199 }
200200 }
......@@ -231,10 +231,10 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
231231
232232 fn indent(self: *Self) !void {
233233 assert(self.state_index >= 1);
234 try self.stream.write(self.newline);
234 try self.stream.writeAll(self.newline);
235235 var i: usize = 0;
236236 while (i < self.state_index - 1) : (i += 1) {
237 try self.stream.write(self.one_indent);
237 try self.stream.writeAll(self.one_indent);
238238 }
239239 }
240240
......@@ -249,15 +249,22 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
249249 };
250250}
251251
252pub fn writeStream(
253 out_stream: var,
254 comptime max_depth: usize,
255) WriteStream(@TypeOf(out_stream), max_depth) {
256 return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
257}
258
252259test "json write stream" {
253260 var out_buf: [1024]u8 = undefined;
254 var slice_stream = std.io.SliceOutStream.init(&out_buf);
255 const out = &slice_stream.stream;
261 var slice_stream = std.io.fixedBufferStream(&out_buf);
262 const out = slice_stream.outStream();
256263
257264 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
258265 defer arena_allocator.deinit();
259266
260 var w = std.json.WriteStream(@TypeOf(out).Child, 10).init(out);
267 var w = std.json.writeStream(out, 10);
261268 try w.emitJson(try getJson(&arena_allocator.allocator));
262269
263270 const result = slice_stream.getWritten();
lib/std/net.zig+2-2
......@@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts(
816816 };
817817 defer file.close();
818818
819 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
819 const stream = std.io.bufferedInStream(file.inStream()).inStream();
820820 var line_buf: [512]u8 = undefined;
821821 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
822822 error.StreamTooLong => blk: {
......@@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
10101010 };
10111011 defer file.close();
10121012
1013 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
1013 const stream = std.io.bufferedInStream(file.inStream()).inStream();
10141014 var line_buf: [512]u8 = undefined;
10151015 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
10161016 error.StreamTooLong => blk: {
lib/std/net/test.zig+1-1
......@@ -113,6 +113,6 @@ fn testClient(addr: net.Address) anyerror!void {
113113fn testServer(server: *net.StreamServer) anyerror!void {
114114 var client = try server.accept();
115115
116 const stream = &client.file.outStream().stream;
116 const stream = client.file.outStream();
117117 try stream.print("hello from server\n", .{});
118118}
lib/std/os.zig+1-1
......@@ -176,7 +176,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
176176 .io_mode = .blocking,
177177 .async_block_allowed = std.fs.File.async_block_allowed_yes,
178178 };
179 const stream = &file.inStream().stream;
179 const stream = file.inStream();
180180 stream.readNoEof(buf) catch return error.Unexpected;
181181}
182182
lib/std/os/test.zig+34-9
......@@ -95,15 +95,41 @@ test "sendfile" {
9595 },
9696 };
9797
98 var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;
98 var written_buf: [100]u8 = undefined;
9999 try dest_file.writeFileAll(src_file, .{
100100 .in_offset = 1,
101101 .in_len = 10,
102102 .headers_and_trailers = &hdtr,
103103 .header_count = 2,
104104 });
105 try dest_file.preadAll(&written_buf, 0);
106 expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
105 const amt = try dest_file.preadAll(&written_buf, 0);
106 expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
107}
108
109test "fs.copyFile" {
110 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
111 const src_file = "tmp_test_copy_file.txt";
112 const dest_file = "tmp_test_copy_file2.txt";
113 const dest_file2 = "tmp_test_copy_file3.txt";
114
115 try fs.cwd().writeFile(src_file, data);
116 defer fs.cwd().deleteFile(src_file) catch {};
117
118 try fs.copyFile(src_file, dest_file);
119 defer fs.cwd().deleteFile(dest_file) catch {};
120
121 try fs.copyFileMode(src_file, dest_file2, File.default_mode);
122 defer fs.cwd().deleteFile(dest_file2) catch {};
123
124 try expectFileContents(dest_file, data);
125 try expectFileContents(dest_file2, data);
126}
127
128fn expectFileContents(file_path: []const u8, data: []const u8) !void {
129 const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000);
130 defer testing.allocator.free(contents);
131
132 testing.expectEqualSlices(u8, data, contents);
107133}
108134
109135test "std.Thread.getCurrentId" {
......@@ -354,8 +380,7 @@ test "mmap" {
354380 const file = try fs.cwd().createFile(test_out_file, .{});
355381 defer file.close();
356382
357 var out_stream = file.outStream();
358 const stream = &out_stream.stream;
383 const stream = file.outStream();
359384
360385 var i: u32 = 0;
361386 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
......@@ -378,8 +403,8 @@ test "mmap" {
378403 );
379404 defer os.munmap(data);
380405
381 var mem_stream = io.SliceInStream.init(data);
382 const stream = &mem_stream.stream;
406 var mem_stream = io.fixedBufferStream(data);
407 const stream = mem_stream.inStream();
383408
384409 var i: u32 = 0;
385410 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
......@@ -402,8 +427,8 @@ test "mmap" {
402427 );
403428 defer os.munmap(data);
404429
405 var mem_stream = io.SliceInStream.init(data);
406 const stream = &mem_stream.stream;
430 var mem_stream = io.fixedBufferStream(data);
431 const stream = mem_stream.inStream();
407432
408433 var i: u32 = alloc_size / 2 / @sizeOf(u32);
409434 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
407407 switch (kernel32.GetLastError()) {
408408 .OPERATION_ABORTED => continue,
409409 .BROKEN_PIPE => return index,
410 .HANDLE_EOF => return index,
410411 else => |err| return unexpectedError(err),
411412 }
412413 }
lib/std/pdb.zig+8-16
......@@ -495,8 +495,7 @@ const Msf = struct {
495495 streams: []MsfStream,
496496
497497 fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void {
498 var file_stream = file.inStream();
499 const in = &file_stream.stream;
498 const in = file.inStream();
500499
501500 const superblock = try in.readStruct(SuperBlock);
502501
......@@ -529,7 +528,7 @@ const Msf = struct {
529528 );
530529
531530 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);
533532 const stream_sizes = try allocator.alloc(u32, stream_count);
534533 defer allocator.free(stream_sizes);
535534
......@@ -538,7 +537,7 @@ const Msf = struct {
538537 // and must be taken into account when resolving stream indices.
539538 const Nil = 0xFFFFFFFF;
540539 for (stream_sizes) |*s, i| {
541 const size = try self.directory.stream.readIntLittle(u32);
540 const size = try self.directory.inStream().readIntLittle(u32);
542541 s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize);
543542 }
544543
......@@ -553,7 +552,7 @@ const Msf = struct {
553552 var blocks = try allocator.alloc(u32, size);
554553 var j: u32 = 0;
555554 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);
557556 const n = (block_id % superblock.BlockSize);
558557 // 0 is for SuperBlock, 1 and 2 for FPMs.
559558 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 {
632631 blocks: []u32 = undefined,
633632 block_size: u32 = undefined,
634633
635 /// Implementation of InStream trait for Pdb.MsfStream
636 stream: Stream = undefined,
637
638634 pub const Error = @TypeOf(read).ReturnType.ErrorSet;
639 pub const Stream = io.InStream(Error);
640635
641636 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
642637 const stream = MsfStream{
......@@ -644,7 +639,6 @@ const MsfStream = struct {
644639 .pos = 0,
645640 .blocks = blocks,
646641 .block_size = block_size,
647 .stream = Stream{ .readFn = readFn },
648642 };
649643
650644 return stream;
......@@ -653,7 +647,7 @@ const MsfStream = struct {
653647 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
654648 var list = ArrayList(u8).init(allocator);
655649 while (true) {
656 const byte = try self.stream.readByte();
650 const byte = try self.inStream().readByte();
657651 if (byte == 0) {
658652 return list.toSlice();
659653 }
......@@ -667,8 +661,7 @@ const MsfStream = struct {
667661 var offset = self.pos % self.block_size;
668662
669663 try self.in_file.seekTo(block * self.block_size + offset);
670 var file_stream = self.in_file.inStream();
671 const in = &file_stream.stream;
664 const in = self.in_file.inStream();
672665
673666 var size: usize = 0;
674667 var rem_buffer = buffer;
......@@ -715,8 +708,7 @@ const MsfStream = struct {
715708 return block * self.block_size + offset;
716709 }
717710
718 fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
719 const self = @fieldParentPtr(MsfStream, "stream", in_stream);
720 return self.read(buffer);
711 fn inStream(self: *MsfStream) std.io.InStream(*MsfStream, Error, read) {
712 return .{ .context = self };
721713 }
722714};
lib/std/progress.zig+1-1
......@@ -177,7 +177,7 @@ pub const Progress = struct {
177177 pub fn log(self: *Progress, comptime format: []const u8, args: var) void {
178178 const file = self.terminal orelse return;
179179 self.refresh();
180 file.outStream().stream.print(format, args) catch {
180 file.outStream().print(format, args) catch {
181181 self.terminal = null;
182182 return;
183183 };
lib/std/special/build_runner.zig+4-4
......@@ -42,8 +42,8 @@ pub fn main() !void {
4242
4343 var targets = ArrayList([]const u8).init(allocator);
4444
45 const stderr_stream = &io.getStdErr().outStream().stream;
46 const stdout_stream = &io.getStdOut().outStream().stream;
45 const stderr_stream = io.getStdErr().outStream();
46 const stdout_stream = io.getStdOut().outStream();
4747
4848 while (nextArg(args, &arg_idx)) |arg| {
4949 if (mem.startsWith(u8, arg, "-D")) {
......@@ -159,7 +159,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
159159 try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
160160 }
161161
162 try out_stream.write(
162 try out_stream.writeAll(
163163 \\
164164 \\General Options:
165165 \\ --help Print this help and exit
......@@ -184,7 +184,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
184184 }
185185 }
186186
187 try out_stream.write(
187 try out_stream.writeAll(
188188 \\
189189 \\Advanced Options:
190190 \\ --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;
55pub const BufMap = @import("buf_map.zig").BufMap;
66pub const BufSet = @import("buf_set.zig").BufSet;
77pub const Buffer = @import("buffer.zig").Buffer;
8pub const BufferOutStream = @import("io.zig").BufferOutStream;
98pub const ChildProcess = @import("child_process.zig").ChildProcess;
109pub const DynLib = @import("dynamic_library.zig").DynLib;
1110pub const HashMap = @import("hash_map.zig").HashMap;
lib/std/zig/ast.zig+1-1
......@@ -375,7 +375,7 @@ pub const Error = union(enum) {
375375 token: TokenIndex,
376376
377377 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
378 return stream.write(msg);
378 return stream.writeAll(msg);
379379 }
380380 };
381381 }
lib/std/zig/parser_test.zig+5-6
......@@ -2809,7 +2809,7 @@ const maxInt = std.math.maxInt;
28092809var fixed_buffer_mem: [100 * 1024]u8 = undefined;
28102810
28112811fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
2812 const stderr = &io.getStdErr().outStream().stream;
2812 const stderr = io.getStdErr().outStream();
28132813
28142814 const tree = try std.zig.parse(allocator, source);
28152815 defer tree.deinit();
......@@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
28242824 {
28252825 var i: usize = 0;
28262826 while (i < loc.column) : (i += 1) {
2827 try stderr.write(" ");
2827 try stderr.writeAll(" ");
28282828 }
28292829 }
28302830 {
28312831 const caret_count = token.end - token.start;
28322832 var i: usize = 0;
28332833 while (i < caret_count) : (i += 1) {
2834 try stderr.write("~");
2834 try stderr.writeAll("~");
28352835 }
28362836 }
2837 try stderr.write("\n");
2837 try stderr.writeAll("\n");
28382838 }
28392839 if (tree.errors.len != 0) {
28402840 return error.ParseError;
......@@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
28432843 var buffer = try std.Buffer.initSize(allocator, 0);
28442844 errdefer buffer.deinit();
28452845
2846 var buffer_out_stream = io.BufferOutStream.init(&buffer);
2847 anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, tree);
2846 anything_changed.* = try std.zig.render(allocator, buffer.outStream(), tree);
28482847 return buffer.toOwnedSlice();
28492848}
28502849
lib/std/zig/render.zig+66-73
......@@ -12,64 +12,58 @@ pub const Error = error{
1212};
1313
1414/// Returns whether anything changed
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Child.Error || Error)!bool {
16 comptime assert(@typeInfo(@TypeOf(stream)) == .Pointer);
17
18 var anything_changed: bool = false;
19
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
2016 // make a passthrough stream that checks whether something changed
2117 const MyStream = struct {
2218 const MyStream = @This();
23 const StreamError = @TypeOf(stream).Child.Error;
24 const Stream = std.io.OutStream(StreamError);
19 const StreamError = @TypeOf(stream).Error;
2520
26 anything_changed_ptr: *bool,
2721 child_stream: @TypeOf(stream),
28 stream: Stream,
22 anything_changed: bool,
2923 source_index: usize,
3024 source: []const u8,
3125
32 fn write(iface_stream: *Stream, bytes: []const u8) StreamError!usize {
33 const self = @fieldParentPtr(MyStream, "stream", iface_stream);
34
35 if (!self.anything_changed_ptr.*) {
26 fn write(self: *MyStream, bytes: []const u8) StreamError!usize {
27 if (!self.anything_changed) {
3628 const end = self.source_index + bytes.len;
3729 if (end > self.source.len) {
38 self.anything_changed_ptr.* = true;
30 self.anything_changed = true;
3931 } else {
4032 const src_slice = self.source[self.source_index..end];
4133 self.source_index += bytes.len;
4234 if (!mem.eql(u8, bytes, src_slice)) {
43 self.anything_changed_ptr.* = true;
35 self.anything_changed = true;
4436 }
4537 }
4638 }
4739
48 return self.child_stream.writeOnce(bytes);
40 return self.child_stream.write(bytes);
4941 }
5042 };
5143 var my_stream = MyStream{
52 .stream = MyStream.Stream{ .writeFn = MyStream.write },
5344 .child_stream = stream,
54 .anything_changed_ptr = &anything_changed,
45 .anything_changed = false,
5546 .source_index = 0,
5647 .source = tree.source,
5748 };
49 const my_stream_stream: std.io.OutStream(*MyStream, MyStream.StreamError, MyStream.write) = .{
50 .context = &my_stream,
51 };
5852
59 try renderRoot(allocator, &my_stream.stream, tree);
53 try renderRoot(allocator, my_stream_stream, tree);
6054
61 if (!anything_changed and my_stream.source_index != my_stream.source.len) {
62 anything_changed = true;
55 if (my_stream.source_index != my_stream.source.len) {
56 my_stream.anything_changed = true;
6357 }
6458
65 return anything_changed;
59 return my_stream.anything_changed;
6660}
6761
6862fn renderRoot(
6963 allocator: *mem.Allocator,
7064 stream: var,
7165 tree: *ast.Tree,
72) (@TypeOf(stream).Child.Error || Error)!void {
66) (@TypeOf(stream).Error || Error)!void {
7367 var tok_it = tree.tokens.iterator(0);
7468
7569 // render all the line comments at the beginning of the file
......@@ -189,7 +183,7 @@ fn renderRoot(
189183 }
190184}
191185
192fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Child.Error!void {
186fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
193187 const first_token = node.firstToken();
194188 var prev_token = first_token;
195189 if (prev_token == 0) return;
......@@ -204,11 +198,11 @@ fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *as
204198 }
205199}
206200
207fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Child.Error || Error)!void {
201fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
208202 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
209203}
210204
211fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Child.Error || Error)!void {
205fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
212206 switch (decl.id) {
213207 .FnProto => {
214208 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
......@@ -343,7 +337,7 @@ fn renderExpression(
343337 start_col: *usize,
344338 base: *ast.Node,
345339 space: Space,
346) (@TypeOf(stream).Child.Error || Error)!void {
340) (@TypeOf(stream).Error || Error)!void {
347341 switch (base.id) {
348342 .Identifier => {
349343 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
......@@ -449,9 +443,9 @@ fn renderExpression(
449443 switch (op_tok_id) {
450444 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
451445 .LBracket => if (tree.tokens.at(prefix_op_node.op_token + 2).id == .Identifier)
452 try stream.write("[*c")
446 try stream.writeAll("[*c")
453447 else
454 try stream.write("[*"),
448 try stream.writeAll("[*"),
455449 else => unreachable,
456450 }
457451 if (ptr_info.sentinel) |sentinel| {
......@@ -757,7 +751,7 @@ fn renderExpression(
757751 while (it.next()) |field_init| {
758752 var find_stream = FindByteOutStream.init('\n');
759753 var dummy_col: usize = 0;
760 try renderExpression(allocator, &find_stream.stream, tree, 0, &dummy_col, field_init.*, Space.None);
754 try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init.*, Space.None);
761755 if (find_stream.byte_found) break :blk false;
762756 }
763757 break :blk true;
......@@ -909,8 +903,7 @@ fn renderExpression(
909903 var column_widths = widths[widths.len - row_size ..];
910904
911905 // Null stream for counting the printed length of each expression
912 var null_stream = std.io.NullOutStream.init();
913 var counting_stream = std.io.CountingOutStream(std.io.NullOutStream.Error).init(&null_stream.stream);
906 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
914907
915908 var it = exprs.iterator(0);
916909 var i: usize = 0;
......@@ -918,7 +911,7 @@ fn renderExpression(
918911 while (it.next()) |expr| : (i += 1) {
919912 counting_stream.bytes_written = 0;
920913 var dummy_col: usize = 0;
921 try renderExpression(allocator, &counting_stream.stream, tree, indent, &dummy_col, expr.*, Space.None);
914 try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr.*, Space.None);
922915 const width = @intCast(usize, counting_stream.bytes_written);
923916 const col = i % row_size;
924917 column_widths[col] = std.math.max(column_widths[col], width);
......@@ -1336,7 +1329,7 @@ fn renderExpression(
13361329
13371330 // TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348
13381331 if (mem.eql(u8, tree.tokenSlicePtr(tree.tokens.at(builtin_call.builtin_token)), "@typeOf")) {
1339 try stream.write("@TypeOf");
1332 try stream.writeAll("@TypeOf");
13401333 } else {
13411334 try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name
13421335 }
......@@ -1505,9 +1498,9 @@ fn renderExpression(
15051498 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
15061499 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
15071500 } else if (cc_rewrite_str) |str| {
1508 try stream.write("callconv(");
1509 try stream.write(mem.toSliceConst(u8, str));
1510 try stream.write(") ");
1501 try stream.writeAll("callconv(");
1502 try stream.writeAll(mem.toSliceConst(u8, str));
1503 try stream.writeAll(") ");
15111504 }
15121505
15131506 switch (fn_proto.return_type) {
......@@ -1997,11 +1990,11 @@ fn renderExpression(
19971990 .AsmInput => {
19981991 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
19991992
2000 try stream.write("[");
1993 try stream.writeAll("[");
20011994 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);
2002 try stream.write("] ");
1995 try stream.writeAll("] ");
20031996 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);
2004 try stream.write(" (");
1997 try stream.writeAll(" (");
20051998 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);
20061999 return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )
20072000 },
......@@ -2009,18 +2002,18 @@ fn renderExpression(
20092002 .AsmOutput => {
20102003 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
20112004
2012 try stream.write("[");
2005 try stream.writeAll("[");
20132006 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);
2014 try stream.write("] ");
2007 try stream.writeAll("] ");
20152008 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);
2016 try stream.write(" (");
2009 try stream.writeAll(" (");
20172010
20182011 switch (asm_output.kind) {
20192012 ast.Node.AsmOutput.Kind.Variable => |variable_name| {
20202013 try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None);
20212014 },
20222015 ast.Node.AsmOutput.Kind.Return => |return_type| {
2023 try stream.write("-> ");
2016 try stream.writeAll("-> ");
20242017 try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);
20252018 },
20262019 }
......@@ -2052,7 +2045,7 @@ fn renderVarDecl(
20522045 indent: usize,
20532046 start_col: *usize,
20542047 var_decl: *ast.Node.VarDecl,
2055) (@TypeOf(stream).Child.Error || Error)!void {
2048) (@TypeOf(stream).Error || Error)!void {
20562049 if (var_decl.visib_token) |visib_token| {
20572050 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
20582051 }
......@@ -2125,7 +2118,7 @@ fn renderParamDecl(
21252118 start_col: *usize,
21262119 base: *ast.Node,
21272120 space: Space,
2128) (@TypeOf(stream).Child.Error || Error)!void {
2121) (@TypeOf(stream).Error || Error)!void {
21292122 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
21302123
21312124 try renderDocComments(tree, stream, param_decl, indent, start_col);
......@@ -2154,7 +2147,7 @@ fn renderStatement(
21542147 indent: usize,
21552148 start_col: *usize,
21562149 base: *ast.Node,
2157) (@TypeOf(stream).Child.Error || Error)!void {
2150) (@TypeOf(stream).Error || Error)!void {
21582151 switch (base.id) {
21592152 .VarDecl => {
21602153 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
......@@ -2193,7 +2186,7 @@ fn renderTokenOffset(
21932186 start_col: *usize,
21942187 space: Space,
21952188 token_skip_bytes: usize,
2196) (@TypeOf(stream).Child.Error || Error)!void {
2189) (@TypeOf(stream).Error || Error)!void {
21972190 if (space == Space.BlockStart) {
21982191 if (start_col.* < indent + indent_delta)
21992192 return renderToken(tree, stream, token_index, indent, start_col, Space.Space);
......@@ -2204,7 +2197,7 @@ fn renderTokenOffset(
22042197 }
22052198
22062199 var token = tree.tokens.at(token_index);
2207 try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
2200 try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
22082201
22092202 if (space == Space.NoComment)
22102203 return;
......@@ -2214,15 +2207,15 @@ fn renderTokenOffset(
22142207 if (space == Space.Comma) switch (next_token.id) {
22152208 .Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),
22162209 .LineComment => {
2217 try stream.write(", ");
2210 try stream.writeAll(", ");
22182211 return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);
22192212 },
22202213 else => {
22212214 if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == .MultilineStringLiteralLine) {
2222 try stream.write(",");
2215 try stream.writeAll(",");
22232216 return;
22242217 } else {
2225 try stream.write(",\n");
2218 try stream.writeAll(",\n");
22262219 start_col.* = 0;
22272220 return;
22282221 }
......@@ -2246,7 +2239,7 @@ fn renderTokenOffset(
22462239 if (next_token.id == .MultilineStringLiteralLine) {
22472240 return;
22482241 } else {
2249 try stream.write("\n");
2242 try stream.writeAll("\n");
22502243 start_col.* = 0;
22512244 return;
22522245 }
......@@ -2309,7 +2302,7 @@ fn renderTokenOffset(
23092302 if (next_token.id == .MultilineStringLiteralLine) {
23102303 return;
23112304 } else {
2312 try stream.write("\n");
2305 try stream.writeAll("\n");
23132306 start_col.* = 0;
23142307 return;
23152308 }
......@@ -2327,7 +2320,7 @@ fn renderTokenOffset(
23272320 const newline_count = if (loc.line == 1) @as(u8, 1) else @as(u8, 2);
23282321 try stream.writeByteNTimes('\n', newline_count);
23292322 try stream.writeByteNTimes(' ', indent);
2330 try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
2323 try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
23312324
23322325 offset += 1;
23332326 token = next_token;
......@@ -2338,7 +2331,7 @@ fn renderTokenOffset(
23382331 if (next_token.id == .MultilineStringLiteralLine) {
23392332 return;
23402333 } else {
2341 try stream.write("\n");
2334 try stream.writeAll("\n");
23422335 start_col.* = 0;
23432336 return;
23442337 }
......@@ -2381,7 +2374,7 @@ fn renderToken(
23812374 indent: usize,
23822375 start_col: *usize,
23832376 space: Space,
2384) (@TypeOf(stream).Child.Error || Error)!void {
2377) (@TypeOf(stream).Error || Error)!void {
23852378 return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);
23862379}
23872380
......@@ -2391,7 +2384,7 @@ fn renderDocComments(
23912384 node: var,
23922385 indent: usize,
23932386 start_col: *usize,
2394) (@TypeOf(stream).Child.Error || Error)!void {
2387) (@TypeOf(stream).Error || Error)!void {
23952388 const comment = node.doc_comments orelse return;
23962389 var it = comment.lines.iterator(0);
23972390 const first_token = node.firstToken();
......@@ -2401,7 +2394,7 @@ fn renderDocComments(
24012394 try stream.writeByteNTimes(' ', indent);
24022395 } else {
24032396 try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.NoComment);
2404 try stream.write("\n");
2397 try stream.writeAll("\n");
24052398 try stream.writeByteNTimes(' ', indent);
24062399 }
24072400 }
......@@ -2427,27 +2420,23 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
24272420 };
24282421}
24292422
2430// An OutStream that returns whether the given character has been written to it.
2431// The contents are not written to anything.
2423/// A `std.io.OutStream` that returns whether the given character has been written to it.
2424/// The contents are not written to anything.
24322425const FindByteOutStream = struct {
2433 const Self = FindByteOutStream;
2434 pub const Error = error{};
2435 pub const Stream = std.io.OutStream(Error);
2436
2437 stream: Stream,
24382426 byte_found: bool,
24392427 byte: u8,
24402428
2441 pub fn init(byte: u8) Self {
2442 return Self{
2443 .stream = Stream{ .writeFn = writeFn },
2429 pub const Error = error{};
2430 pub const OutStream = std.io.OutStream(*FindByteOutStream, Error, write);
2431
2432 pub fn init(byte: u8) FindByteOutStream {
2433 return FindByteOutStream{
24442434 .byte = byte,
24452435 .byte_found = false,
24462436 };
24472437 }
24482438
2449 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
2450 const self = @fieldParentPtr(Self, "stream", out_stream);
2439 pub fn write(self: *FindByteOutStream, bytes: []const u8) Error!usize {
24512440 if (self.byte_found) return bytes.len;
24522441 self.byte_found = blk: {
24532442 for (bytes) |b|
......@@ -2456,11 +2445,15 @@ const FindByteOutStream = struct {
24562445 };
24572446 return bytes.len;
24582447 }
2448
2449 pub fn outStream(self: *FindByteOutStream) OutStream {
2450 return .{ .context = self };
2451 }
24592452};
24602453
2461fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Child.Error!void {
2454fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void {
24622455 for (slice) |byte| switch (byte) {
2463 '\t' => try stream.write(" "),
2456 '\t' => try stream.writeAll(" "),
24642457 '\r' => {},
24652458 else => try stream.writeByte(byte),
24662459 };
lib/std/zig/system.zig+10-10
......@@ -570,7 +570,7 @@ pub const NativeTargetInfo = struct {
570570 cross_target: CrossTarget,
571571 ) AbiAndDynamicLinkerFromFileError!NativeTargetInfo {
572572 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);
574574 const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
575575 const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
576576 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
......@@ -610,7 +610,7 @@ pub const NativeTargetInfo = struct {
610610 // Reserve some bytes so that we can deref the 64-bit struct fields
611611 // even when the ELF file is 32-bits.
612612 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);
614614 var ph_buf_i: usize = 0;
615615 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
616616 ph_i += 1;
......@@ -625,7 +625,7 @@ pub const NativeTargetInfo = struct {
625625 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
626626 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
627627 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);
629629 // PT_INTERP includes a null byte in p_filesz.
630630 const len = p_filesz - 1;
631631 // dynamic_linker.max_byte is "max", not "len".
......@@ -656,7 +656,7 @@ pub const NativeTargetInfo = struct {
656656 // Reserve some bytes so that we can deref the 64-bit struct fields
657657 // even when the ELF file is 32-bits.
658658 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(
660660 file,
661661 dyn_buf[0 .. dyn_buf.len - dyn_reserve],
662662 dyn_off,
......@@ -701,14 +701,14 @@ pub const NativeTargetInfo = struct {
701701 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
702702 if (sh_buf.len < shentsize) return error.InvalidElfFile;
703703
704 _ = try preadFull(file, &sh_buf, str_section_off, shentsize);
704 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
705705 const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
706706 const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
707707 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
708708 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
709709 var strtab_buf: [4096:0]u8 = undefined;
710710 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);
712712 const shstrtab = strtab_buf[0..shstrtab_read_len];
713713
714714 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
......@@ -717,7 +717,7 @@ pub const NativeTargetInfo = struct {
717717 // Reserve some bytes so that we can deref the 64-bit struct fields
718718 // even when the ELF file is 32-bits.
719719 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(
721721 file,
722722 sh_buf[0 .. sh_buf.len - sh_reserve],
723723 shoff,
......@@ -751,7 +751,7 @@ pub const NativeTargetInfo = struct {
751751
752752 if (dynstr) |ds| {
753753 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);
755755 const strtab = strtab_buf[0..strtab_read_len];
756756 // TODO this pointer cast should not be necessary
757757 const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
......@@ -813,7 +813,7 @@ pub const NativeTargetInfo = struct {
813813 return result;
814814 }
815815
816 fn preadFull(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
816 fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
817817 var i: u64 = 0;
818818 while (i < min_read_len) {
819819 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
......@@ -853,7 +853,7 @@ pub const NativeTargetInfo = struct {
853853 abi: Target.Abi,
854854 };
855855
856 fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
856 pub fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
857857 if (is_64) {
858858 if (need_bswap) {
859859 return @byteSwap(@TypeOf(int_64), int_64);
src-self-hosted/libc_installation.zig+5-5
......@@ -38,7 +38,7 @@ pub const LibCInstallation = struct {
3838 pub fn parse(
3939 allocator: *Allocator,
4040 libc_file: []const u8,
41 stderr: *std.io.OutStream(fs.File.WriteError),
41 stderr: var,
4242 ) !LibCInstallation {
4343 var self: LibCInstallation = .{};
4444
......@@ -123,7 +123,7 @@ pub const LibCInstallation = struct {
123123 return self;
124124 }
125125
126 pub fn render(self: LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void {
126 pub fn render(self: LibCInstallation, out: var) !void {
127127 @setEvalBranchQuota(4000);
128128 const include_dir = self.include_dir orelse "";
129129 const sys_include_dir = self.sys_include_dir orelse "";
......@@ -348,7 +348,7 @@ pub const LibCInstallation = struct {
348348
349349 for (searches) |search| {
350350 result_buf.shrink(0);
351 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
351 const stream = result_buf.outStream();
352352 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
353353
354354 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
......@@ -395,7 +395,7 @@ pub const LibCInstallation = struct {
395395
396396 for (searches) |search| {
397397 result_buf.shrink(0);
398 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
398 const stream = result_buf.outStream();
399399 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
400400
401401 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
......@@ -459,7 +459,7 @@ pub const LibCInstallation = struct {
459459
460460 for (searches) |search| {
461461 result_buf.shrink(0);
462 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
462 const stream = result_buf.outStream();
463463 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
464464
465465 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{
5252 "sparc-linux-gnu",
5353 "sparcv9-linux-gnu",
5454 "wasm32-freestanding-musl",
55 "x86_64-linux-gnu (native)",
55 "x86_64-linux-gnu",
5656 "x86_64-linux-gnux32",
5757 "x86_64-linux-musl",
5858 "x86_64-windows-gnu",
......@@ -61,7 +61,8 @@ const available_libcs = [_][]const u8{
6161pub fn cmdTargets(
6262 allocator: *Allocator,
6363 args: []const []const u8,
64 stdout: *io.OutStream(fs.File.WriteError),
64 /// Output stream
65 stdout: var,
6566 native_target: Target,
6667) !void {
6768 const available_glibcs = blk: {
......@@ -92,9 +93,9 @@ pub fn cmdTargets(
9293 };
9394 defer allocator.free(available_glibcs);
9495
95 const BOS = io.BufferedOutStream(fs.File.WriteError);
96 var bos = BOS.init(stdout);
97 var jws = std.json.WriteStream(BOS.Stream, 6).init(&bos.stream);
96 var bos = io.bufferedOutStream(stdout);
97 const bos_stream = bos.outStream();
98 var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
9899
99100 try jws.beginObject();
100101
......@@ -219,6 +220,6 @@ pub fn cmdTargets(
219220
220221 try jws.endObject();
221222
222 try bos.stream.writeByte('\n');
223 try bos_stream.writeByte('\n');
223224 return bos.flush();
224225}
src-self-hosted/stage2.zig+15-15
......@@ -18,8 +18,8 @@ const assert = std.debug.assert;
1818const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1919
2020var stderr_file: fs.File = undefined;
21var stderr: *io.OutStream(fs.File.WriteError) = undefined;
22var stdout: *io.OutStream(fs.File.WriteError) = undefined;
21var stderr: fs.File.OutStream = undefined;
22var stdout: fs.File.OutStream = undefined;
2323
2424comptime {
2525 _ = @import("dep_tokenizer.zig");
......@@ -146,7 +146,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error
146146}
147147
148148export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
149 const c_out_stream = &std.io.COutStream.init(output_file).stream;
149 const c_out_stream = std.io.cOutStream(output_file);
150150 _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
151151 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
152152 error.SystemResources => return .SystemResources,
......@@ -186,9 +186,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
186186 try args_list.append(mem.toSliceConst(u8, argv[arg_i]));
187187 }
188188
189 stdout = &std.io.getStdOut().outStream().stream;
189 stdout = std.io.getStdOut().outStream();
190190 stderr_file = std.io.getStdErr();
191 stderr = &stderr_file.outStream().stream;
191 stderr = stderr_file.outStream();
192192
193193 const args = args_list.toSliceConst()[2..];
194194
......@@ -203,11 +203,11 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
203203 const arg = args[i];
204204 if (mem.startsWith(u8, arg, "-")) {
205205 if (mem.eql(u8, arg, "--help")) {
206 try stdout.write(self_hosted_main.usage_fmt);
206 try stdout.writeAll(self_hosted_main.usage_fmt);
207207 process.exit(0);
208208 } else if (mem.eql(u8, arg, "--color")) {
209209 if (i + 1 >= args.len) {
210 try stderr.write("expected [auto|on|off] after --color\n");
210 try stderr.writeAll("expected [auto|on|off] after --color\n");
211211 process.exit(1);
212212 }
213213 i += 1;
......@@ -238,14 +238,14 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
238238
239239 if (stdin_flag) {
240240 if (input_files.len != 0) {
241 try stderr.write("cannot use --stdin with positional arguments\n");
241 try stderr.writeAll("cannot use --stdin with positional arguments\n");
242242 process.exit(1);
243243 }
244244
245245 const stdin_file = io.getStdIn();
246246 var stdin = stdin_file.inStream();
247247
248 const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size);
248 const source_code = try stdin.readAllAlloc(allocator, self_hosted_main.max_src_size);
249249 defer allocator.free(source_code);
250250
251251 const tree = std.zig.parse(allocator, source_code) catch |err| {
......@@ -272,7 +272,7 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
272272 }
273273
274274 if (input_files.len == 0) {
275 try stderr.write("expected at least one source file argument\n");
275 try stderr.writeAll("expected at least one source file argument\n");
276276 process.exit(1);
277277 }
278278
......@@ -409,11 +409,11 @@ fn printErrMsgToFile(
409409 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
410410
411411 var text_buf = try std.Buffer.initSize(allocator, 0);
412 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
412 const out_stream = &text_buf.outStream();
413413 try parse_error.render(&tree.tokens, out_stream);
414414 const text = text_buf.toOwnedSlice();
415415
416 const stream = &file.outStream().stream;
416 const stream = &file.outStream();
417417 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
418418
419419 if (!color_on) return;
......@@ -641,7 +641,7 @@ fn cmdTargets(zig_triple: [*:0]const u8) !void {
641641 return @import("print_targets.zig").cmdTargets(
642642 std.heap.c_allocator,
643643 &[0][]u8{},
644 &std.io.getStdOut().outStream().stream,
644 std.io.getStdOut().outStream(),
645645 target,
646646 );
647647}
......@@ -808,7 +808,7 @@ const Stage2LibCInstallation = extern struct {
808808// ABI warning
809809export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
810810 stderr_file = std.io.getStdErr();
811 stderr = &stderr_file.outStream().stream;
811 stderr = stderr_file.outStream();
812812 const libc_file = mem.toSliceConst(u8, libc_file_z);
813813 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
814814 error.ParseError => return .SemanticAnalyzeFail,
......@@ -870,7 +870,7 @@ export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
870870// ABI warning
871871export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {
872872 var libc = stage1_libc.toStage2();
873 const c_out_stream = &std.io.COutStream.init(output_file).stream;
873 const c_out_stream = std.io.cOutStream(output_file);
874874 libc.render(c_out_stream) catch |err| switch (err) {
875875 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
876876 error.SystemResources => return .SystemResources,
test/compare_output.zig+15-19
......@@ -22,7 +22,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
2222 \\
2323 \\pub fn main() void {
2424 \\ privateFunction();
25 \\ const stdout = &getStdOut().outStream().stream;
25 \\ const stdout = getStdOut().outStream();
2626 \\ stdout.print("OK 2\n", .{}) catch unreachable;
2727 \\}
2828 \\
......@@ -37,7 +37,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
3737 \\// purposefully conflicting function with main.zig
3838 \\// but it's private so it should be OK
3939 \\fn privateFunction() void {
40 \\ const stdout = &getStdOut().outStream().stream;
40 \\ const stdout = getStdOut().outStream();
4141 \\ stdout.print("OK 1\n", .{}) catch unreachable;
4242 \\}
4343 \\
......@@ -63,7 +63,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
6363 tc.addSourceFile("foo.zig",
6464 \\usingnamespace @import("std").io;
6565 \\pub fn foo_function() void {
66 \\ const stdout = &getStdOut().outStream().stream;
66 \\ const stdout = getStdOut().outStream();
6767 \\ stdout.print("OK\n", .{}) catch unreachable;
6868 \\}
6969 );
......@@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
7474 \\
7575 \\pub fn bar_function() void {
7676 \\ if (foo_function()) {
77 \\ const stdout = &getStdOut().outStream().stream;
77 \\ const stdout = getStdOut().outStream();
7878 \\ stdout.print("OK\n", .{}) catch unreachable;
7979 \\ }
8080 \\}
......@@ -106,7 +106,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
106106 \\pub const a_text = "OK\n";
107107 \\
108108 \\pub fn ok() void {
109 \\ const stdout = &io.getStdOut().outStream().stream;
109 \\ const stdout = io.getStdOut().outStream();
110110 \\ stdout.print(b_text, .{}) catch unreachable;
111111 \\}
112112 );
......@@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
124124 \\const io = @import("std").io;
125125 \\
126126 \\pub fn main() void {
127 \\ const stdout = &io.getStdOut().outStream().stream;
127 \\ const stdout = io.getStdOut().outStream();
128128 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable;
129129 \\}
130130 , "Hello, world!\n 12 12 a\n");
......@@ -267,7 +267,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
267267 \\ var x_local : i32 = print_ok(x);
268268 \\}
269269 \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) {
270 \\ const stdout = &io.getStdOut().outStream().stream;
270 \\ const stdout = io.getStdOut().outStream();
271271 \\ stdout.print("OK\n", .{}) catch unreachable;
272272 \\ return 0;
273273 \\}
......@@ -349,7 +349,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
349349 \\pub fn main() void {
350350 \\ const bar = Bar {.field2 = 13,};
351351 \\ const foo = Foo {.field1 = bar,};
352 \\ const stdout = &io.getStdOut().outStream().stream;
352 \\ const stdout = io.getStdOut().outStream();
353353 \\ if (!foo.method()) {
354354 \\ stdout.print("BAD\n", .{}) catch unreachable;
355355 \\ }
......@@ -363,7 +363,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
363363 cases.add("defer with only fallthrough",
364364 \\const io = @import("std").io;
365365 \\pub fn main() void {
366 \\ const stdout = &io.getStdOut().outStream().stream;
366 \\ const stdout = io.getStdOut().outStream();
367367 \\ stdout.print("before\n", .{}) catch unreachable;
368368 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
369369 \\ defer stdout.print("defer2\n", .{}) catch unreachable;
......@@ -376,7 +376,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
376376 \\const io = @import("std").io;
377377 \\const os = @import("std").os;
378378 \\pub fn main() void {
379 \\ const stdout = &io.getStdOut().outStream().stream;
379 \\ const stdout = io.getStdOut().outStream();
380380 \\ stdout.print("before\n", .{}) catch unreachable;
381381 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
382382 \\ defer stdout.print("defer2\n", .{}) catch unreachable;
......@@ -393,7 +393,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
393393 \\ do_test() catch return;
394394 \\}
395395 \\fn do_test() !void {
396 \\ const stdout = &io.getStdOut().outStream().stream;
396 \\ const stdout = io.getStdOut().outStream();
397397 \\ stdout.print("before\n", .{}) catch unreachable;
398398 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
399399 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
......@@ -412,7 +412,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
412412 \\ do_test() catch return;
413413 \\}
414414 \\fn do_test() !void {
415 \\ const stdout = &io.getStdOut().outStream().stream;
415 \\ const stdout = io.getStdOut().outStream();
416416 \\ stdout.print("before\n", .{}) catch unreachable;
417417 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
418418 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
......@@ -429,7 +429,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
429429 \\const io = @import("std").io;
430430 \\
431431 \\pub fn main() void {
432 \\ const stdout = &io.getStdOut().outStream().stream;
432 \\ const stdout = io.getStdOut().outStream();
433433 \\ stdout.print(foo_txt, .{}) catch unreachable;
434434 \\}
435435 , "1234\nabcd\n");
......@@ -448,9 +448,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
448448 \\
449449 \\pub fn main() !void {
450450 \\ var args_it = std.process.args();
451 \\ var stdout_file = io.getStdOut();
452 \\ var stdout_adapter = stdout_file.outStream();
453 \\ const stdout = &stdout_adapter.stream;
451 \\ const stdout = io.getStdOut().outStream();
454452 \\ var index: usize = 0;
455453 \\ _ = args_it.skip();
456454 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
......@@ -489,9 +487,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
489487 \\
490488 \\pub fn main() !void {
491489 \\ var args_it = std.process.args();
492 \\ var stdout_file = io.getStdOut();
493 \\ var stdout_adapter = stdout_file.outStream();
494 \\ const stdout = &stdout_adapter.stream;
490 \\ const stdout = io.getStdOut().outStream();
495491 \\ var index: usize = 0;
496492 \\ _ = args_it.skip();
497493 \\ 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;
44const fmt = std.fmt;
55
66pub fn main() !void {
7 const stdout = &io.getStdOut().outStream().stream;
7 const stdout = io.getStdOut().outStream();
88 const stdin = io.getStdIn();
99
1010 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 {
566566 }
567567 child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) });
568568
569 var stdout_file_in_stream = child.stdout.?.inStream();
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;
569 const stdout = child.stdout.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
573570 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;
575572 defer b.allocator.free(stderr);
576573
577574 const term = child.wait() catch |err| {
......@@ -798,11 +795,8 @@ pub const CompileErrorContext = struct {
798795 var stdout_buf = Buffer.initNull(b.allocator);
799796 var stderr_buf = Buffer.initNull(b.allocator);
800797
801 var stdout_file_in_stream = child.stdout.?.inStream();
802 var stderr_file_in_stream = child.stderr.?.inStream();
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;
798 child.stdout.?.inStream().readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
799 child.stderr.?.inStream().readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
806800
807801 const term = child.wait() catch |err| {
808802 debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) });