diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index 91d4c0330b..9a7d2209f5 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -375,6 +375,25 @@ pub fn LinearFifo( } return self.buf[index]; } + + /// Pump data from a reader into a writer + /// stops when reader returns 0 bytes (EOF) + /// Buffer size must be set before calling; a buffer length of 0 is invalid. + pub fn pump(self: *Self, src_reader: anytype, dest_writer: anytype) !void { + assert(self.buf.len > 0); + while (true) { + if (self.writableLength() > 0) { + const n = try src_reader.read(self.writableSlice(0)); + if (n == 0) break; // EOF + self.update(n); + } + self.discard(try dest_writer.write(self.readableSlice(0))); + } + // flush remaining data + while (self.readableLength() > 0) { + self.discard(try dest_writer.write(self.readableSlice(0))); + } + } }; } @@ -461,6 +480,15 @@ test "LinearFifo(u8, .Dynamic)" { testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); } + + { + try fifo.ensureCapacity(1); + var in_fbs = std.io.fixedBufferStream("pump test"); + var out_buf: [50]u8 = undefined; + var out_fbs = std.io.fixedBufferStream(&out_buf); + try fifo.pump(in_fbs.reader(), out_fbs.writer()); + testing.expectEqualSlices(u8, in_fbs.buffer, out_fbs.getWritten()); + } } test "LinearFifo" { diff --git a/test/standalone/cat/main.zig b/test/standalone/cat/main.zig index 8539a0de0f..65cd2b1e7e 100644 --- a/test/standalone/cat/main.zig +++ b/test/standalone/cat/main.zig @@ -3,12 +3,17 @@ const io = std.io; const process = std.process; const fs = std.fs; const mem = std.mem; -const warn = std.debug.warn; -const allocator = std.testing.allocator; +const warn = std.log.warn; + +var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; +const allocator = &general_purpose_allocator.allocator; pub fn main() !void { + defer _ = general_purpose_allocator.deinit(); + var args_it = process.args(); const exe = try unwrapArg(args_it.next(allocator).?); + defer allocator.free(exe); var catted_anything = false; const stdout_file = io.getStdOut(); @@ -16,6 +21,7 @@ pub fn main() !void { while (args_it.next(allocator)) |arg_or_err| { const arg = try unwrapArg(arg_or_err); + defer allocator.free(arg); if (mem.eql(u8, arg, "-")) { catted_anything = true; try cat_file(stdout_file, io.getStdIn()); @@ -44,23 +50,12 @@ fn usage(exe: []const u8) !void { // TODO use copy_file_range fn cat_file(stdout: fs.File, file: fs.File) !void { - var buf: [1024 * 4]u8 = undefined; + var fifo = std.fifo.LinearFifo(u8, .{ .Static = 1024 * 4 }).init(); - while (true) { - const bytes_read = file.read(buf[0..]) catch |err| { - warn("Unable to read from stream: {}\n", .{@errorName(err)}); - return err; - }; - - if (bytes_read == 0) { - break; - } - - stdout.writeAll(buf[0..bytes_read]) catch |err| { - warn("Unable to write to stdout: {}\n", .{@errorName(err)}); - return err; - }; - } + fifo.pump(file.reader(), stdout.writer()) catch |err| { + warn("Unable to read from stream or write to stdout: {}\n", .{@errorName(err)}); + return err; + }; } fn unwrapArg(arg: anyerror![]u8) ![]u8 {