Commit 823969a5a4

Jimmi Holst Christensen <jimmi@ziglang.org>
2018-11-29 22:38:39
Implemented new more flexible readLineFrom (#1801)
1 parent b297695
Changed files (2)
example
guess_number
std
example/guess_number/main.zig
@@ -24,15 +24,15 @@ pub fn main() !void {
         try stdout.print("\nGuess a number between 1 and 100: ");
         var line_buf: [20]u8 = undefined;
 
-        const line_len = io.readLine(line_buf[0..]) catch |err| switch (err) {
-            error.InputTooLong => {
+        const line = io.readLineSlice(line_buf[0..]) catch |err| switch (err) {
+            error.OutOfMemory => {
                 try stdout.print("Input too long.\n");
                 continue;
             },
-            error.EndOfFile, error.StdInUnavailable => return err,
+            else => return err,
         };
 
-        const guess = fmt.parseUnsigned(u8, line_buf[0..line_len], 10) catch {
+        const guess = fmt.parseUnsigned(u8, line, 10) catch {
             try stdout.print("Invalid number.\n");
             continue;
         };
std/io.zig
@@ -683,25 +683,73 @@ test "import io tests" {
     }
 }
 
-pub fn readLine(buf: []u8) !usize {
-    var stdin = getStdIn() catch return error.StdInUnavailable;
-    var adapter = stdin.inStream();
-    var stream = &adapter.stream;
-    var index: usize = 0;
+pub fn readLine(buf: *std.Buffer) ![]u8 {
+    var stdin = try getStdIn();
+    var stdin_stream = stdin.inStream();
+    return readLineFrom(&stdin_stream.stream, buf);
+}
+
+/// Reads all characters until the next newline into buf, and returns
+/// a slice of the characters read (excluding the newline character(s)).
+pub fn readLineFrom(stream: var, buf: *std.Buffer) ![]u8 {
+    const start = buf.len();
     while (true) {
-        const byte = stream.readByte() catch return error.EndOfFile;
+        const byte = try stream.readByte();
         switch (byte) {
             '\r' => {
                 // trash the following \n
-                _ = stream.readByte() catch return error.EndOfFile;
-                return index;
-            },
-            '\n' => return index,
-            else => {
-                if (index == buf.len) return error.InputTooLong;
-                buf[index] = byte;
-                index += 1;
+                _ = try stream.readByte();
+                return buf.toSlice()[start..];
             },
+            '\n' => return buf.toSlice()[start..],
+            else => try buf.appendByte(byte),
         }
     }
 }
+
+test "io.readLineFrom" {
+    var bytes: [128]u8 = undefined;
+    const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
+
+    var buf = try std.Buffer.initSize(allocator, 0);
+    var mem_stream = SliceInStream.init(
+        \\Line 1
+        \\Line 22
+        \\Line 333
+    );
+    const stream = &mem_stream.stream;
+
+    debug.assert(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf)));
+    debug.assert(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf)));
+    debug.assertError(readLineFrom(stream, &buf), error.EndOfStream);
+    debug.assert(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333"));
+}
+
+pub fn readLineSlice(slice: []u8) ![]u8 {
+    var stdin = try getStdIn();
+    var stdin_stream = stdin.inStream();
+    return readLineSliceFrom(&stdin_stream.stream, slice);
+}
+
+/// Reads all characters until the next newline into slice, and returns
+/// a slice of the characters read (excluding the newline character(s)).
+pub fn readLineSliceFrom(stream: var, slice: []u8) ![]u8 {
+    // We cannot use Buffer.fromOwnedSlice, as it wants to append a null byte
+    // after taking ownership, which would always require an allocation.
+    var buf = std.Buffer{ .list = std.ArrayList(u8).fromOwnedSlice(debug.failing_allocator, slice) };
+    try buf.resize(0);
+    return try readLineFrom(stream, &buf);
+}
+
+test "io.readLineSliceFrom" {
+    var buf: [7]u8 = undefined;
+    var mem_stream = SliceInStream.init(
+        \\Line 1
+        \\Line 22
+        \\Line 333
+    );
+    const stream = &mem_stream.stream;
+
+    debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..])));
+    debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory);
+}