| author | |
| committer | |
| log | a282ac7a9119cd0961ea17e29c4a0e9b0baf60d0 |
| tree | a5e8b62dafcdc4fa17ad0dc6ccedf3865a2dd4bc |
| parent | 4302f276ed3083b4f261f9e50a9546c9877d2785 |
18 files changed, 757 insertions(+), 681 deletions(-)
lib/std/elf.zig-1| ... | ... | @@ -5,7 +5,6 @@ const os = std.os; |
| 5 | 5 | const math = std.math; |
| 6 | 6 | const mem = std.mem; |
| 7 | 7 | const debug = std.debug; |
| 8 | const InStream = std.stream.InStream; | |
| 9 | 8 | const File = std.fs.File; |
| 10 | 9 | |
| 11 | 10 | pub const AT_NULL = 0; |
lib/std/fifo.zig+9-5| ... | ... | @@ -216,11 +216,15 @@ pub fn LinearFifo( |
| 216 | 216 | } |
| 217 | 217 | |
| 218 | 218 | /// Same as `read` except it returns an error union |
| 219 | /// The purpose of this function existing is to match `std.io.InStream` API. | |
| 219 | /// The purpose of this function existing is to match `std.io.Reader` API. | |
| 220 | 220 | fn readFn(self: *Self, dest: []u8) error{}!usize { |
| 221 | 221 | return self.read(dest); |
| 222 | 222 | } |
| 223 | 223 | |
| 224 | pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) { | |
| 225 | return .{ .context = self }; | |
| 226 | } | |
| 227 | /// Deprecated: `use reader` | |
| 224 | 228 | pub fn inStream(self: *Self) std.io.InStream(*Self, error{}, readFn) { |
| 225 | 229 | return .{ .context = self }; |
| 226 | 230 | } |
| ... | ... | @@ -431,10 +435,10 @@ test "LinearFifo(u8, .Dynamic)" { |
| 431 | 435 | { |
| 432 | 436 | try fifo.outStream().writeAll("This is a test"); |
| 433 | 437 | var result: [30]u8 = undefined; |
| 434 | testing.expectEqualSlices(u8, "This", (try fifo.inStream().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 435 | testing.expectEqualSlices(u8, "is", (try fifo.inStream().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 436 | testing.expectEqualSlices(u8, "a", (try fifo.inStream().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 437 | testing.expectEqualSlices(u8, "test", (try fifo.inStream().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 438 | testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 439 | testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 440 | testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 441 | testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | |
| 438 | 442 | } |
| 439 | 443 | } |
| 440 | 444 |
lib/std/fs/file.zig+7-1| ... | ... | @@ -642,8 +642,14 @@ pub const File = struct { |
| 642 | 642 | } |
| 643 | 643 | } |
| 644 | 644 | |
| 645 | pub const InStream = io.InStream(File, ReadError, read); | |
| 645 | pub const Reader = io.Reader(File, ReadError, read); | |
| 646 | /// Deprecated: use `Reader` | |
| 647 | pub const InStream = Reader; | |
| 646 | 648 | |
| 649 | pub fn reader(file: File) io.Reader(File, ReadError, read) { | |
| 650 | return .{ .context = file }; | |
| 651 | } | |
| 652 | /// Deprecated: use `reader` | |
| 647 | 653 | pub fn inStream(file: File) io.InStream(File, ReadError, read) { |
| 648 | 654 | return .{ .context = file }; |
| 649 | 655 | } |
lib/std/io.zig+18-8| ... | ... | @@ -101,7 +101,9 @@ pub fn getStdIn() File { |
| 101 | 101 | }; |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | pub const InStream = @import("io/in_stream.zig").InStream; | |
| 104 | pub const Reader = @import("io/reader.zig").Reader; | |
| 105 | /// Deprecated: use `Reader` | |
| 106 | pub const InStream = Reader; | |
| 105 | 107 | pub const Writer = @import("io/writer.zig").Writer; |
| 106 | 108 | /// Deprecated: use `Writer` |
| 107 | 109 | pub const OutStream = Writer; |
| ... | ... | @@ -114,8 +116,12 @@ pub const BufferedOutStream = BufferedWriter; |
| 114 | 116 | /// Deprecated: use `bufferedWriter` |
| 115 | 117 | pub const bufferedOutStream = bufferedWriter; |
| 116 | 118 | |
| 117 | pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream; | |
| 118 | pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream; | |
| 119 | pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader; | |
| 120 | pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader; | |
| 121 | /// Deprecated: use `BufferedReader` | |
| 122 | pub const BufferedInStream = BufferedReader; | |
| 123 | /// Deprecated: use `bufferedReader` | |
| 124 | pub const bufferedInStream = bufferedReader; | |
| 119 | 125 | |
| 120 | 126 | pub const PeekStream = @import("io/peek_stream.zig").PeekStream; |
| 121 | 127 | pub const peekStream = @import("io/peek_stream.zig").peekStream; |
| ... | ... | @@ -144,8 +150,12 @@ pub const MultiOutStream = MultiWriter; |
| 144 | 150 | /// Deprecated: use `multiWriter` |
| 145 | 151 | pub const multiOutStream = multiWriter; |
| 146 | 152 | |
| 147 | pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream; | |
| 148 | pub const bitInStream = @import("io/bit_in_stream.zig").bitInStream; | |
| 153 | pub const BitReader = @import("io/bit_reader.zig").BitReader; | |
| 154 | pub const bitReader = @import("io/bit_reader.zig").bitReader; | |
| 155 | /// Deprecated: use `BitReader` | |
| 156 | pub const BitInStream = BitReader; | |
| 157 | /// Deprecated: use `bitReader` | |
| 158 | pub const bitInStream = bitReader; | |
| 149 | 159 | |
| 150 | 160 | pub const BitWriter = @import("io/bit_writer.zig").BitWriter; |
| 151 | 161 | pub const bitWriter = @import("io/bit_writer.zig").bitWriter; |
| ... | ... | @@ -184,15 +194,15 @@ test "null_writer" { |
| 184 | 194 | } |
| 185 | 195 | |
| 186 | 196 | test "" { |
| 187 | _ = @import("io/bit_in_stream.zig"); | |
| 197 | _ = @import("io/bit_reader.zig"); | |
| 188 | 198 | _ = @import("io/bit_writer.zig"); |
| 189 | 199 | _ = @import("io/buffered_atomic_file.zig"); |
| 190 | _ = @import("io/buffered_in_stream.zig"); | |
| 200 | _ = @import("io/buffered_reader.zig"); | |
| 191 | 201 | _ = @import("io/buffered_writer.zig"); |
| 192 | 202 | _ = @import("io/c_writer.zig"); |
| 193 | 203 | _ = @import("io/counting_writer.zig"); |
| 194 | 204 | _ = @import("io/fixed_buffer_stream.zig"); |
| 195 | _ = @import("io/in_stream.zig"); | |
| 205 | _ = @import("io/reader.zig"); | |
| 196 | 206 | _ = @import("io/writer.zig"); |
| 197 | 207 | _ = @import("io/peek_stream.zig"); |
| 198 | 208 | _ = @import("io/seekable_stream.zig"); |
lib/std/io/bit_in_stream.zig+4-242| ... | ... | @@ -1,243 +1,5 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = std.builtin; | |
| 3 | const io = std.io; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const testing = std.testing; | |
| 6 | const trait = std.meta.trait; | |
| 7 | const meta = std.meta; | |
| 8 | const math = std.math; | |
| 1 | /// Deprecated: use `std.io.bit_reader.BitReader` | |
| 2 | pub const BitInStream = @import("./bit_reader.zig").BitReader; | |
| 9 | 3 | |
| 10 | /// Creates a stream which allows for reading bit fields from another stream | |
| 11 | pub 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.Int(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 | ||
| 164 | pub 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 | ||
| 171 | test "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 | } | |
| 4 | /// Deprecated: use `std.io.bit_reader.bitReader` | |
| 5 | pub const bitInStream = @import("./bit_reader.zig").bitReader; |
lib/std/io/bit_reader.zig created+250| ... | ... | @@ -0,0 +1,250 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = std.builtin; | |
| 3 | const io = std.io; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const testing = std.testing; | |
| 6 | const trait = std.meta.trait; | |
| 7 | const meta = std.meta; | |
| 8 | const math = std.math; | |
| 9 | ||
| 10 | /// Creates a stream which allows for reading bit fields from another stream | |
| 11 | pub fn BitReader(endian: builtin.Endian, comptime ReaderType: type) type { | |
| 12 | return struct { | |
| 13 | forward_reader: ReaderType, | |
| 14 | bit_buffer: u7, | |
| 15 | bit_count: u3, | |
| 16 | ||
| 17 | pub const Error = ReaderType.Error; | |
| 18 | pub const Reader = io.Reader(*Self, Error, read); | |
| 19 | /// Deprecated: use `Reader` | |
| 20 | pub const InStream = io.InStream(*Self, Error, read); | |
| 21 | ||
| 22 | const Self = @This(); | |
| 23 | const u8_bit_count = comptime meta.bitCount(u8); | |
| 24 | const u7_bit_count = comptime meta.bitCount(u7); | |
| 25 | const u4_bit_count = comptime meta.bitCount(u4); | |
| 26 | ||
| 27 | pub fn init(forward_reader: ReaderType) Self { | |
| 28 | return Self{ | |
| 29 | .forward_reader = forward_reader, | |
| 30 | .bit_buffer = 0, | |
| 31 | .bit_count = 0, | |
| 32 | }; | |
| 33 | } | |
| 34 | ||
| 35 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | |
| 36 | /// containing them in the least significant end, returning an error if the | |
| 37 | /// specified number of bits could not be read. | |
| 38 | pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U { | |
| 39 | var n: usize = undefined; | |
| 40 | const result = try self.readBits(U, bits, &n); | |
| 41 | if (n < bits) return error.EndOfStream; | |
| 42 | return result; | |
| 43 | } | |
| 44 | ||
| 45 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | |
| 46 | /// containing them in the least significant end. The number of bits successfully | |
| 47 | /// read is placed in `out_bits`, as reaching the end of the stream is not an error. | |
| 48 | pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { | |
| 49 | comptime assert(trait.isUnsignedInt(U)); | |
| 50 | ||
| 51 | //by extending the buffer to a minimum of u8 we can cover a number of edge cases | |
| 52 | // related to shifting and casting. | |
| 53 | const u_bit_count = comptime meta.bitCount(U); | |
| 54 | const buf_bit_count = bc: { | |
| 55 | assert(u_bit_count >= bits); | |
| 56 | break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; | |
| 57 | }; | |
| 58 | const Buf = std.meta.Int(false, buf_bit_count); | |
| 59 | const BufShift = math.Log2Int(Buf); | |
| 60 | ||
| 61 | out_bits.* = @as(usize, 0); | |
| 62 | if (U == u0 or bits == 0) return 0; | |
| 63 | var out_buffer = @as(Buf, 0); | |
| 64 | ||
| 65 | if (self.bit_count > 0) { | |
| 66 | const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; | |
| 67 | const shift = u7_bit_count - n; | |
| 68 | switch (endian) { | |
| 69 | .Big => { | |
| 70 | out_buffer = @as(Buf, self.bit_buffer >> shift); | |
| 71 | if (n >= u7_bit_count) | |
| 72 | self.bit_buffer = 0 | |
| 73 | else | |
| 74 | self.bit_buffer <<= n; | |
| 75 | }, | |
| 76 | .Little => { | |
| 77 | const value = (self.bit_buffer << shift) >> shift; | |
| 78 | out_buffer = @as(Buf, value); | |
| 79 | if (n >= u7_bit_count) | |
| 80 | self.bit_buffer = 0 | |
| 81 | else | |
| 82 | self.bit_buffer >>= n; | |
| 83 | }, | |
| 84 | } | |
| 85 | self.bit_count -= n; | |
| 86 | out_bits.* = n; | |
| 87 | } | |
| 88 | //at this point we know bit_buffer is empty | |
| 89 | ||
| 90 | //copy bytes until we have enough bits, then leave the rest in bit_buffer | |
| 91 | while (out_bits.* < bits) { | |
| 92 | const n = bits - out_bits.*; | |
| 93 | const next_byte = self.forward_reader.readByte() catch |err| { | |
| 94 | if (err == error.EndOfStream) { | |
| 95 | return @intCast(U, out_buffer); | |
| 96 | } | |
| 97 | //@BUG: See #1810. Not sure if the bug is that I have to do this for some | |
| 98 | // streams, or that I don't for streams with emtpy errorsets. | |
| 99 | return @errSetCast(Error, err); | |
| 100 | }; | |
| 101 | ||
| 102 | switch (endian) { | |
| 103 | .Big => { | |
| 104 | if (n >= u8_bit_count) { | |
| 105 | out_buffer <<= @intCast(u3, u8_bit_count - 1); | |
| 106 | out_buffer <<= 1; | |
| 107 | out_buffer |= @as(Buf, next_byte); | |
| 108 | out_bits.* += u8_bit_count; | |
| 109 | continue; | |
| 110 | } | |
| 111 | ||
| 112 | const shift = @intCast(u3, u8_bit_count - n); | |
| 113 | out_buffer <<= @intCast(BufShift, n); | |
| 114 | out_buffer |= @as(Buf, next_byte >> shift); | |
| 115 | out_bits.* += n; | |
| 116 | self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1)); | |
| 117 | self.bit_count = shift; | |
| 118 | }, | |
| 119 | .Little => { | |
| 120 | if (n >= u8_bit_count) { | |
| 121 | out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*); | |
| 122 | out_bits.* += u8_bit_count; | |
| 123 | continue; | |
| 124 | } | |
| 125 | ||
| 126 | const shift = @intCast(u3, u8_bit_count - n); | |
| 127 | const value = (next_byte << shift) >> shift; | |
| 128 | out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*); | |
| 129 | out_bits.* += n; | |
| 130 | self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n)); | |
| 131 | self.bit_count = shift; | |
| 132 | }, | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | return @intCast(U, out_buffer); | |
| 137 | } | |
| 138 | ||
| 139 | pub fn alignToByte(self: *Self) void { | |
| 140 | self.bit_buffer = 0; | |
| 141 | self.bit_count = 0; | |
| 142 | } | |
| 143 | ||
| 144 | pub fn read(self: *Self, buffer: []u8) Error!usize { | |
| 145 | var out_bits: usize = undefined; | |
| 146 | var out_bits_total = @as(usize, 0); | |
| 147 | //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced | |
| 148 | if (self.bit_count > 0) { | |
| 149 | for (buffer) |*b, i| { | |
| 150 | b.* = try self.readBits(u8, u8_bit_count, &out_bits); | |
| 151 | out_bits_total += out_bits; | |
| 152 | } | |
| 153 | const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0); | |
| 154 | return (out_bits_total / u8_bit_count) + incomplete_byte; | |
| 155 | } | |
| 156 | ||
| 157 | return self.forward_reader.read(buffer); | |
| 158 | } | |
| 159 | ||
| 160 | pub fn reader(self: *Self) Reader { | |
| 161 | return .{ .context = self }; | |
| 162 | } | |
| 163 | ||
| 164 | /// Deprecated: use `reader` | |
| 165 | pub fn inStream(self: *Self) InStream { | |
| 166 | return .{ .context = self }; | |
| 167 | } | |
| 168 | }; | |
| 169 | } | |
| 170 | ||
| 171 | pub fn bitReader( | |
| 172 | comptime endian: builtin.Endian, | |
| 173 | underlying_stream: var, | |
| 174 | ) BitReader(endian, @TypeOf(underlying_stream)) { | |
| 175 | return BitReader(endian, @TypeOf(underlying_stream)).init(underlying_stream); | |
| 176 | } | |
| 177 | ||
| 178 | test "api coverage" { | |
| 179 | const mem_be = [_]u8{ 0b11001101, 0b00001011 }; | |
| 180 | const mem_le = [_]u8{ 0b00011101, 0b10010101 }; | |
| 181 | ||
| 182 | var mem_in_be = io.fixedBufferStream(&mem_be); | |
| 183 | var bit_stream_be = bitReader(.Big, mem_in_be.reader()); | |
| 184 | ||
| 185 | var out_bits: usize = undefined; | |
| 186 | ||
| 187 | const expect = testing.expect; | |
| 188 | const expectError = testing.expectError; | |
| 189 | ||
| 190 | expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); | |
| 191 | expect(out_bits == 1); | |
| 192 | expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); | |
| 193 | expect(out_bits == 2); | |
| 194 | expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); | |
| 195 | expect(out_bits == 3); | |
| 196 | expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); | |
| 197 | expect(out_bits == 4); | |
| 198 | expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); | |
| 199 | expect(out_bits == 5); | |
| 200 | expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); | |
| 201 | expect(out_bits == 1); | |
| 202 | ||
| 203 | mem_in_be.pos = 0; | |
| 204 | bit_stream_be.bit_count = 0; | |
| 205 | expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); | |
| 206 | expect(out_bits == 15); | |
| 207 | ||
| 208 | mem_in_be.pos = 0; | |
| 209 | bit_stream_be.bit_count = 0; | |
| 210 | expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); | |
| 211 | expect(out_bits == 16); | |
| 212 | ||
| 213 | _ = try bit_stream_be.readBits(u0, 0, &out_bits); | |
| 214 | ||
| 215 | expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); | |
| 216 | expect(out_bits == 0); | |
| 217 | expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); | |
| 218 | ||
| 219 | var mem_in_le = io.fixedBufferStream(&mem_le); | |
| 220 | var bit_stream_le = bitReader(.Little, mem_in_le.reader()); | |
| 221 | ||
| 222 | expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); | |
| 223 | expect(out_bits == 1); | |
| 224 | expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); | |
| 225 | expect(out_bits == 2); | |
| 226 | expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); | |
| 227 | expect(out_bits == 3); | |
| 228 | expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); | |
| 229 | expect(out_bits == 4); | |
| 230 | expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); | |
| 231 | expect(out_bits == 5); | |
| 232 | expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); | |
| 233 | expect(out_bits == 1); | |
| 234 | ||
| 235 | mem_in_le.pos = 0; | |
| 236 | bit_stream_le.bit_count = 0; | |
| 237 | expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); | |
| 238 | expect(out_bits == 15); | |
| 239 | ||
| 240 | mem_in_le.pos = 0; | |
| 241 | bit_stream_le.bit_count = 0; | |
| 242 | expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); | |
| 243 | expect(out_bits == 16); | |
| 244 | ||
| 245 | _ = try bit_stream_le.readBits(u0, 0, &out_bits); | |
| 246 | ||
| 247 | expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); | |
| 248 | expect(out_bits == 0); | |
| 249 | expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); | |
| 250 | } |
lib/std/io/buffered_in_stream.zig+4-85| ... | ... | @@ -1,86 +1,5 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const io = std.io; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 1 | /// Deprecated: use `std.io.buffered_reader.BufferedReader` | |
| 2 | pub const BufferedInStream = @import("./buffered_reader.zig").BufferedReader; | |
| 5 | 3 | |
| 6 | pub 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 | ||
| 44 | pub fn bufferedInStream(underlying_stream: var) BufferedInStream(4096, @TypeOf(underlying_stream)) { | |
| 45 | return .{ .unbuffered_in_stream = underlying_stream }; | |
| 46 | } | |
| 47 | ||
| 48 | test "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 | } | |
| 4 | /// Deprecated: use `std.io.buffered_reader.bufferedReader` | |
| 5 | pub const bufferedInStream = @import("./buffered_reader.zig").bufferedReader; |
lib/std/io/buffered_reader.zig created+93| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const io = std.io; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | ||
| 6 | pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) type { | |
| 7 | return struct { | |
| 8 | unbuffered_reader: ReaderType, | |
| 9 | fifo: FifoType = FifoType.init(), | |
| 10 | ||
| 11 | pub const Error = ReaderType.Error; | |
| 12 | pub const Reader = io.Reader(*Self, Error, read); | |
| 13 | /// Deprecated: use `Reader` | |
| 14 | pub const InStream = Reader; | |
| 15 | ||
| 16 | const Self = @This(); | |
| 17 | const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size }); | |
| 18 | ||
| 19 | pub fn read(self: *Self, dest: []u8) Error!usize { | |
| 20 | var dest_index: usize = 0; | |
| 21 | while (dest_index < dest.len) { | |
| 22 | const written = self.fifo.read(dest[dest_index..]); | |
| 23 | if (written == 0) { | |
| 24 | // fifo empty, fill it | |
| 25 | const writable = self.fifo.writableSlice(0); | |
| 26 | assert(writable.len > 0); | |
| 27 | const n = try self.unbuffered_reader.read(writable); | |
| 28 | if (n == 0) { | |
| 29 | // reading from the unbuffered stream returned nothing | |
| 30 | // so we have nothing left to read. | |
| 31 | return dest_index; | |
| 32 | } | |
| 33 | self.fifo.update(n); | |
| 34 | } | |
| 35 | dest_index += written; | |
| 36 | } | |
| 37 | return dest.len; | |
| 38 | } | |
| 39 | ||
| 40 | pub fn reader(self: *Self) Reader { | |
| 41 | return .{ .context = self }; | |
| 42 | } | |
| 43 | ||
| 44 | /// Deprecated: use `reader` | |
| 45 | pub fn inStream(self: *Self) InStream { | |
| 46 | return .{ .context = self }; | |
| 47 | } | |
| 48 | }; | |
| 49 | } | |
| 50 | ||
| 51 | pub fn bufferedReader(underlying_stream: var) BufferedReader(4096, @TypeOf(underlying_stream)) { | |
| 52 | return .{ .unbuffered_reader = underlying_stream }; | |
| 53 | } | |
| 54 | ||
| 55 | test "io.BufferedReader" { | |
| 56 | const OneByteReadReader = struct { | |
| 57 | str: []const u8, | |
| 58 | curr: usize, | |
| 59 | ||
| 60 | const Error = error{NoError}; | |
| 61 | const Self = @This(); | |
| 62 | const Reader = io.Reader(*Self, Error, read); | |
| 63 | ||
| 64 | fn init(str: []const u8) Self { | |
| 65 | return Self{ | |
| 66 | .str = str, | |
| 67 | .curr = 0, | |
| 68 | }; | |
| 69 | } | |
| 70 | ||
| 71 | fn read(self: *Self, dest: []u8) Error!usize { | |
| 72 | if (self.str.len <= self.curr or dest.len == 0) | |
| 73 | return 0; | |
| 74 | ||
| 75 | dest[0] = self.str[self.curr]; | |
| 76 | self.curr += 1; | |
| 77 | return 1; | |
| 78 | } | |
| 79 | ||
| 80 | fn reader(self: *Self) Reader { | |
| 81 | return .{ .context = self }; | |
| 82 | } | |
| 83 | }; | |
| 84 | ||
| 85 | const str = "This is a test"; | |
| 86 | var one_byte_stream = OneByteReadReader.init(str); | |
| 87 | var buf_reader = bufferedReader(one_byte_stream.reader()); | |
| 88 | const stream = buf_reader.reader(); | |
| 89 | ||
| 90 | const res = try stream.readAllAlloc(testing.allocator, str.len + 1); | |
| 91 | defer testing.allocator.free(res); | |
| 92 | testing.expectEqualSlices(u8, str, res); | |
| 93 | } |
lib/std/io/fixed_buffer_stream.zig+12-5| ... | ... | @@ -4,8 +4,8 @@ const testing = std.testing; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 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. | |
| 7 | /// This turns a byte buffer into an `io.Writer`, `io.Reader`, or `io.SeekableStream`. | |
| 8 | /// If the supplied byte buffer is const, then `io.Writer` is not available. | |
| 9 | 9 | pub fn FixedBufferStream(comptime Buffer: type) type { |
| 10 | 10 | return struct { |
| 11 | 11 | /// `Buffer` is either a `[]u8` or `[]const u8`. |
| ... | ... | @@ -17,6 +17,8 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 17 | 17 | pub const SeekError = error{}; |
| 18 | 18 | pub const GetSeekPosError = error{}; |
| 19 | 19 | |
| 20 | pub const Reader = io.Reader(*Self, ReadError, read); | |
| 21 | /// Deprecated: use `Reader` | |
| 20 | 22 | pub const InStream = io.InStream(*Self, ReadError, read); |
| 21 | 23 | pub const Writer = io.Writer(*Self, WriteError, write); |
| 22 | 24 | /// Deprecated: use `Writer` |
| ... | ... | @@ -34,6 +36,11 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 34 | 36 | |
| 35 | 37 | const Self = @This(); |
| 36 | 38 | |
| 39 | pub fn reader(self: *Self) Reader { | |
| 40 | return .{ .context = self }; | |
| 41 | } | |
| 42 | ||
| 43 | /// Deprecated: use `inStream` | |
| 37 | 44 | pub fn inStream(self: *Self) InStream { |
| 38 | 45 | return .{ .context = self }; |
| 39 | 46 | } |
| ... | ... | @@ -165,14 +172,14 @@ test "FixedBufferStream input" { |
| 165 | 172 | |
| 166 | 173 | var dest: [4]u8 = undefined; |
| 167 | 174 | |
| 168 | var read = try fbs.inStream().read(dest[0..4]); | |
| 175 | var read = try fbs.reader().read(dest[0..4]); | |
| 169 | 176 | testing.expect(read == 4); |
| 170 | 177 | testing.expect(mem.eql(u8, dest[0..4], bytes[0..4])); |
| 171 | 178 | |
| 172 | read = try fbs.inStream().read(dest[0..4]); | |
| 179 | read = try fbs.reader().read(dest[0..4]); | |
| 173 | 180 | testing.expect(read == 3); |
| 174 | 181 | testing.expect(mem.eql(u8, dest[0..3], bytes[4..7])); |
| 175 | 182 | |
| 176 | read = try fbs.inStream().read(dest[0..4]); | |
| 183 | read = try fbs.reader().read(dest[0..4]); | |
| 177 | 184 | testing.expect(read == 0); |
| 178 | 185 | } |
lib/std/io/in_stream.zig+2-296| ... | ... | @@ -1,296 +1,2 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = std.builtin; | |
| 3 | const math = std.math; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const mem = std.mem; | |
| 6 | const testing = std.testing; | |
| 7 | ||
| 8 | pub fn InStream( | |
| 9 | comptime Context: type, | |
| 10 | comptime ReadError: type, | |
| 11 | /// Returns the number of bytes read. It may be less than buffer.len. | |
| 12 | /// If the number of bytes read is 0, it means end of stream. | |
| 13 | /// End of stream is not an error condition. | |
| 14 | comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize, | |
| 15 | ) type { | |
| 16 | return struct { | |
| 17 | pub const Error = ReadError; | |
| 18 | ||
| 19 | context: Context, | |
| 20 | ||
| 21 | const Self = @This(); | |
| 22 | ||
| 23 | /// Returns the number of bytes read. It may be less than buffer.len. | |
| 24 | /// If the number of bytes read is 0, it means end of stream. | |
| 25 | /// End of stream is not an error condition. | |
| 26 | pub fn read(self: Self, buffer: []u8) Error!usize { | |
| 27 | return readFn(self.context, buffer); | |
| 28 | } | |
| 29 | ||
| 30 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it | |
| 31 | /// means the stream reached the end. Reaching the end of a stream is not an error | |
| 32 | /// condition. | |
| 33 | pub fn readAll(self: Self, buffer: []u8) Error!usize { | |
| 34 | var index: usize = 0; | |
| 35 | while (index != buffer.len) { | |
| 36 | const amt = try self.read(buffer[index..]); | |
| 37 | if (amt == 0) return index; | |
| 38 | index += amt; | |
| 39 | } | |
| 40 | return index; | |
| 41 | } | |
| 42 | ||
| 43 | /// Returns the number of bytes read. If the number read would be smaller than buf.len, | |
| 44 | /// error.EndOfStream is returned instead. | |
| 45 | pub fn readNoEof(self: Self, buf: []u8) !void { | |
| 46 | const amt_read = try self.readAll(buf); | |
| 47 | if (amt_read < buf.len) return error.EndOfStream; | |
| 48 | } | |
| 49 | ||
| 50 | pub const readAllBuffer = @compileError("deprecated; use readAllArrayList()"); | |
| 51 | ||
| 52 | /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found. | |
| 53 | /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned | |
| 54 | /// and the `std.ArrayList` has exactly `max_append_size` bytes appended. | |
| 55 | pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void { | |
| 56 | try array_list.ensureCapacity(math.min(max_append_size, 4096)); | |
| 57 | const original_len = array_list.items.len; | |
| 58 | var start_index: usize = original_len; | |
| 59 | while (true) { | |
| 60 | array_list.expandToCapacity(); | |
| 61 | const dest_slice = array_list.span()[start_index..]; | |
| 62 | const bytes_read = try self.readAll(dest_slice); | |
| 63 | start_index += bytes_read; | |
| 64 | ||
| 65 | if (start_index - original_len > max_append_size) { | |
| 66 | array_list.shrink(original_len + max_append_size); | |
| 67 | return error.StreamTooLong; | |
| 68 | } | |
| 69 | ||
| 70 | if (bytes_read != dest_slice.len) { | |
| 71 | array_list.shrink(start_index); | |
| 72 | return; | |
| 73 | } | |
| 74 | ||
| 75 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 76 | try array_list.ensureCapacity(start_index + 1); | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | /// Allocates enough memory to hold all the contents of the stream. If the allocated | |
| 81 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 82 | /// Caller owns returned memory. | |
| 83 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 84 | pub fn readAllAlloc(self: Self, allocator: *mem.Allocator, max_size: usize) ![]u8 { | |
| 85 | var array_list = std.ArrayList(u8).init(allocator); | |
| 86 | defer array_list.deinit(); | |
| 87 | try self.readAllArrayList(&array_list, max_size); | |
| 88 | return array_list.toOwnedSlice(); | |
| 89 | } | |
| 90 | ||
| 91 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. | |
| 92 | /// Does not include the delimiter in the result. | |
| 93 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the | |
| 94 | /// `std.ArrayList` is populated with `max_size` bytes from the stream. | |
| 95 | pub fn readUntilDelimiterArrayList( | |
| 96 | self: Self, | |
| 97 | array_list: *std.ArrayList(u8), | |
| 98 | delimiter: u8, | |
| 99 | max_size: usize, | |
| 100 | ) !void { | |
| 101 | array_list.shrink(0); | |
| 102 | while (true) { | |
| 103 | var byte: u8 = try self.readByte(); | |
| 104 | ||
| 105 | if (byte == delimiter) { | |
| 106 | return; | |
| 107 | } | |
| 108 | ||
| 109 | if (array_list.items.len == max_size) { | |
| 110 | return error.StreamTooLong; | |
| 111 | } | |
| 112 | ||
| 113 | try array_list.append(byte); | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | /// Allocates enough memory to read until `delimiter`. If the allocated | |
| 118 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 119 | /// Caller owns returned memory. | |
| 120 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 121 | pub fn readUntilDelimiterAlloc( | |
| 122 | self: Self, | |
| 123 | allocator: *mem.Allocator, | |
| 124 | delimiter: u8, | |
| 125 | max_size: usize, | |
| 126 | ) ![]u8 { | |
| 127 | var array_list = std.ArrayList(u8).init(allocator); | |
| 128 | defer array_list.deinit(); | |
| 129 | try self.readUntilDelimiterArrayList(&array_list, delimiter, max_size); | |
| 130 | return array_list.toOwnedSlice(); | |
| 131 | } | |
| 132 | ||
| 133 | /// Reads from the stream until specified byte is found. If the buffer is not | |
| 134 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | |
| 135 | /// If end-of-stream is found, returns the rest of the stream. If this | |
| 136 | /// function is called again after that, returns null. | |
| 137 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | |
| 138 | /// delimiter byte is not included in the returned slice. | |
| 139 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 { | |
| 140 | var index: usize = 0; | |
| 141 | while (true) { | |
| 142 | const byte = self.readByte() catch |err| switch (err) { | |
| 143 | error.EndOfStream => { | |
| 144 | if (index == 0) { | |
| 145 | return null; | |
| 146 | } else { | |
| 147 | return buf[0..index]; | |
| 148 | } | |
| 149 | }, | |
| 150 | else => |e| return e, | |
| 151 | }; | |
| 152 | ||
| 153 | if (byte == delimiter) return buf[0..index]; | |
| 154 | if (index >= buf.len) return error.StreamTooLong; | |
| 155 | ||
| 156 | buf[index] = byte; | |
| 157 | index += 1; | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | /// Reads from the stream until specified byte is found, discarding all data, | |
| 162 | /// including the delimiter. | |
| 163 | /// If end-of-stream is found, this function succeeds. | |
| 164 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void { | |
| 165 | while (true) { | |
| 166 | const byte = self.readByte() catch |err| switch (err) { | |
| 167 | error.EndOfStream => return, | |
| 168 | else => |e| return e, | |
| 169 | }; | |
| 170 | if (byte == delimiter) return; | |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | |
| 175 | pub fn readByte(self: Self) !u8 { | |
| 176 | var result: [1]u8 = undefined; | |
| 177 | const amt_read = try self.read(result[0..]); | |
| 178 | if (amt_read < 1) return error.EndOfStream; | |
| 179 | return result[0]; | |
| 180 | } | |
| 181 | ||
| 182 | /// Same as `readByte` except the returned byte is signed. | |
| 183 | pub fn readByteSigned(self: Self) !i8 { | |
| 184 | return @bitCast(i8, try self.readByte()); | |
| 185 | } | |
| 186 | ||
| 187 | /// Reads exactly `num_bytes` bytes and returns as an array. | |
| 188 | /// `num_bytes` must be comptime-known | |
| 189 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) ![num_bytes]u8 { | |
| 190 | var bytes: [num_bytes]u8 = undefined; | |
| 191 | try self.readNoEof(&bytes); | |
| 192 | return bytes; | |
| 193 | } | |
| 194 | ||
| 195 | /// Reads a native-endian integer | |
| 196 | pub fn readIntNative(self: Self, comptime T: type) !T { | |
| 197 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 198 | return mem.readIntNative(T, &bytes); | |
| 199 | } | |
| 200 | ||
| 201 | /// Reads a foreign-endian integer | |
| 202 | pub fn readIntForeign(self: Self, comptime T: type) !T { | |
| 203 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 204 | return mem.readIntForeign(T, &bytes); | |
| 205 | } | |
| 206 | ||
| 207 | pub fn readIntLittle(self: Self, comptime T: type) !T { | |
| 208 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 209 | return mem.readIntLittle(T, &bytes); | |
| 210 | } | |
| 211 | ||
| 212 | pub fn readIntBig(self: Self, comptime T: type) !T { | |
| 213 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 214 | return mem.readIntBig(T, &bytes); | |
| 215 | } | |
| 216 | ||
| 217 | pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T { | |
| 218 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 219 | return mem.readInt(T, &bytes, endian); | |
| 220 | } | |
| 221 | ||
| 222 | pub fn readVarInt(self: Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType { | |
| 223 | assert(size <= @sizeOf(ReturnType)); | |
| 224 | var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; | |
| 225 | const bytes = bytes_buf[0..size]; | |
| 226 | try self.readNoEof(bytes); | |
| 227 | return mem.readVarInt(ReturnType, bytes, endian); | |
| 228 | } | |
| 229 | ||
| 230 | pub fn skipBytes(self: Self, num_bytes: u64) !void { | |
| 231 | var i: u64 = 0; | |
| 232 | while (i < num_bytes) : (i += 1) { | |
| 233 | _ = try self.readByte(); | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 237 | /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice | |
| 238 | pub fn isBytes(self: Self, slice: []const u8) !bool { | |
| 239 | var i: usize = 0; | |
| 240 | var matches = true; | |
| 241 | while (i < slice.len) : (i += 1) { | |
| 242 | if (slice[i] != try self.readByte()) { | |
| 243 | matches = false; | |
| 244 | } | |
| 245 | } | |
| 246 | return matches; | |
| 247 | } | |
| 248 | ||
| 249 | pub fn readStruct(self: Self, comptime T: type) !T { | |
| 250 | // Only extern and packed structs have defined in-memory layout. | |
| 251 | comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); | |
| 252 | var res: [1]T = undefined; | |
| 253 | try self.readNoEof(mem.sliceAsBytes(res[0..])); | |
| 254 | return res[0]; | |
| 255 | } | |
| 256 | ||
| 257 | /// Reads an integer with the same size as the given enum's tag type. If the integer matches | |
| 258 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error. | |
| 259 | /// TODO optimization taking advantage of most fields being in order | |
| 260 | pub fn readEnum(self: Self, comptime Enum: type, endian: builtin.Endian) !Enum { | |
| 261 | const E = error{ | |
| 262 | /// An integer was read, but it did not match any of the tags in the supplied enum. | |
| 263 | InvalidValue, | |
| 264 | }; | |
| 265 | const type_info = @typeInfo(Enum).Enum; | |
| 266 | const tag = try self.readInt(type_info.tag_type, endian); | |
| 267 | ||
| 268 | inline for (std.meta.fields(Enum)) |field| { | |
| 269 | if (tag == field.value) { | |
| 270 | return @field(Enum, field.name); | |
| 271 | } | |
| 272 | } | |
| 273 | ||
| 274 | return E.InvalidValue; | |
| 275 | } | |
| 276 | }; | |
| 277 | } | |
| 278 | ||
| 279 | test "InStream" { | |
| 280 | var buf = "a\x02".*; | |
| 281 | const in_stream = std.io.fixedBufferStream(&buf).inStream(); | |
| 282 | testing.expect((try in_stream.readByte()) == 'a'); | |
| 283 | testing.expect((try in_stream.readEnum(enum(u8) { | |
| 284 | a = 0, | |
| 285 | b = 99, | |
| 286 | c = 2, | |
| 287 | d = 3, | |
| 288 | }, undefined)) == .c); | |
| 289 | testing.expectError(error.EndOfStream, in_stream.readByte()); | |
| 290 | } | |
| 291 | ||
| 292 | test "InStream.isBytes" { | |
| 293 | const in_stream = std.io.fixedBufferStream("foobar").inStream(); | |
| 294 | testing.expectEqual(true, try in_stream.isBytes("foo")); | |
| 295 | testing.expectEqual(false, try in_stream.isBytes("qux")); | |
| 296 | } | |
| 1 | /// Deprecated: use `std.io.reader.Reader` | |
| 2 | pub const InStream = @import("./reader.zig").Reader; |
lib/std/io/peek_stream.zig+20-13| ... | ... | @@ -5,24 +5,26 @@ const testing = std.testing; |
| 5 | 5 | |
| 6 | 6 | /// Creates a stream which supports 'un-reading' data, so that it can be read again. |
| 7 | 7 | /// This makes look-ahead style parsing much easier. |
| 8 | /// TODO merge this with `std.io.BufferedInStream`: https://github.com/ziglang/zig/issues/4501 | |
| 8 | /// TODO merge this with `std.io.BufferedReader`: https://github.com/ziglang/zig/issues/4501 | |
| 9 | 9 | pub fn PeekStream( |
| 10 | 10 | comptime buffer_type: std.fifo.LinearFifoBufferType, |
| 11 | comptime InStreamType: type, | |
| 11 | comptime ReaderType: type, | |
| 12 | 12 | ) type { |
| 13 | 13 | return struct { |
| 14 | unbuffered_in_stream: InStreamType, | |
| 14 | unbuffered_in_stream: ReaderType, | |
| 15 | 15 | fifo: FifoType, |
| 16 | 16 | |
| 17 | pub const Error = InStreamType.Error; | |
| 18 | pub const InStream = io.InStream(*Self, Error, read); | |
| 17 | pub const Error = ReaderType.Error; | |
| 18 | pub const Reader = io.Reader(*Self, Error, read); | |
| 19 | /// Deprecated: use `Reader` | |
| 20 | pub const InStream = Reader; | |
| 19 | 21 | |
| 20 | 22 | const Self = @This(); |
| 21 | 23 | const FifoType = std.fifo.LinearFifo(u8, buffer_type); |
| 22 | 24 | |
| 23 | 25 | pub usingnamespace switch (buffer_type) { |
| 24 | 26 | .Static => struct { |
| 25 | pub fn init(base: InStreamType) Self { | |
| 27 | pub fn init(base: ReaderType) Self { | |
| 26 | 28 | return .{ |
| 27 | 29 | .unbuffered_in_stream = base, |
| 28 | 30 | .fifo = FifoType.init(), |
| ... | ... | @@ -30,7 +32,7 @@ pub fn PeekStream( |
| 30 | 32 | } |
| 31 | 33 | }, |
| 32 | 34 | .Slice => struct { |
| 33 | pub fn init(base: InStreamType, buf: []u8) Self { | |
| 35 | pub fn init(base: ReaderType, buf: []u8) Self { | |
| 34 | 36 | return .{ |
| 35 | 37 | .unbuffered_in_stream = base, |
| 36 | 38 | .fifo = FifoType.init(buf), |
| ... | ... | @@ -38,7 +40,7 @@ pub fn PeekStream( |
| 38 | 40 | } |
| 39 | 41 | }, |
| 40 | 42 | .Dynamic => struct { |
| 41 | pub fn init(base: InStreamType, allocator: *mem.Allocator) Self { | |
| 43 | pub fn init(base: ReaderType, allocator: *mem.Allocator) Self { | |
| 42 | 44 | return .{ |
| 43 | 45 | .unbuffered_in_stream = base, |
| 44 | 46 | .fifo = FifoType.init(allocator), |
| ... | ... | @@ -65,6 +67,11 @@ pub fn PeekStream( |
| 65 | 67 | return dest_index; |
| 66 | 68 | } |
| 67 | 69 | |
| 70 | pub fn reader(self: *Self) Reader { | |
| 71 | return .{ .context = self }; | |
| 72 | } | |
| 73 | ||
| 74 | /// Deprecated: use `reader` | |
| 68 | 75 | pub fn inStream(self: *Self) InStream { |
| 69 | 76 | return .{ .context = self }; |
| 70 | 77 | } |
| ... | ... | @@ -81,31 +88,31 @@ pub fn peekStream( |
| 81 | 88 | test "PeekStream" { |
| 82 | 89 | const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; |
| 83 | 90 | var fbs = io.fixedBufferStream(&bytes); |
| 84 | var ps = peekStream(2, fbs.inStream()); | |
| 91 | var ps = peekStream(2, fbs.reader()); | |
| 85 | 92 | |
| 86 | 93 | var dest: [4]u8 = undefined; |
| 87 | 94 | |
| 88 | 95 | try ps.putBackByte(9); |
| 89 | 96 | try ps.putBackByte(10); |
| 90 | 97 | |
| 91 | var read = try ps.inStream().read(dest[0..4]); | |
| 98 | var read = try ps.reader().read(dest[0..4]); | |
| 92 | 99 | testing.expect(read == 4); |
| 93 | 100 | testing.expect(dest[0] == 10); |
| 94 | 101 | testing.expect(dest[1] == 9); |
| 95 | 102 | testing.expect(mem.eql(u8, dest[2..4], bytes[0..2])); |
| 96 | 103 | |
| 97 | read = try ps.inStream().read(dest[0..4]); | |
| 104 | read = try ps.reader().read(dest[0..4]); | |
| 98 | 105 | testing.expect(read == 4); |
| 99 | 106 | testing.expect(mem.eql(u8, dest[0..4], bytes[2..6])); |
| 100 | 107 | |
| 101 | read = try ps.inStream().read(dest[0..4]); | |
| 108 | read = try ps.reader().read(dest[0..4]); | |
| 102 | 109 | testing.expect(read == 2); |
| 103 | 110 | testing.expect(mem.eql(u8, dest[0..2], bytes[6..8])); |
| 104 | 111 | |
| 105 | 112 | try ps.putBackByte(11); |
| 106 | 113 | try ps.putBackByte(12); |
| 107 | 114 | |
| 108 | read = try ps.inStream().read(dest[0..4]); | |
| 115 | read = try ps.reader().read(dest[0..4]); | |
| 109 | 116 | testing.expect(read == 2); |
| 110 | 117 | testing.expect(dest[0] == 12); |
| 111 | 118 | testing.expect(dest[1] == 11); |
lib/std/io/reader.zig created+296| ... | ... | @@ -0,0 +1,296 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = std.builtin; | |
| 3 | const math = std.math; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const mem = std.mem; | |
| 6 | const testing = std.testing; | |
| 7 | ||
| 8 | pub fn Reader( | |
| 9 | comptime Context: type, | |
| 10 | comptime ReadError: type, | |
| 11 | /// Returns the number of bytes read. It may be less than buffer.len. | |
| 12 | /// If the number of bytes read is 0, it means end of stream. | |
| 13 | /// End of stream is not an error condition. | |
| 14 | comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize, | |
| 15 | ) type { | |
| 16 | return struct { | |
| 17 | pub const Error = ReadError; | |
| 18 | ||
| 19 | context: Context, | |
| 20 | ||
| 21 | const Self = @This(); | |
| 22 | ||
| 23 | /// Returns the number of bytes read. It may be less than buffer.len. | |
| 24 | /// If the number of bytes read is 0, it means end of stream. | |
| 25 | /// End of stream is not an error condition. | |
| 26 | pub fn read(self: Self, buffer: []u8) Error!usize { | |
| 27 | return readFn(self.context, buffer); | |
| 28 | } | |
| 29 | ||
| 30 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it | |
| 31 | /// means the stream reached the end. Reaching the end of a stream is not an error | |
| 32 | /// condition. | |
| 33 | pub fn readAll(self: Self, buffer: []u8) Error!usize { | |
| 34 | var index: usize = 0; | |
| 35 | while (index != buffer.len) { | |
| 36 | const amt = try self.read(buffer[index..]); | |
| 37 | if (amt == 0) return index; | |
| 38 | index += amt; | |
| 39 | } | |
| 40 | return index; | |
| 41 | } | |
| 42 | ||
| 43 | /// Returns the number of bytes read. If the number read would be smaller than buf.len, | |
| 44 | /// error.EndOfStream is returned instead. | |
| 45 | pub fn readNoEof(self: Self, buf: []u8) !void { | |
| 46 | const amt_read = try self.readAll(buf); | |
| 47 | if (amt_read < buf.len) return error.EndOfStream; | |
| 48 | } | |
| 49 | ||
| 50 | pub const readAllBuffer = @compileError("deprecated; use readAllArrayList()"); | |
| 51 | ||
| 52 | /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found. | |
| 53 | /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned | |
| 54 | /// and the `std.ArrayList` has exactly `max_append_size` bytes appended. | |
| 55 | pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void { | |
| 56 | try array_list.ensureCapacity(math.min(max_append_size, 4096)); | |
| 57 | const original_len = array_list.items.len; | |
| 58 | var start_index: usize = original_len; | |
| 59 | while (true) { | |
| 60 | array_list.expandToCapacity(); | |
| 61 | const dest_slice = array_list.span()[start_index..]; | |
| 62 | const bytes_read = try self.readAll(dest_slice); | |
| 63 | start_index += bytes_read; | |
| 64 | ||
| 65 | if (start_index - original_len > max_append_size) { | |
| 66 | array_list.shrink(original_len + max_append_size); | |
| 67 | return error.StreamTooLong; | |
| 68 | } | |
| 69 | ||
| 70 | if (bytes_read != dest_slice.len) { | |
| 71 | array_list.shrink(start_index); | |
| 72 | return; | |
| 73 | } | |
| 74 | ||
| 75 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 76 | try array_list.ensureCapacity(start_index + 1); | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | /// Allocates enough memory to hold all the contents of the stream. If the allocated | |
| 81 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 82 | /// Caller owns returned memory. | |
| 83 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 84 | pub fn readAllAlloc(self: Self, allocator: *mem.Allocator, max_size: usize) ![]u8 { | |
| 85 | var array_list = std.ArrayList(u8).init(allocator); | |
| 86 | defer array_list.deinit(); | |
| 87 | try self.readAllArrayList(&array_list, max_size); | |
| 88 | return array_list.toOwnedSlice(); | |
| 89 | } | |
| 90 | ||
| 91 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. | |
| 92 | /// Does not include the delimiter in the result. | |
| 93 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the | |
| 94 | /// `std.ArrayList` is populated with `max_size` bytes from the stream. | |
| 95 | pub fn readUntilDelimiterArrayList( | |
| 96 | self: Self, | |
| 97 | array_list: *std.ArrayList(u8), | |
| 98 | delimiter: u8, | |
| 99 | max_size: usize, | |
| 100 | ) !void { | |
| 101 | array_list.shrink(0); | |
| 102 | while (true) { | |
| 103 | var byte: u8 = try self.readByte(); | |
| 104 | ||
| 105 | if (byte == delimiter) { | |
| 106 | return; | |
| 107 | } | |
| 108 | ||
| 109 | if (array_list.items.len == max_size) { | |
| 110 | return error.StreamTooLong; | |
| 111 | } | |
| 112 | ||
| 113 | try array_list.append(byte); | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | /// Allocates enough memory to read until `delimiter`. If the allocated | |
| 118 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 119 | /// Caller owns returned memory. | |
| 120 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 121 | pub fn readUntilDelimiterAlloc( | |
| 122 | self: Self, | |
| 123 | allocator: *mem.Allocator, | |
| 124 | delimiter: u8, | |
| 125 | max_size: usize, | |
| 126 | ) ![]u8 { | |
| 127 | var array_list = std.ArrayList(u8).init(allocator); | |
| 128 | defer array_list.deinit(); | |
| 129 | try self.readUntilDelimiterArrayList(&array_list, delimiter, max_size); | |
| 130 | return array_list.toOwnedSlice(); | |
| 131 | } | |
| 132 | ||
| 133 | /// Reads from the stream until specified byte is found. If the buffer is not | |
| 134 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | |
| 135 | /// If end-of-stream is found, returns the rest of the stream. If this | |
| 136 | /// function is called again after that, returns null. | |
| 137 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | |
| 138 | /// delimiter byte is not included in the returned slice. | |
| 139 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 { | |
| 140 | var index: usize = 0; | |
| 141 | while (true) { | |
| 142 | const byte = self.readByte() catch |err| switch (err) { | |
| 143 | error.EndOfStream => { | |
| 144 | if (index == 0) { | |
| 145 | return null; | |
| 146 | } else { | |
| 147 | return buf[0..index]; | |
| 148 | } | |
| 149 | }, | |
| 150 | else => |e| return e, | |
| 151 | }; | |
| 152 | ||
| 153 | if (byte == delimiter) return buf[0..index]; | |
| 154 | if (index >= buf.len) return error.StreamTooLong; | |
| 155 | ||
| 156 | buf[index] = byte; | |
| 157 | index += 1; | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | /// Reads from the stream until specified byte is found, discarding all data, | |
| 162 | /// including the delimiter. | |
| 163 | /// If end-of-stream is found, this function succeeds. | |
| 164 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void { | |
| 165 | while (true) { | |
| 166 | const byte = self.readByte() catch |err| switch (err) { | |
| 167 | error.EndOfStream => return, | |
| 168 | else => |e| return e, | |
| 169 | }; | |
| 170 | if (byte == delimiter) return; | |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | |
| 175 | pub fn readByte(self: Self) !u8 { | |
| 176 | var result: [1]u8 = undefined; | |
| 177 | const amt_read = try self.read(result[0..]); | |
| 178 | if (amt_read < 1) return error.EndOfStream; | |
| 179 | return result[0]; | |
| 180 | } | |
| 181 | ||
| 182 | /// Same as `readByte` except the returned byte is signed. | |
| 183 | pub fn readByteSigned(self: Self) !i8 { | |
| 184 | return @bitCast(i8, try self.readByte()); | |
| 185 | } | |
| 186 | ||
| 187 | /// Reads exactly `num_bytes` bytes and returns as an array. | |
| 188 | /// `num_bytes` must be comptime-known | |
| 189 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) ![num_bytes]u8 { | |
| 190 | var bytes: [num_bytes]u8 = undefined; | |
| 191 | try self.readNoEof(&bytes); | |
| 192 | return bytes; | |
| 193 | } | |
| 194 | ||
| 195 | /// Reads a native-endian integer | |
| 196 | pub fn readIntNative(self: Self, comptime T: type) !T { | |
| 197 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 198 | return mem.readIntNative(T, &bytes); | |
| 199 | } | |
| 200 | ||
| 201 | /// Reads a foreign-endian integer | |
| 202 | pub fn readIntForeign(self: Self, comptime T: type) !T { | |
| 203 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 204 | return mem.readIntForeign(T, &bytes); | |
| 205 | } | |
| 206 | ||
| 207 | pub fn readIntLittle(self: Self, comptime T: type) !T { | |
| 208 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 209 | return mem.readIntLittle(T, &bytes); | |
| 210 | } | |
| 211 | ||
| 212 | pub fn readIntBig(self: Self, comptime T: type) !T { | |
| 213 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 214 | return mem.readIntBig(T, &bytes); | |
| 215 | } | |
| 216 | ||
| 217 | pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T { | |
| 218 | const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8); | |
| 219 | return mem.readInt(T, &bytes, endian); | |
| 220 | } | |
| 221 | ||
| 222 | pub fn readVarInt(self: Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType { | |
| 223 | assert(size <= @sizeOf(ReturnType)); | |
| 224 | var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; | |
| 225 | const bytes = bytes_buf[0..size]; | |
| 226 | try self.readNoEof(bytes); | |
| 227 | return mem.readVarInt(ReturnType, bytes, endian); | |
| 228 | } | |
| 229 | ||
| 230 | pub fn skipBytes(self: Self, num_bytes: u64) !void { | |
| 231 | var i: u64 = 0; | |
| 232 | while (i < num_bytes) : (i += 1) { | |
| 233 | _ = try self.readByte(); | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 237 | /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice | |
| 238 | pub fn isBytes(self: Self, slice: []const u8) !bool { | |
| 239 | var i: usize = 0; | |
| 240 | var matches = true; | |
| 241 | while (i < slice.len) : (i += 1) { | |
| 242 | if (slice[i] != try self.readByte()) { | |
| 243 | matches = false; | |
| 244 | } | |
| 245 | } | |
| 246 | return matches; | |
| 247 | } | |
| 248 | ||
| 249 | pub fn readStruct(self: Self, comptime T: type) !T { | |
| 250 | // Only extern and packed structs have defined in-memory layout. | |
| 251 | comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); | |
| 252 | var res: [1]T = undefined; | |
| 253 | try self.readNoEof(mem.sliceAsBytes(res[0..])); | |
| 254 | return res[0]; | |
| 255 | } | |
| 256 | ||
| 257 | /// Reads an integer with the same size as the given enum's tag type. If the integer matches | |
| 258 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error. | |
| 259 | /// TODO optimization taking advantage of most fields being in order | |
| 260 | pub fn readEnum(self: Self, comptime Enum: type, endian: builtin.Endian) !Enum { | |
| 261 | const E = error{ | |
| 262 | /// An integer was read, but it did not match any of the tags in the supplied enum. | |
| 263 | InvalidValue, | |
| 264 | }; | |
| 265 | const type_info = @typeInfo(Enum).Enum; | |
| 266 | const tag = try self.readInt(type_info.tag_type, endian); | |
| 267 | ||
| 268 | inline for (std.meta.fields(Enum)) |field| { | |
| 269 | if (tag == field.value) { | |
| 270 | return @field(Enum, field.name); | |
| 271 | } | |
| 272 | } | |
| 273 | ||
| 274 | return E.InvalidValue; | |
| 275 | } | |
| 276 | }; | |
| 277 | } | |
| 278 | ||
| 279 | test "Reader" { | |
| 280 | var buf = "a\x02".*; | |
| 281 | const reader = std.io.fixedBufferStream(&buf).reader(); | |
| 282 | testing.expect((try reader.readByte()) == 'a'); | |
| 283 | testing.expect((try reader.readEnum(enum(u8) { | |
| 284 | a = 0, | |
| 285 | b = 99, | |
| 286 | c = 2, | |
| 287 | d = 3, | |
| 288 | }, undefined)) == .c); | |
| 289 | testing.expectError(error.EndOfStream, reader.readByte()); | |
| 290 | } | |
| 291 | ||
| 292 | test "Reader.isBytes" { | |
| 293 | const reader = std.io.fixedBufferStream("foobar").reader(); | |
| 294 | testing.expectEqual(true, try reader.isBytes("foo")); | |
| 295 | testing.expectEqual(false, try reader.isBytes("qux")); | |
| 296 | } |
lib/std/io/seekable_stream.zig-1| ... | ... | @@ -1,5 +1,4 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const InStream = std.io.InStream; | |
| 3 | 2 | |
| 4 | 3 | pub fn SeekableStream( |
| 5 | 4 | comptime Context: type, |
lib/std/io/serialization.zig+9-9| ... | ... | @@ -24,16 +24,16 @@ pub const Packing = enum { |
| 24 | 24 | /// which will be called when the deserializer is used to deserialize |
| 25 | 25 | /// that type. It will pass a pointer to the type instance to deserialize |
| 26 | 26 | /// into and a pointer to the deserializer struct. |
| 27 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime InStreamType: type) type { | |
| 27 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type { | |
| 28 | 28 | return struct { |
| 29 | in_stream: if (packing == .Bit) io.BitInStream(endian, InStreamType) else InStreamType, | |
| 29 | in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType, | |
| 30 | 30 | |
| 31 | 31 | const Self = @This(); |
| 32 | 32 | |
| 33 | pub fn init(in_stream: InStreamType) Self { | |
| 33 | pub fn init(in_stream: ReaderType) Self { | |
| 34 | 34 | return Self{ |
| 35 | 35 | .in_stream = switch (packing) { |
| 36 | .Bit => io.bitInStream(endian, in_stream), | |
| 36 | .Bit => io.bitReader(endian, in_stream), | |
| 37 | 37 | .Byte => in_stream, |
| 38 | 38 | }, |
| 39 | 39 | }; |
| ... | ... | @@ -45,7 +45,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | 47 | //@BUG: inferred error issue. See: #1386 |
| 48 | fn deserializeInt(self: *Self, comptime T: type) (InStreamType.Error || error{EndOfStream})!T { | |
| 48 | fn deserializeInt(self: *Self, comptime T: type) (ReaderType.Error || error{EndOfStream})!T { | |
| 49 | 49 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); |
| 50 | 50 | |
| 51 | 51 | const u8_bit_count = 8; |
| ... | ... | @@ -368,7 +368,7 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packi |
| 368 | 368 | var _serializer = serializer(endian, packing, out.outStream()); |
| 369 | 369 | |
| 370 | 370 | var in = io.fixedBufferStream(&data_mem); |
| 371 | var _deserializer = deserializer(endian, packing, in.inStream()); | |
| 371 | var _deserializer = deserializer(endian, packing, in.reader()); | |
| 372 | 372 | |
| 373 | 373 | comptime var i = 0; |
| 374 | 374 | inline while (i <= max_test_bitsize) : (i += 1) { |
| ... | ... | @@ -425,7 +425,7 @@ fn testIntSerializerDeserializerInfNaN( |
| 425 | 425 | var _serializer = serializer(endian, packing, out.outStream()); |
| 426 | 426 | |
| 427 | 427 | var in = io.fixedBufferStream(&data_mem); |
| 428 | var _deserializer = deserializer(endian, packing, in.inStream()); | |
| 428 | var _deserializer = deserializer(endian, packing, in.reader()); | |
| 429 | 429 | |
| 430 | 430 | //@TODO: isInf/isNan not currently implemented for f128. |
| 431 | 431 | try _serializer.serialize(std.math.nan(f16)); |
| ... | ... | @@ -554,7 +554,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: |
| 554 | 554 | var _serializer = serializer(endian, packing, out.outStream()); |
| 555 | 555 | |
| 556 | 556 | var in = io.fixedBufferStream(&data_mem); |
| 557 | var _deserializer = deserializer(endian, packing, in.inStream()); | |
| 557 | var _deserializer = deserializer(endian, packing, in.reader()); | |
| 558 | 558 | |
| 559 | 559 | try _serializer.serialize(my_inst); |
| 560 | 560 | |
| ... | ... | @@ -589,7 +589,7 @@ fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !v |
| 589 | 589 | var _serializer = serializer(endian, packing, out.outStream()); |
| 590 | 590 | |
| 591 | 591 | var in = io.fixedBufferStream(&data_mem); |
| 592 | var _deserializer = deserializer(endian, packing, in.inStream()); | |
| 592 | var _deserializer = deserializer(endian, packing, in.reader()); | |
| 593 | 593 | |
| 594 | 594 | try _serializer.serialize(@as(u14, 3)); |
| 595 | 595 | testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(A)); |
lib/std/io/stream_source.zig+17-3| ... | ... | @@ -2,7 +2,7 @@ const std = @import("../std.zig"); |
| 2 | 2 | const io = std.io; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | |
| 5 | /// Provides `io.InStream`, `io.OutStream`, and `io.SeekableStream` for in-memory buffers as | |
| 5 | /// Provides `io.Reader`, `io.Writer`, and `io.SeekableStream` for in-memory buffers as | |
| 6 | 6 | /// well as files. |
| 7 | 7 | /// For memory sources, if the supplied byte buffer is const, then `io.OutStream` is not available. |
| 8 | 8 | /// The error set of the stream functions is the error set of the corresponding file functions. |
| ... | ... | @@ -16,8 +16,12 @@ pub const StreamSource = union(enum) { |
| 16 | 16 | pub const SeekError = std.fs.File.SeekError; |
| 17 | 17 | pub const GetSeekPosError = std.fs.File.GetPosError; |
| 18 | 18 | |
| 19 | pub const InStream = io.InStream(*StreamSource, ReadError, read); | |
| 20 | pub const OutStream = io.OutStream(*StreamSource, WriteError, write); | |
| 19 | pub const Reader = io.Reader(*StreamSource, ReadError, read); | |
| 20 | /// Deprecated: use `Reader` | |
| 21 | pub const InStream = Reader; | |
| 22 | pub const Writer = io.Writer(*StreamSource, WriteError, write); | |
| 23 | /// Deprecated: use `Writer` | |
| 24 | pub const OutStream = Writer; | |
| 21 | 25 | pub const SeekableStream = io.SeekableStream( |
| 22 | 26 | *StreamSource, |
| 23 | 27 | SeekError, |
| ... | ... | @@ -76,10 +80,20 @@ pub const StreamSource = union(enum) { |
| 76 | 80 | } |
| 77 | 81 | } |
| 78 | 82 | |
| 83 | pub fn reader(self: *StreamSource) Reader { | |
| 84 | return .{ .context = self }; | |
| 85 | } | |
| 86 | ||
| 87 | /// Deprecated: use `reader` | |
| 79 | 88 | pub fn inStream(self: *StreamSource) InStream { |
| 80 | 89 | return .{ .context = self }; |
| 81 | 90 | } |
| 82 | 91 | |
| 92 | pub fn writer(self: *StreamSource) Writer { | |
| 93 | return .{ .context = self }; | |
| 94 | } | |
| 95 | ||
| 96 | /// Deprecated: use `writer` | |
| 83 | 97 | pub fn outStream(self: *StreamSource) OutStream { |
| 84 | 98 | return .{ .context = self }; |
| 85 | 99 | } |
lib/std/io/test.zig+3-3| ... | ... | @@ -50,8 +50,8 @@ test "write a file, read it, then delete it" { |
| 50 | 50 | const expected_file_size: u64 = "begin".len + data.len + "end".len; |
| 51 | 51 | expectEqual(expected_file_size, file_size); |
| 52 | 52 | |
| 53 | var buf_stream = io.bufferedInStream(file.inStream()); | |
| 54 | const st = buf_stream.inStream(); | |
| 53 | var buf_stream = io.bufferedReader(file.reader()); | |
| 54 | const st = buf_stream.reader(); | |
| 55 | 55 | const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); |
| 56 | 56 | defer std.testing.allocator.free(contents); |
| 57 | 57 | |
| ... | ... | @@ -85,7 +85,7 @@ test "BitStreams with File Stream" { |
| 85 | 85 | var file = try tmp.dir.openFile(tmp_file_name, .{}); |
| 86 | 86 | defer file.close(); |
| 87 | 87 | |
| 88 | var bit_stream = io.bitInStream(builtin.endian, file.inStream()); | |
| 88 | var bit_stream = io.bitReader(builtin.endian, file.reader()); | |
| 89 | 89 | |
| 90 | 90 | var out_bits: usize = undefined; |
| 91 | 91 |
lib/std/pdb.zig+10-6| ... | ... | @@ -495,7 +495,7 @@ const Msf = struct { |
| 495 | 495 | streams: []MsfStream, |
| 496 | 496 | |
| 497 | 497 | fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void { |
| 498 | const in = file.inStream(); | |
| 498 | const in = file.reader(); | |
| 499 | 499 | |
| 500 | 500 | const superblock = try in.readStruct(SuperBlock); |
| 501 | 501 | |
| ... | ... | @@ -528,7 +528,7 @@ const Msf = struct { |
| 528 | 528 | ); |
| 529 | 529 | |
| 530 | 530 | const begin = self.directory.pos; |
| 531 | const stream_count = try self.directory.inStream().readIntLittle(u32); | |
| 531 | const stream_count = try self.directory.reader().readIntLittle(u32); | |
| 532 | 532 | const stream_sizes = try allocator.alloc(u32, stream_count); |
| 533 | 533 | defer allocator.free(stream_sizes); |
| 534 | 534 | |
| ... | ... | @@ -537,7 +537,7 @@ const Msf = struct { |
| 537 | 537 | // and must be taken into account when resolving stream indices. |
| 538 | 538 | const Nil = 0xFFFFFFFF; |
| 539 | 539 | for (stream_sizes) |*s, i| { |
| 540 | const size = try self.directory.inStream().readIntLittle(u32); | |
| 540 | const size = try self.directory.reader().readIntLittle(u32); | |
| 541 | 541 | s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize); |
| 542 | 542 | } |
| 543 | 543 | |
| ... | ... | @@ -552,7 +552,7 @@ const Msf = struct { |
| 552 | 552 | var blocks = try allocator.alloc(u32, size); |
| 553 | 553 | var j: u32 = 0; |
| 554 | 554 | while (j < size) : (j += 1) { |
| 555 | const block_id = try self.directory.inStream().readIntLittle(u32); | |
| 555 | const block_id = try self.directory.reader().readIntLittle(u32); | |
| 556 | 556 | const n = (block_id % superblock.BlockSize); |
| 557 | 557 | // 0 is for SuperBlock, 1 and 2 for FPMs. |
| 558 | 558 | if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos()) |
| ... | ... | @@ -647,7 +647,7 @@ const MsfStream = struct { |
| 647 | 647 | pub fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 { |
| 648 | 648 | var list = ArrayList(u8).init(allocator); |
| 649 | 649 | while (true) { |
| 650 | const byte = try self.inStream().readByte(); | |
| 650 | const byte = try self.reader().readByte(); | |
| 651 | 651 | if (byte == 0) { |
| 652 | 652 | return list.span(); |
| 653 | 653 | } |
| ... | ... | @@ -661,7 +661,7 @@ const MsfStream = struct { |
| 661 | 661 | var offset = self.pos % self.block_size; |
| 662 | 662 | |
| 663 | 663 | try self.in_file.seekTo(block * self.block_size + offset); |
| 664 | const in = self.in_file.inStream(); | |
| 664 | const in = self.in_file.reader(); | |
| 665 | 665 | |
| 666 | 666 | var size: usize = 0; |
| 667 | 667 | var rem_buffer = buffer; |
| ... | ... | @@ -708,6 +708,10 @@ const MsfStream = struct { |
| 708 | 708 | return block * self.block_size + offset; |
| 709 | 709 | } |
| 710 | 710 | |
| 711 | pub fn reader(self: *MsfStream) std.io.Reader(*MsfStream, Error, read) { | |
| 712 | return .{ .context = self }; | |
| 713 | } | |
| 714 | /// Deprecated: use `reader` | |
| 711 | 715 | pub fn inStream(self: *MsfStream) std.io.InStream(*MsfStream, Error, read) { |
| 712 | 716 | return .{ .context = self }; |
| 713 | 717 | } |
lib/std/process.zig+3-3| ... | ... | @@ -615,8 +615,8 @@ pub fn getUserInfo(name: []const u8) !UserInfo { |
| 615 | 615 | /// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else |
| 616 | 616 | /// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. |
| 617 | 617 | pub fn posixGetUserInfo(name: []const u8) !UserInfo { |
| 618 | var in_stream = try io.InStream.open("/etc/passwd", null); | |
| 619 | defer in_stream.close(); | |
| 618 | var reader = try io.Reader.open("/etc/passwd", null); | |
| 619 | defer reader.close(); | |
| 620 | 620 | |
| 621 | 621 | const State = enum { |
| 622 | 622 | Start, |
| ... | ... | @@ -633,7 +633,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { |
| 633 | 633 | var gid: u32 = 0; |
| 634 | 634 | |
| 635 | 635 | while (true) { |
| 636 | const amt_read = try in_stream.read(buf[0..]); | |
| 636 | const amt_read = try reader.read(buf[0..]); | |
| 637 | 637 | for (buf[0..amt_read]) |byte| { |
| 638 | 638 | switch (state) { |
| 639 | 639 | .Start => switch (byte) { |