Commit 4d5a598599

Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2022-01-09 17:12:55
readUntilDelimiter*: read only if buffer not full
Ref.: #9594
1 parent aa8112c
Changed files (1)
lib
std
lib/std/io/reader.zig
@@ -107,16 +107,16 @@ pub fn Reader(
         ) !void {
             array_list.shrinkRetainingCapacity(0);
             while (true) {
+                if (array_list.items.len == max_size) {
+                    return error.StreamTooLong;
+                }
+
                 var byte: u8 = try self.readByte();
 
                 if (byte == delimiter) {
                     return;
                 }
 
-                if (array_list.items.len == max_size) {
-                    return error.StreamTooLong;
-                }
-
                 try array_list.append(byte);
             }
         }
@@ -139,17 +139,20 @@ pub fn Reader(
 
         /// Reads from the stream until specified byte is found. If the buffer is not
         /// large enough to hold the entire contents, `error.StreamTooLong` is returned.
+        /// If end-of-stream is found, `error.EndOfStream` is returned.
         /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
-        /// delimiter byte is not included in the returned slice.
+        /// delimiter byte is written to the output buffer but is not included
+        /// in the returned slice.
         pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 {
             var index: usize = 0;
             while (true) {
+                if (index >= buf.len) return error.StreamTooLong;
+
                 const byte = try self.readByte();
+                buf[index] = byte;
 
                 if (byte == delimiter) return buf[0..index];
-                if (index >= buf.len) return error.StreamTooLong;
 
-                buf[index] = byte;
                 index += 1;
             }
         }
@@ -185,10 +188,13 @@ pub fn Reader(
         /// If end-of-stream is found, returns the rest of the stream. If this
         /// function is called again after that, returns null.
         /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
-        /// delimiter byte is not included in the returned slice.
+        /// delimiter byte is written to the output buffer but is not included
+        /// in the returned slice.
         pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {
             var index: usize = 0;
             while (true) {
+                if (index >= buf.len) return error.StreamTooLong;
+
                 const byte = self.readByte() catch |err| switch (err) {
                     error.EndOfStream => {
                         if (index == 0) {
@@ -199,11 +205,10 @@ pub fn Reader(
                     },
                     else => |e| return e,
                 };
+                buf[index] = byte;
 
                 if (byte == delimiter) return buf[0..index];
-                if (index >= buf.len) return error.StreamTooLong;
 
-                buf[index] = byte;
                 index += 1;
             }
         }
@@ -363,3 +368,272 @@ test "Reader.skipBytes" {
     try reader.skipBytes(0, .{});
     try testing.expectError(error.EndOfStream, reader.skipBytes(1, .{}));
 }
+
+test "Reader.readUntilDelimiterArrayList returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
+    const a = std.testing.allocator;
+    var list = std.ArrayList(u8).init(a);
+    defer list.deinit();
+
+    const reader = std.io.fixedBufferStream("0000\n1234\n").reader();
+
+    try reader.readUntilDelimiterArrayList(&list, '\n', 5);
+    try std.testing.expectEqualStrings("0000", list.items);
+    try reader.readUntilDelimiterArrayList(&list, '\n', 5);
+    try std.testing.expectEqualStrings("1234", list.items);
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
+}
+
+test "Reader.readUntilDelimiterArrayList returns an empty ArrayList" {
+    const a = std.testing.allocator;
+    var list = std.ArrayList(u8).init(a);
+    defer list.deinit();
+
+    const reader = std.io.fixedBufferStream("\n").reader();
+
+    try reader.readUntilDelimiterArrayList(&list, '\n', 5);
+    try std.testing.expectEqualStrings("", list.items);
+}
+
+test "Reader.readUntilDelimiterArrayList returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
+    const a = std.testing.allocator;
+    var list = std.ArrayList(u8).init(a);
+    defer list.deinit();
+
+    const reader = std.io.fixedBufferStream("1234567\n").reader();
+
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5));
+    try std.testing.expectEqualStrings("12345", list.items);
+    try reader.readUntilDelimiterArrayList(&list, '\n', 5);
+    try std.testing.expectEqualStrings("67", list.items);
+}
+
+test "Reader.readUntilDelimiterArrayList returns EndOfStream" {
+    const a = std.testing.allocator;
+    var list = std.ArrayList(u8).init(a);
+    defer list.deinit();
+
+    const reader = std.io.fixedBufferStream("1234").reader();
+
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
+    try std.testing.expectEqualStrings("1234", list.items);
+}
+
+test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("0000\n1234\n").reader();
+
+    {
+        var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
+        defer a.free(result);
+        try std.testing.expectEqualStrings("0000", result);
+    }
+
+    {
+        var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
+        defer a.free(result);
+        try std.testing.expectEqualStrings("1234", result);
+    }
+
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
+}
+
+test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("\n").reader();
+
+    {
+        var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
+        defer a.free(result);
+        try std.testing.expectEqualStrings("", result);
+    }
+}
+
+test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("1234567\n").reader();
+
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5));
+
+    var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
+    defer a.free(result);
+    try std.testing.expectEqualStrings("67", result);
+}
+
+test "Reader.readUntilDelimiterAlloc returns EndOfStream" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("1234").reader();
+
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
+}
+
+test "Reader.readUntilDelimiter returns bytes read until the delimiter" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("0000\n1234\n").reader();
+    try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n'));
+    try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns an empty string" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("\n").reader();
+    try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns StreamTooLong, then an empty string" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("12345\n").reader();
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
+    try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234567\n").reader();
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
+    try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns EndOfStream" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("").reader();
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns bytes read until delimiter, then EndOfStream" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234\n").reader();
+    try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns EndOfStream" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234").reader();
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter returns StreamTooLong, then EndOfStream" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("12345").reader();
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
+    try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
+}
+
+test "Reader.readUntilDelimiter writes all bytes read to the output buffer" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("0000\n12345").reader();
+    _ = try reader.readUntilDelimiter(&buf, '\n');
+    try std.testing.expectEqualStrings("0000\n", &buf);
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
+    try std.testing.expectEqualStrings("12345", &buf);
+}
+
+test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("0000\n1234\n").reader();
+
+    {
+        var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
+        defer a.free(result);
+        try std.testing.expectEqualStrings("0000", result);
+    }
+
+    {
+        var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
+        defer a.free(result);
+        try std.testing.expectEqualStrings("1234", result);
+    }
+
+    try std.testing.expect((try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)) == null);
+}
+
+test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("\n").reader();
+
+    {
+        var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
+        defer a.free(result);
+        try std.testing.expectEqualStrings("", result);
+    }
+}
+
+test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
+    const a = std.testing.allocator;
+
+    const reader = std.io.fixedBufferStream("1234567\n").reader();
+
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5));
+
+    var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
+    defer a.free(result);
+    try std.testing.expectEqualStrings("67", result);
+}
+
+test "Reader.readUntilDelimiterOrEof returns bytes read until the delimiter" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("0000\n1234\n").reader();
+    try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+    try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+}
+
+test "Reader.readUntilDelimiterOrEof returns an empty string" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("\n").reader();
+    try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+}
+
+test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then an empty string" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("12345\n").reader();
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
+    try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+}
+
+test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234567\n").reader();
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
+    try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+}
+
+test "Reader.readUntilDelimiterOrEof returns null" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("").reader();
+    try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
+}
+
+test "Reader.readUntilDelimiterOrEof returns bytes read until delimiter, then null" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234\n").reader();
+    try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+    try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
+}
+
+test "Reader.readUntilDelimiterOrEof returns bytes read until end-of-stream" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234").reader();
+    try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+}
+
+test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("1234567").reader();
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
+    try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
+}
+
+test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" {
+    var buf: [5]u8 = undefined;
+    const reader = std.io.fixedBufferStream("0000\n12345").reader();
+    _ = try reader.readUntilDelimiterOrEof(&buf, '\n');
+    try std.testing.expectEqualStrings("0000\n", &buf);
+    try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
+    try std.testing.expectEqualStrings("12345", &buf);
+}