diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d0cdf6f4f5..f339aa639b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -53,7 +53,7 @@ pub const LineInfo = struct { /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. var stderr_file: File = undefined; -var stderr_file_out_stream: File.OutStream = undefined; +var stderr_file_writer: File.Writer = undefined; var stderr_stream: ?*File.OutStream = null; var stderr_mutex = std.Mutex.init(); @@ -70,8 +70,8 @@ pub fn getStderrStream() *File.OutStream { return st; } else { stderr_file = io.getStdErr(); - stderr_file_out_stream = stderr_file.outStream(); - const st = &stderr_file_out_stream; + stderr_file_writer = stderr_file.outStream(); + const st = &stderr_file_writer; stderr_stream = st; return st; } diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index ce9546a9d9..9db95c4c6f 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -648,9 +648,17 @@ pub const File = struct { return .{ .context = file }; } - pub const OutStream = io.OutStream(File, WriteError, write); + pub const Writer = io.Writer(File, WriteError, write); - pub fn outStream(file: File) OutStream { + /// Deprecated: use `Writer` + pub const OutStream = Writer; + + pub fn writer(file: File) Writer { + return .{ .context = file }; + } + + /// Deprecated: use `writer` + pub fn outStream(file: File) Writer { return .{ .context = file }; } diff --git a/lib/std/io.zig b/lib/std/io.zig index 3a8b27e8cf..fe45cd8132 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -102,11 +102,17 @@ pub fn getStdIn() File { } pub const InStream = @import("io/in_stream.zig").InStream; -pub const OutStream = @import("io/out_stream.zig").OutStream; +pub const Writer = @import("io/writer.zig").Writer; +/// Deprecated: use `Writer` +pub const OutStream = Writer; pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; -pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream; -pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream; +pub const BufferedWriter = @import("io/buffered_writer.zig").BufferedWriter; +pub const bufferedWriter = @import("io/buffered_writer.zig").bufferedWriter; +/// Deprecated: use `BufferedWriter` +pub const BufferedOutStream = BufferedWriter; +/// Deprecated: use `bufferedWriter` +pub const bufferedOutStream = bufferedWriter; pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream; pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream; @@ -117,20 +123,36 @@ pub const peekStream = @import("io/peek_stream.zig").peekStream; pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream; pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream; -pub const COutStream = @import("io/c_out_stream.zig").COutStream; -pub const cOutStream = @import("io/c_out_stream.zig").cOutStream; +pub const CWriter = @import("io/c_writer.zig").CWriter; +pub const cWriter = @import("io/c_writer.zig").cWriter; +/// Deprecated: use `CWriter` +pub const COutStream = CWriter; +/// Deprecated: use `cWriter` +pub const cOutStream = cWriter; -pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream; -pub const countingOutStream = @import("io/counting_out_stream.zig").countingOutStream; +pub const CountingWriter = @import("io/counting_writer.zig").CountingWriter; +pub const countingWriter = @import("io/counting_writer.zig").countingWriter; +/// Deprecated: use `CountingWriter` +pub const CountingOutStream = CountingWriter; +/// Deprecated: use `countingWriter` +pub const countingOutStream = countingWriter; -pub const MultiOutStream = @import("io/multi_out_stream.zig").MultiOutStream; -pub const multiOutStream = @import("io/multi_out_stream.zig").multiOutStream; +pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter; +pub const multiWriter = @import("io/multi_writer.zig").multiWriter; +/// Deprecated: use `MultiWriter` +pub const MultiOutStream = MultiWriter; +/// Deprecated: use `multiWriter` +pub const multiOutStream = multiWriter; pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream; pub const bitInStream = @import("io/bit_in_stream.zig").bitInStream; -pub const BitOutStream = @import("io/bit_out_stream.zig").BitOutStream; -pub const bitOutStream = @import("io/bit_out_stream.zig").bitOutStream; +pub const BitWriter = @import("io/bit_writer.zig").BitWriter; +pub const bitWriter = @import("io/bit_writer.zig").bitWriter; +/// Deprecated: use `BitWriter` +pub const BitOutStream = BitWriter; +/// Deprecated: use `bitWriter` +pub const bitOutStream = bitWriter; pub const Packing = @import("io/serialization.zig").Packing; @@ -144,29 +166,34 @@ pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAt pub const StreamSource = @import("io/stream_source.zig").StreamSource; -/// An OutStream that doesn't write to anything. -pub const null_out_stream = @as(NullOutStream, .{ .context = {} }); +/// A Writer that doesn't write to anything. +pub const null_writer = @as(NullWriter, .{ .context = {} }); -const NullOutStream = OutStream(void, error{}, dummyWrite); +/// Deprecated: use `null_writer` +pub const null_out_stream = null_writer; + +const NullWriter = Writer(void, error{}, dummyWrite); +/// Deprecated: use NullWriter +const NullOutStream = NullWriter; fn dummyWrite(context: void, data: []const u8) error{}!usize { return data.len; } -test "null_out_stream" { - null_out_stream.writeAll("yay" ** 10) catch |err| switch (err) {}; +test "null_writer" { + null_writer.writeAll("yay" ** 10) catch |err| switch (err) {}; } test "" { _ = @import("io/bit_in_stream.zig"); - _ = @import("io/bit_out_stream.zig"); + _ = @import("io/bit_writer.zig"); _ = @import("io/buffered_atomic_file.zig"); _ = @import("io/buffered_in_stream.zig"); - _ = @import("io/buffered_out_stream.zig"); - _ = @import("io/c_out_stream.zig"); - _ = @import("io/counting_out_stream.zig"); + _ = @import("io/buffered_writer.zig"); + _ = @import("io/c_writer.zig"); + _ = @import("io/counting_writer.zig"); _ = @import("io/fixed_buffer_stream.zig"); _ = @import("io/in_stream.zig"); - _ = @import("io/out_stream.zig"); + _ = @import("io/writer.zig"); _ = @import("io/peek_stream.zig"); _ = @import("io/seekable_stream.zig"); _ = @import("io/serialization.zig"); diff --git a/lib/std/io/bit_out_stream.zig b/lib/std/io/bit_out_stream.zig index cffea26de6..20a3795d59 100644 --- a/lib/std/io/bit_out_stream.zig +++ b/lib/std/io/bit_out_stream.zig @@ -1,197 +1,5 @@ -const std = @import("../std.zig"); -const builtin = std.builtin; -const io = std.io; -const testing = std.testing; -const assert = std.debug.assert; -const trait = std.meta.trait; -const meta = std.meta; -const math = std.math; +/// Deprecated: use `std.io.bit_writer.BitWriter` +pub const BitOutStream = @import("./bit_writer.zig").BitWriter; -/// Creates a stream which allows for writing bit fields to another stream -pub fn BitOutStream(endian: builtin.Endian, comptime OutStreamType: type) type { - return struct { - out_stream: OutStreamType, - bit_buffer: u8, - bit_count: u4, - - pub const Error = OutStreamType.Error; - pub const OutStream = io.OutStream(*Self, Error, write); - - const Self = @This(); - const u8_bit_count = comptime meta.bitCount(u8); - const u4_bit_count = comptime meta.bitCount(u4); - - pub fn init(out_stream: OutStreamType) Self { - return Self{ - .out_stream = out_stream, - .bit_buffer = 0, - .bit_count = 0, - }; - } - - /// Write the specified number of bits to the stream from the least significant bits of - /// the specified unsigned int value. Bits will only be written to the stream when there - /// are enough to fill a byte. - pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { - if (bits == 0) return; - - const U = @TypeOf(value); - comptime assert(trait.isUnsignedInt(U)); - - //by extending the buffer to a minimum of u8 we can cover a number of edge cases - // related to shifting and casting. - const u_bit_count = comptime meta.bitCount(U); - const buf_bit_count = bc: { - assert(u_bit_count >= bits); - break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; - }; - const Buf = std.meta.Int(false, buf_bit_count); - const BufShift = math.Log2Int(Buf); - - const buf_value = @intCast(Buf, value); - - const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); - var in_buffer = switch (endian) { - .Big => buf_value << @intCast(BufShift, buf_bit_count - bits), - .Little => buf_value, - }; - var in_bits = bits; - - if (self.bit_count > 0) { - const bits_remaining = u8_bit_count - self.bit_count; - const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); - switch (endian) { - .Big => { - const shift = @intCast(BufShift, high_byte_shift + self.bit_count); - const v = @intCast(u8, in_buffer >> shift); - self.bit_buffer |= v; - in_buffer <<= n; - }, - .Little => { - const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); - self.bit_buffer |= v; - in_buffer >>= n; - }, - } - self.bit_count += n; - in_bits -= n; - - //if we didn't fill the buffer, it's because bits < bits_remaining; - if (self.bit_count != u8_bit_count) return; - try self.out_stream.writeByte(self.bit_buffer); - self.bit_buffer = 0; - self.bit_count = 0; - } - //at this point we know bit_buffer is empty - - //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer - while (in_bits >= u8_bit_count) { - switch (endian) { - .Big => { - const v = @intCast(u8, in_buffer >> high_byte_shift); - try self.out_stream.writeByte(v); - in_buffer <<= @intCast(u3, u8_bit_count - 1); - in_buffer <<= 1; - }, - .Little => { - const v = @truncate(u8, in_buffer); - try self.out_stream.writeByte(v); - in_buffer >>= @intCast(u3, u8_bit_count - 1); - in_buffer >>= 1; - }, - } - in_bits -= u8_bit_count; - } - - if (in_bits > 0) { - self.bit_count = @intCast(u4, in_bits); - self.bit_buffer = switch (endian) { - .Big => @truncate(u8, in_buffer >> high_byte_shift), - .Little => @truncate(u8, in_buffer), - }; - } - } - - /// Flush any remaining bits to the stream. - pub fn flushBits(self: *Self) Error!void { - if (self.bit_count == 0) return; - try self.out_stream.writeByte(self.bit_buffer); - self.bit_buffer = 0; - self.bit_count = 0; - } - - pub fn write(self: *Self, buffer: []const u8) Error!usize { - // TODO: I'm not sure this is a good idea, maybe flushBits should be forced - if (self.bit_count > 0) { - for (buffer) |b, i| - try self.writeBits(b, u8_bit_count); - return buffer.len; - } - - return self.out_stream.write(buffer); - } - - pub fn outStream(self: *Self) OutStream { - return .{ .context = self }; - } - }; -} - -pub fn bitOutStream( - comptime endian: builtin.Endian, - underlying_stream: var, -) BitOutStream(endian, @TypeOf(underlying_stream)) { - return BitOutStream(endian, @TypeOf(underlying_stream)).init(underlying_stream); -} - -test "api coverage" { - var mem_be = [_]u8{0} ** 2; - var mem_le = [_]u8{0} ** 2; - - var mem_out_be = io.fixedBufferStream(&mem_be); - var bit_stream_be = bitOutStream(.Big, mem_out_be.outStream()); - - try bit_stream_be.writeBits(@as(u2, 1), 1); - try bit_stream_be.writeBits(@as(u5, 2), 2); - try bit_stream_be.writeBits(@as(u128, 3), 3); - try bit_stream_be.writeBits(@as(u8, 4), 4); - try bit_stream_be.writeBits(@as(u9, 5), 5); - try bit_stream_be.writeBits(@as(u1, 1), 1); - - testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); - - mem_out_be.pos = 0; - - try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15); - try bit_stream_be.flushBits(); - testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); - - mem_out_be.pos = 0; - try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16); - testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); - - try bit_stream_be.writeBits(@as(u0, 0), 0); - - var mem_out_le = io.fixedBufferStream(&mem_le); - var bit_stream_le = bitOutStream(.Little, mem_out_le.outStream()); - - try bit_stream_le.writeBits(@as(u2, 1), 1); - try bit_stream_le.writeBits(@as(u5, 2), 2); - try bit_stream_le.writeBits(@as(u128, 3), 3); - try bit_stream_le.writeBits(@as(u8, 4), 4); - try bit_stream_le.writeBits(@as(u9, 5), 5); - try bit_stream_le.writeBits(@as(u1, 1), 1); - - testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); - - mem_out_le.pos = 0; - try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15); - try bit_stream_le.flushBits(); - testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); - - mem_out_le.pos = 0; - try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16); - testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); - - try bit_stream_le.writeBits(@as(u0, 0), 0); -} +/// Deprecated: use `std.io.bit_writer.bitWriter` +pub const bitOutStream = @import("./bit_writer.zig").bitWriter; diff --git a/lib/std/io/bit_writer.zig b/lib/std/io/bit_writer.zig new file mode 100644 index 0000000000..bdf9156136 --- /dev/null +++ b/lib/std/io/bit_writer.zig @@ -0,0 +1,203 @@ +const std = @import("../std.zig"); +const builtin = std.builtin; +const io = std.io; +const testing = std.testing; +const assert = std.debug.assert; +const trait = std.meta.trait; +const meta = std.meta; +const math = std.math; + +/// Creates a stream which allows for writing bit fields to another stream +pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type { + return struct { + forward_writer: WriterType, + bit_buffer: u8, + bit_count: u4, + + pub const Error = WriterType.Error; + pub const Writer = io.Writer(*Self, Error, write); + /// Deprecated: use `Writer` + pub const OutStream = io.OutStream(*Self, Error, write); + + const Self = @This(); + const u8_bit_count = comptime meta.bitCount(u8); + const u4_bit_count = comptime meta.bitCount(u4); + + pub fn init(forward_writer: WriterType) Self { + return Self{ + .forward_writer = forward_writer, + .bit_buffer = 0, + .bit_count = 0, + }; + } + + /// Write the specified number of bits to the stream from the least significant bits of + /// the specified unsigned int value. Bits will only be written to the stream when there + /// are enough to fill a byte. + pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { + if (bits == 0) return; + + const U = @TypeOf(value); + comptime assert(trait.isUnsignedInt(U)); + + //by extending the buffer to a minimum of u8 we can cover a number of edge cases + // related to shifting and casting. + const u_bit_count = comptime meta.bitCount(U); + const buf_bit_count = bc: { + assert(u_bit_count >= bits); + break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; + }; + const Buf = std.meta.Int(false, buf_bit_count); + const BufShift = math.Log2Int(Buf); + + const buf_value = @intCast(Buf, value); + + const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); + var in_buffer = switch (endian) { + .Big => buf_value << @intCast(BufShift, buf_bit_count - bits), + .Little => buf_value, + }; + var in_bits = bits; + + if (self.bit_count > 0) { + const bits_remaining = u8_bit_count - self.bit_count; + const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); + switch (endian) { + .Big => { + const shift = @intCast(BufShift, high_byte_shift + self.bit_count); + const v = @intCast(u8, in_buffer >> shift); + self.bit_buffer |= v; + in_buffer <<= n; + }, + .Little => { + const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); + self.bit_buffer |= v; + in_buffer >>= n; + }, + } + self.bit_count += n; + in_bits -= n; + + //if we didn't fill the buffer, it's because bits < bits_remaining; + if (self.bit_count != u8_bit_count) return; + try self.forward_writer.writeByte(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + //at this point we know bit_buffer is empty + + //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer + while (in_bits >= u8_bit_count) { + switch (endian) { + .Big => { + const v = @intCast(u8, in_buffer >> high_byte_shift); + try self.forward_writer.writeByte(v); + in_buffer <<= @intCast(u3, u8_bit_count - 1); + in_buffer <<= 1; + }, + .Little => { + const v = @truncate(u8, in_buffer); + try self.forward_writer.writeByte(v); + in_buffer >>= @intCast(u3, u8_bit_count - 1); + in_buffer >>= 1; + }, + } + in_bits -= u8_bit_count; + } + + if (in_bits > 0) { + self.bit_count = @intCast(u4, in_bits); + self.bit_buffer = switch (endian) { + .Big => @truncate(u8, in_buffer >> high_byte_shift), + .Little => @truncate(u8, in_buffer), + }; + } + } + + /// Flush any remaining bits to the stream. + pub fn flushBits(self: *Self) Error!void { + if (self.bit_count == 0) return; + try self.forward_writer.writeByte(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn write(self: *Self, buffer: []const u8) Error!usize { + // TODO: I'm not sure this is a good idea, maybe flushBits should be forced + if (self.bit_count > 0) { + for (buffer) |b, i| + try self.writeBits(b, u8_bit_count); + return buffer.len; + } + + return self.forward_writer.write(buffer); + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + /// Deprecated: use `writer` + pub fn outStream(self: *Self) OutStream { + return .{ .context = self }; + } + }; +} + +pub fn bitWriter( + comptime endian: builtin.Endian, + underlying_stream: var, +) BitWriter(endian, @TypeOf(underlying_stream)) { + return BitWriter(endian, @TypeOf(underlying_stream)).init(underlying_stream); +} + +test "api coverage" { + var mem_be = [_]u8{0} ** 2; + var mem_le = [_]u8{0} ** 2; + + var mem_out_be = io.fixedBufferStream(&mem_be); + var bit_stream_be = bitWriter(.Big, mem_out_be.writer()); + + try bit_stream_be.writeBits(@as(u2, 1), 1); + try bit_stream_be.writeBits(@as(u5, 2), 2); + try bit_stream_be.writeBits(@as(u128, 3), 3); + try bit_stream_be.writeBits(@as(u8, 4), 4); + try bit_stream_be.writeBits(@as(u9, 5), 5); + try bit_stream_be.writeBits(@as(u1, 1), 1); + + testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); + + mem_out_be.pos = 0; + + try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15); + try bit_stream_be.flushBits(); + testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); + + mem_out_be.pos = 0; + try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16); + testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); + + try bit_stream_be.writeBits(@as(u0, 0), 0); + + var mem_out_le = io.fixedBufferStream(&mem_le); + var bit_stream_le = bitWriter(.Little, mem_out_le.writer()); + + try bit_stream_le.writeBits(@as(u2, 1), 1); + try bit_stream_le.writeBits(@as(u5, 2), 2); + try bit_stream_le.writeBits(@as(u128, 3), 3); + try bit_stream_le.writeBits(@as(u8, 4), 4); + try bit_stream_le.writeBits(@as(u9, 5), 5); + try bit_stream_le.writeBits(@as(u1, 1), 1); + + testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15); + try bit_stream_le.flushBits(); + testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16); + testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); + + try bit_stream_le.writeBits(@as(u0, 0), 0); +} diff --git a/lib/std/io/buffered_atomic_file.zig b/lib/std/io/buffered_atomic_file.zig index 81c040da28..3a3deb43bf 100644 --- a/lib/std/io/buffered_atomic_file.zig +++ b/lib/std/io/buffered_atomic_file.zig @@ -34,7 +34,7 @@ pub const BufferedAtomicFile = struct { errdefer self.atomic_file.deinit(); self.file_stream = self.atomic_file.file.outStream(); - self.buffered_stream = .{ .unbuffered_out_stream = self.file_stream }; + self.buffered_stream = .{ .unbuffered_writer = self.file_stream }; return self; } diff --git a/lib/std/io/buffered_out_stream.zig b/lib/std/io/buffered_out_stream.zig index 43af83c537..6b8ede5489 100644 --- a/lib/std/io/buffered_out_stream.zig +++ b/lib/std/io/buffered_out_stream.zig @@ -1,41 +1,5 @@ -const std = @import("../std.zig"); -const io = std.io; +/// Deprecated: use `std.io.buffered_writer.BufferedWriter` +pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter; -pub fn BufferedOutStream(comptime buffer_size: usize, comptime OutStreamType: type) type { - return struct { - unbuffered_out_stream: OutStreamType, - fifo: FifoType = FifoType.init(), - - pub const Error = OutStreamType.Error; - pub const OutStream = io.OutStream(*Self, Error, write); - - const Self = @This(); - const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size }); - - pub fn flush(self: *Self) !void { - while (true) { - const slice = self.fifo.readableSlice(0); - if (slice.len == 0) break; - try self.unbuffered_out_stream.writeAll(slice); - self.fifo.discard(slice.len); - } - } - - pub fn outStream(self: *Self) OutStream { - return .{ .context = self }; - } - - pub fn write(self: *Self, bytes: []const u8) Error!usize { - if (bytes.len >= self.fifo.writableLength()) { - try self.flush(); - return self.unbuffered_out_stream.write(bytes); - } - self.fifo.writeAssumeCapacity(bytes); - return bytes.len; - } - }; -} - -pub fn bufferedOutStream(underlying_stream: var) BufferedOutStream(4096, @TypeOf(underlying_stream)) { - return .{ .unbuffered_out_stream = underlying_stream }; -} +/// Deprecated: use `std.io.buffered_writer.bufferedWriter` +pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter diff --git a/lib/std/io/buffered_writer.zig b/lib/std/io/buffered_writer.zig new file mode 100644 index 0000000000..5cd102b510 --- /dev/null +++ b/lib/std/io/buffered_writer.zig @@ -0,0 +1,48 @@ +const std = @import("../std.zig"); +const io = std.io; + +pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) type { + return struct { + unbuffered_writer: WriterType, + fifo: FifoType = FifoType.init(), + + pub const Error = WriterType.Error; + pub const Writer = io.Writer(*Self, Error, write); + /// Deprecated: use `Writer` + pub const OutStream = Writer; + + const Self = @This(); + const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size }); + + pub fn flush(self: *Self) !void { + while (true) { + const slice = self.fifo.readableSlice(0); + if (slice.len == 0) break; + try self.unbuffered_writer.writeAll(slice); + self.fifo.discard(slice.len); + } + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + /// Deprecated: use writer + pub fn outStream(self: *Self) Writer { + return .{ .context = self }; + } + + pub fn write(self: *Self, bytes: []const u8) Error!usize { + if (bytes.len >= self.fifo.writableLength()) { + try self.flush(); + return self.unbuffered_writer.write(bytes); + } + self.fifo.writeAssumeCapacity(bytes); + return bytes.len; + } + }; +} + +pub fn bufferedWriter(underlying_stream: var) BufferedWriter(4096, @TypeOf(underlying_stream)) { + return .{ .unbuffered_writer = underlying_stream }; +} diff --git a/lib/std/io/c_out_stream.zig b/lib/std/io/c_out_stream.zig index 119b7f9d29..e10795ae0a 100644 --- a/lib/std/io/c_out_stream.zig +++ b/lib/std/io/c_out_stream.zig @@ -1,44 +1,5 @@ -const std = @import("../std.zig"); -const builtin = std.builtin; -const io = std.io; -const testing = std.testing; +/// Deprecated: use `std.io.c_writer.CWriter` +pub const COutStream = @import("./c_writer.zig").CWriter; -pub const COutStream = io.OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite); - -pub fn cOutStream(c_file: *std.c.FILE) COutStream { - return .{ .context = c_file }; -} - -fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { - const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); - if (amt_written >= 0) return amt_written; - switch (std.c._errno().*) { - 0 => unreachable, - os.EINVAL => unreachable, - os.EFAULT => unreachable, - os.EAGAIN => unreachable, // this is a blocking API - os.EBADF => unreachable, // always a race condition - os.EDESTADDRREQ => unreachable, // connect was never called - os.EDQUOT => return error.DiskQuota, - os.EFBIG => return error.FileTooBig, - os.EIO => return error.InputOutput, - os.ENOSPC => return error.NoSpaceLeft, - os.EPERM => return error.AccessDenied, - os.EPIPE => return error.BrokenPipe, - else => |err| return os.unexpectedErrno(@intCast(usize, err)), - } -} - -test "" { - if (!builtin.link_libc) return error.SkipZigTest; - - const filename = "tmp_io_test_file.txt"; - const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; - defer { - _ = std.c.fclose(out_file); - std.fs.cwd().deleteFileZ(filename) catch {}; - } - - const out_stream = cOutStream(out_file); - try out_stream.print("hi: {}\n", .{@as(i32, 123)}); -} +/// Deprecated: use `std.io.c_writer.cWriter` +pub const cOutStream = @import("./c_writer.zig").cWriter; diff --git a/lib/std/io/c_writer.zig b/lib/std/io/c_writer.zig new file mode 100644 index 0000000000..6292450223 --- /dev/null +++ b/lib/std/io/c_writer.zig @@ -0,0 +1,44 @@ +const std = @import("../std.zig"); +const builtin = std.builtin; +const io = std.io; +const testing = std.testing; + +pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite); + +pub fn cWriter(c_file: *std.c.FILE) CWriter { + return .{ .context = c_file }; +} + +fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { + const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); + if (amt_written >= 0) return amt_written; + switch (std.c._errno().*) { + 0 => unreachable, + os.EINVAL => unreachable, + os.EFAULT => unreachable, + os.EAGAIN => unreachable, // this is a blocking API + os.EBADF => unreachable, // always a race condition + os.EDESTADDRREQ => unreachable, // connect was never called + os.EDQUOT => return error.DiskQuota, + os.EFBIG => return error.FileTooBig, + os.EIO => return error.InputOutput, + os.ENOSPC => return error.NoSpaceLeft, + os.EPERM => return error.AccessDenied, + os.EPIPE => return error.BrokenPipe, + else => |err| return os.unexpectedErrno(@intCast(usize, err)), + } +} + +test "" { + if (!builtin.link_libc) return error.SkipZigTest; + + const filename = "tmp_io_test_file.txt"; + const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; + defer { + _ = std.c.fclose(out_file); + std.fs.cwd().deleteFileZ(filename) catch {}; + } + + const writer = cWriter(out_file); + try writer.print("hi: {}\n", .{@as(i32, 123)}); +} diff --git a/lib/std/io/counting_out_stream.zig b/lib/std/io/counting_out_stream.zig index f5bd6634f3..5b0b35b4c5 100644 --- a/lib/std/io/counting_out_stream.zig +++ b/lib/std/io/counting_out_stream.zig @@ -1,39 +1,5 @@ -const std = @import("../std.zig"); -const io = std.io; -const testing = std.testing; +/// Deprecated: use `std.io.counting_writer.CountingWriter` +pub const CountingOutStream = @import("./counting_writer.zig").CountingWriter; -/// An OutStream that counts how many bytes has been written to it. -pub fn CountingOutStream(comptime OutStreamType: type) type { - return struct { - bytes_written: u64, - child_stream: OutStreamType, - - pub const Error = OutStreamType.Error; - pub const OutStream = io.OutStream(*Self, Error, write); - - const Self = @This(); - - pub fn write(self: *Self, bytes: []const u8) Error!usize { - const amt = try self.child_stream.write(bytes); - self.bytes_written += amt; - return amt; - } - - pub fn outStream(self: *Self) OutStream { - return .{ .context = self }; - } - }; -} - -pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) { - return .{ .bytes_written = 0, .child_stream = child_stream }; -} - -test "io.CountingOutStream" { - var counting_stream = countingOutStream(std.io.null_out_stream); - const stream = counting_stream.outStream(); - - const bytes = "yay" ** 100; - stream.writeAll(bytes) catch unreachable; - testing.expect(counting_stream.bytes_written == bytes.len); -} +/// Deprecated: use `std.io.counting_writer.countingWriter` +pub const countingOutStream = @import("./counting_writer.zig").countingWriter; diff --git a/lib/std/io/counting_writer.zig b/lib/std/io/counting_writer.zig new file mode 100644 index 0000000000..90e4580eea --- /dev/null +++ b/lib/std/io/counting_writer.zig @@ -0,0 +1,46 @@ +const std = @import("../std.zig"); +const io = std.io; +const testing = std.testing; + +/// A Writer that counts how many bytes has been written to it. +pub fn CountingWriter(comptime WriterType: type) type { + return struct { + bytes_written: u64, + child_stream: WriterType, + + pub const Error = WriterType.Error; + pub const Writer = io.Writer(*Self, Error, write); + /// Deprecated: use `Writer` + pub const OutStream = Writer; + + const Self = @This(); + + pub fn write(self: *Self, bytes: []const u8) Error!usize { + const amt = try self.child_stream.write(bytes); + self.bytes_written += amt; + return amt; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + /// Deprecated: use `writer` + pub fn outStream(self: *Self) OutStream { + return .{ .context = self }; + } + }; +} + +pub fn countingWriter(child_stream: var) CountingWriter(@TypeOf(child_stream)) { + return .{ .bytes_written = 0, .child_stream = child_stream }; +} + +test "io.CountingWriter" { + var counting_stream = countingWriter(std.io.null_writer); + const stream = counting_stream.writer(); + + const bytes = "yay" ** 100; + stream.writeAll(bytes) catch unreachable; + testing.expect(counting_stream.bytes_written == bytes.len); +} diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index 2f7d613aa6..9ed85978e9 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -18,7 +18,9 @@ pub fn FixedBufferStream(comptime Buffer: type) type { pub const GetSeekPosError = error{}; pub const InStream = io.InStream(*Self, ReadError, read); - pub const OutStream = io.OutStream(*Self, WriteError, write); + pub const Writer = io.Writer(*Self, WriteError, write); + /// Deprecated: use `Writer` + pub const OutStream = Writer; pub const SeekableStream = io.SeekableStream( *Self, @@ -36,6 +38,11 @@ pub fn FixedBufferStream(comptime Buffer: type) type { return .{ .context = self }; } + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + /// Deprecated: use `writer` pub fn outStream(self: *Self) OutStream { return .{ .context = self }; } @@ -126,7 +133,7 @@ fn NonSentinelSpan(comptime T: type) type { test "FixedBufferStream output" { var buf: [255]u8 = undefined; var fbs = fixedBufferStream(&buf); - const stream = fbs.outStream(); + const stream = fbs.writer(); try stream.print("{}{}!", .{ "Hello", "World" }); testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten()); @@ -136,19 +143,19 @@ test "FixedBufferStream output 2" { var buffer: [10]u8 = undefined; var fbs = fixedBufferStream(&buffer); - try fbs.outStream().writeAll("Hello"); + try fbs.writer().writeAll("Hello"); testing.expect(mem.eql(u8, fbs.getWritten(), "Hello")); - try fbs.outStream().writeAll("world"); + try fbs.writer().writeAll("world"); testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld")); - testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("!")); + testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("!")); testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld")); fbs.reset(); testing.expect(fbs.getWritten().len == 0); - testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("Hello world!")); + testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("Hello world!")); testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl")); } diff --git a/lib/std/io/multi_out_stream.zig b/lib/std/io/multi_out_stream.zig index cb8863b5a1..fa7a60de51 100644 --- a/lib/std/io/multi_out_stream.zig +++ b/lib/std/io/multi_out_stream.zig @@ -1,51 +1,5 @@ -const std = @import("../std.zig"); -const io = std.io; -const testing = std.testing; +/// Deprecated: use `std.io.multi_writer.MultiWriter` +pub const MultiOutStream = @import("./multi_writer.zig").MultiWriter; -/// Takes a tuple of streams, and constructs a new stream that writes to all of them -pub fn MultiOutStream(comptime OutStreams: type) type { - comptime var ErrSet = error{}; - inline for (@typeInfo(OutStreams).Struct.fields) |field| { - const StreamType = field.field_type; - ErrSet = ErrSet || StreamType.Error; - } - - return struct { - const Self = @This(); - - streams: OutStreams, - - pub const Error = ErrSet; - pub const OutStream = io.OutStream(*Self, Error, write); - pub fn outStream(self: *Self) OutStream { - return .{ .context = self }; - } - - pub fn write(self: *Self, bytes: []const u8) Error!usize { - var batch = std.event.Batch(Error!void, self.streams.len, .auto_async).init(); - comptime var i = 0; - inline while (i < self.streams.len) : (i += 1) { - const stream = self.streams[i]; - // TODO: remove ptrCast: https://github.com/ziglang/zig/issues/5258 - batch.add(@ptrCast(anyframe->Error!void, &async stream.writeAll(bytes))); - } - try batch.wait(); - return bytes.len; - } - }; -} - -pub fn multiOutStream(streams: var) MultiOutStream(@TypeOf(streams)) { - return .{ .streams = streams }; -} - -test "MultiOutStream" { - var buf1: [255]u8 = undefined; - var fbs1 = io.fixedBufferStream(&buf1); - var buf2: [255]u8 = undefined; - var fbs2 = io.fixedBufferStream(&buf2); - var stream = multiOutStream(.{ fbs1.outStream(), fbs2.outStream() }); - try stream.outStream().print("HI", .{}); - testing.expectEqualSlices(u8, "HI", fbs1.getWritten()); - testing.expectEqualSlices(u8, "HI", fbs2.getWritten()); -} +/// Deprecated: use `std.io.multi_writer.multiWriter` +pub const multiOutStream = @import("./multi_writer.zig").multiWriter; diff --git a/lib/std/io/multi_writer.zig b/lib/std/io/multi_writer.zig new file mode 100644 index 0000000000..02ed75eaaa --- /dev/null +++ b/lib/std/io/multi_writer.zig @@ -0,0 +1,59 @@ +const std = @import("../std.zig"); +const io = std.io; +const testing = std.testing; + +/// Takes a tuple of streams, and constructs a new stream that writes to all of them +pub fn MultiWriter(comptime Writers: type) type { + comptime var ErrSet = error{}; + inline for (@typeInfo(Writers).Struct.fields) |field| { + const StreamType = field.field_type; + ErrSet = ErrSet || StreamType.Error; + } + + return struct { + const Self = @This(); + + streams: Writers, + + pub const Error = ErrSet; + pub const Writer = io.Writer(*Self, Error, write); + /// Deprecated: use `Writer` + pub const OutStream = Writer; + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + /// Deprecated: use `writer` + pub fn outStream(self: *Self) OutStream { + return .{ .context = self }; + } + + pub fn write(self: *Self, bytes: []const u8) Error!usize { + var batch = std.event.Batch(Error!void, self.streams.len, .auto_async).init(); + comptime var i = 0; + inline while (i < self.streams.len) : (i += 1) { + const stream = self.streams[i]; + // TODO: remove ptrCast: https://github.com/ziglang/zig/issues/5258 + batch.add(@ptrCast(anyframe->Error!void, &async stream.writeAll(bytes))); + } + try batch.wait(); + return bytes.len; + } + }; +} + +pub fn multiWriter(streams: var) MultiWriter(@TypeOf(streams)) { + return .{ .streams = streams }; +} + +test "MultiWriter" { + var buf1: [255]u8 = undefined; + var fbs1 = io.fixedBufferStream(&buf1); + var buf2: [255]u8 = undefined; + var fbs2 = io.fixedBufferStream(&buf2); + var stream = multiWriter(.{ fbs1.writer(), fbs2.writer() }); + try stream.writer().print("HI", .{}); + testing.expectEqualSlices(u8, "HI", fbs1.getWritten()); + testing.expectEqualSlices(u8, "HI", fbs2.getWritten()); +} diff --git a/lib/std/io/out_stream.zig b/lib/std/io/out_stream.zig index bf3ae791a1..a3fe3e32b5 100644 --- a/lib/std/io/out_stream.zig +++ b/lib/std/io/out_stream.zig @@ -1,85 +1,2 @@ -const std = @import("../std.zig"); -const builtin = std.builtin; -const mem = std.mem; - -pub fn OutStream( - comptime Context: type, - comptime WriteError: type, - comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize, -) type { - return struct { - context: Context, - - const Self = @This(); - pub const Error = WriteError; - - pub fn write(self: Self, bytes: []const u8) Error!usize { - return writeFn(self.context, bytes); - } - - pub fn writeAll(self: Self, bytes: []const u8) Error!void { - var index: usize = 0; - while (index != bytes.len) { - index += try self.write(bytes[index..]); - } - } - - pub fn print(self: Self, comptime format: []const u8, args: var) Error!void { - return std.fmt.format(self, format, args); - } - - pub fn writeByte(self: Self, byte: u8) Error!void { - const array = [1]u8{byte}; - return self.writeAll(&array); - } - - pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void { - var bytes: [256]u8 = undefined; - mem.set(u8, bytes[0..], byte); - - var remaining: usize = n; - while (remaining > 0) { - const to_write = std.math.min(remaining, bytes.len); - try self.writeAll(bytes[0..to_write]); - remaining -= to_write; - } - } - - /// Write a native-endian integer. - /// TODO audit non-power-of-two int sizes - pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntNative(T, &bytes, value); - return self.writeAll(&bytes); - } - - /// Write a foreign-endian integer. - /// TODO audit non-power-of-two int sizes - pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntForeign(T, &bytes, value); - return self.writeAll(&bytes); - } - - /// TODO audit non-power-of-two int sizes - pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntLittle(T, &bytes, value); - return self.writeAll(&bytes); - } - - /// TODO audit non-power-of-two int sizes - pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntBig(T, &bytes, value); - return self.writeAll(&bytes); - } - - /// TODO audit non-power-of-two int sizes - pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeInt(T, &bytes, value, endian); - return self.writeAll(&bytes); - } - }; -} +/// Deprecated: use `std.io.writer.Writer` +pub const OutStream = @import("./writer.zig").Writer; diff --git a/lib/std/io/writer.zig b/lib/std/io/writer.zig new file mode 100644 index 0000000000..659ba2703e --- /dev/null +++ b/lib/std/io/writer.zig @@ -0,0 +1,85 @@ +const std = @import("../std.zig"); +const builtin = std.builtin; +const mem = std.mem; + +pub fn Writer( + comptime Context: type, + comptime WriteError: type, + comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize, +) type { + return struct { + context: Context, + + const Self = @This(); + pub const Error = WriteError; + + pub fn write(self: Self, bytes: []const u8) Error!usize { + return writeFn(self.context, bytes); + } + + pub fn writeAll(self: Self, bytes: []const u8) Error!void { + var index: usize = 0; + while (index != bytes.len) { + index += try self.write(bytes[index..]); + } + } + + pub fn print(self: Self, comptime format: []const u8, args: var) Error!void { + return std.fmt.format(self, format, args); + } + + pub fn writeByte(self: Self, byte: u8) Error!void { + const array = [1]u8{byte}; + return self.writeAll(&array); + } + + pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void { + var bytes: [256]u8 = undefined; + mem.set(u8, bytes[0..], byte); + + var remaining: usize = n; + while (remaining > 0) { + const to_write = std.math.min(remaining, bytes.len); + try self.writeAll(bytes[0..to_write]); + remaining -= to_write; + } + } + + /// Write a native-endian integer. + /// TODO audit non-power-of-two int sizes + pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntNative(T, &bytes, value); + return self.writeAll(&bytes); + } + + /// Write a foreign-endian integer. + /// TODO audit non-power-of-two int sizes + pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntForeign(T, &bytes, value); + return self.writeAll(&bytes); + } + + /// TODO audit non-power-of-two int sizes + pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntLittle(T, &bytes, value); + return self.writeAll(&bytes); + } + + /// TODO audit non-power-of-two int sizes + pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntBig(T, &bytes, value); + return self.writeAll(&bytes); + } + + /// TODO audit non-power-of-two int sizes + pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeInt(T, &bytes, value, endian); + return self.writeAll(&bytes); + } + }; +} diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 54fa2e39f3..1a30e46ee0 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -49,7 +49,7 @@ pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf( .source_index = 0, .source = tree.source, }; - const my_stream_stream: std.io.OutStream(*MyStream, MyStream.StreamError, MyStream.write) = .{ + const my_stream_stream: std.io.Writer(*MyStream, MyStream.StreamError, MyStream.write) = .{ .context = &my_stream, };