| ... | @@ -51,7 +51,7 @@ pub fn Reader( | ... | @@ -51,7 +51,7 @@ pub fn Reader( |
| 51 | } | 51 | } |
| 52 | | 52 | |
| 53 | /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. | 53 | /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. |
| 54 | pub fn readNoEof(self: Self, buf: []u8) !void { | 54 | pub fn readNoEof(self: Self, buf: []u8) (Error || error{EndOfStream})!void { |
| 55 | const amt_read = try self.readAll(buf); | 55 | const amt_read = try self.readAll(buf); |
| 56 | if (amt_read < buf.len) return error.EndOfStream; | 56 | if (amt_read < buf.len) return error.EndOfStream; |
| 57 | } | 57 | } |
| ... | @@ -71,7 +71,7 @@ pub fn Reader( | ... | @@ -71,7 +71,7 @@ pub fn Reader( |
| 71 | array_list: *std.ArrayListAligned(u8, alignment), | 71 | array_list: *std.ArrayListAligned(u8, alignment), |
| 72 | max_append_size: usize, | 72 | max_append_size: usize, |
| 73 | ) !void { | 73 | ) !void { |
| 74 | try array_list.ensureTotalCapacity(math.min(max_append_size, 4096)); | 74 | try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); |
| 75 | const original_len = array_list.items.len; | 75 | const original_len = array_list.items.len; |
| 76 | var start_index: usize = original_len; | 76 | var start_index: usize = original_len; |
| 77 | while (true) { | 77 | while (true) { |
| ... | @@ -103,9 +103,10 @@ pub fn Reader( | ... | @@ -103,9 +103,10 @@ pub fn Reader( |
| 103 | var array_list = std.ArrayList(u8).init(allocator); | 103 | var array_list = std.ArrayList(u8).init(allocator); |
| 104 | defer array_list.deinit(); | 104 | defer array_list.deinit(); |
| 105 | try self.readAllArrayList(&array_list, max_size); | 105 | try self.readAllArrayList(&array_list, max_size); |
| 106 | return array_list.toOwnedSlice(); | 106 | return try array_list.toOwnedSlice(); |
| 107 | } | 107 | } |
| 108 | | 108 | |
| | 109 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. |
| 109 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. | 110 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. |
| 110 | /// Does not include the delimiter in the result. | 111 | /// Does not include the delimiter in the result. |
| 111 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the | 112 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the |
| ... | @@ -117,21 +118,10 @@ pub fn Reader( | ... | @@ -117,21 +118,10 @@ pub fn Reader( |
| 117 | max_size: usize, | 118 | max_size: usize, |
| 118 | ) !void { | 119 | ) !void { |
| 119 | array_list.shrinkRetainingCapacity(0); | 120 | array_list.shrinkRetainingCapacity(0); |
| 120 | while (true) { | 121 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); |
| 121 | if (array_list.items.len == max_size) { | | |
| 122 | return error.StreamTooLong; | | |
| 123 | } | | |
| 124 | | | |
| 125 | var byte: u8 = try self.readByte(); | | |
| 126 | | | |
| 127 | if (byte == delimiter) { | | |
| 128 | return; | | |
| 129 | } | | |
| 130 | | | |
| 131 | try array_list.append(byte); | | |
| 132 | } | | |
| 133 | } | 122 | } |
| 134 | | 123 | |
| | 124 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. |
| 135 | /// Allocates enough memory to read until `delimiter`. If the allocated | 125 | /// Allocates enough memory to read until `delimiter`. If the allocated |
| 136 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | 126 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. |
| 137 | /// Caller owns returned memory. | 127 | /// Caller owns returned memory. |
| ... | @@ -144,10 +134,11 @@ pub fn Reader( | ... | @@ -144,10 +134,11 @@ pub fn Reader( |
| 144 | ) ![]u8 { | 134 | ) ![]u8 { |
| 145 | var array_list = std.ArrayList(u8).init(allocator); | 135 | var array_list = std.ArrayList(u8).init(allocator); |
| 146 | defer array_list.deinit(); | 136 | defer array_list.deinit(); |
| 147 | try self.readUntilDelimiterArrayList(&array_list, delimiter, max_size); | 137 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); |
| 148 | return array_list.toOwnedSlice(); | 138 | return try array_list.toOwnedSlice(); |
| 149 | } | 139 | } |
| 150 | | 140 | |
| | 141 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. |
| 151 | /// Reads from the stream until specified byte is found. If the buffer is not | 142 | /// Reads from the stream until specified byte is found. If the buffer is not |
| 152 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | 143 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. |
| 153 | /// If end-of-stream is found, `error.EndOfStream` is returned. | 144 | /// If end-of-stream is found, `error.EndOfStream` is returned. |
| ... | @@ -155,19 +146,14 @@ pub fn Reader( | ... | @@ -155,19 +146,14 @@ pub fn Reader( |
| 155 | /// delimiter byte is written to the output buffer but is not included | 146 | /// delimiter byte is written to the output buffer but is not included |
| 156 | /// in the returned slice. | 147 | /// in the returned slice. |
| 157 | pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 { | 148 | pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 { |
| 158 | var index: usize = 0; | 149 | var fbs = std.io.fixedBufferStream(buf); |
| 159 | while (true) { | 150 | try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len); |
| 160 | if (index >= buf.len) return error.StreamTooLong; | 151 | const output = fbs.getWritten(); |
| 161 | | 152 | buf[output.len] = delimiter; // emulating old behaviour |
| 162 | const byte = try self.readByte(); | 153 | return output; |
| 163 | buf[index] = byte; | | |
| 164 | | | |
| 165 | if (byte == delimiter) return buf[0..index]; | | |
| 166 | | | |
| 167 | index += 1; | | |
| 168 | } | | |
| 169 | } | 154 | } |
| 170 | | 155 | |
| | 156 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead. |
| 171 | /// Allocates enough memory to read until `delimiter` or end-of-stream. | 157 | /// Allocates enough memory to read until `delimiter` or end-of-stream. |
| 172 | /// If the allocated memory would be greater than `max_size`, returns | 158 | /// If the allocated memory would be greater than `max_size`, returns |
| 173 | /// `error.StreamTooLong`. If end-of-stream is found, returns the rest | 159 | /// `error.StreamTooLong`. If end-of-stream is found, returns the rest |
| ... | @@ -183,17 +169,16 @@ pub fn Reader( | ... | @@ -183,17 +169,16 @@ pub fn Reader( |
| 183 | ) !?[]u8 { | 169 | ) !?[]u8 { |
| 184 | var array_list = std.ArrayList(u8).init(allocator); | 170 | var array_list = std.ArrayList(u8).init(allocator); |
| 185 | defer array_list.deinit(); | 171 | defer array_list.deinit(); |
| 186 | self.readUntilDelimiterArrayList(&array_list, delimiter, max_size) catch |err| switch (err) { | 172 | self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) { |
| 187 | error.EndOfStream => if (array_list.items.len == 0) { | 173 | error.EndOfStream => if (array_list.items.len == 0) { |
| 188 | return null; | 174 | return null; |
| 189 | } else { | | |
| 190 | return try array_list.toOwnedSlice(); | | |
| 191 | }, | 175 | }, |
| 192 | else => |e| return e, | 176 | else => |e| return e, |
| 193 | }; | 177 | }; |
| 194 | return try array_list.toOwnedSlice(); | 178 | return try array_list.toOwnedSlice(); |
| 195 | } | 179 | } |
| 196 | | 180 | |
| | 181 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. |
| 197 | /// Reads from the stream until specified byte is found. If the buffer is not | 182 | /// Reads from the stream until specified byte is found. If the buffer is not |
| 198 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | 183 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. |
| 199 | /// If end-of-stream is found, returns the rest of the stream. If this | 184 | /// If end-of-stream is found, returns the rest of the stream. If this |
| ... | @@ -202,32 +187,46 @@ pub fn Reader( | ... | @@ -202,32 +187,46 @@ pub fn Reader( |
| 202 | /// delimiter byte is written to the output buffer but is not included | 187 | /// delimiter byte is written to the output buffer but is not included |
| 203 | /// in the returned slice. | 188 | /// in the returned slice. |
| 204 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 { | 189 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 { |
| 205 | var index: usize = 0; | 190 | var fbs = std.io.fixedBufferStream(buf); |
| 206 | while (true) { | 191 | self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) { |
| 207 | if (index >= buf.len) return error.StreamTooLong; | 192 | error.EndOfStream => if (fbs.getWritten().len == 0) { |
| 208 | | 193 | return null; |
| 209 | const byte = self.readByte() catch |err| switch (err) { | 194 | }, |
| 210 | error.EndOfStream => { | | |
| 211 | if (index == 0) { | | |
| 212 | return null; | | |
| 213 | } else { | | |
| 214 | return buf[0..index]; | | |
| 215 | } | | |
| 216 | }, | | |
| 217 | else => |e| return e, | | |
| 218 | }; | | |
| 219 | buf[index] = byte; | | |
| 220 | | 195 | |
| 221 | if (byte == delimiter) return buf[0..index]; | 196 | else => |e| return e, |
| | 197 | }; |
| | 198 | const output = fbs.getWritten(); |
| | 199 | buf[output.len] = delimiter; // emulating old behaviour |
| | 200 | return output; |
| | 201 | } |
| 222 | | 202 | |
| 223 | index += 1; | 203 | /// Appends to the `writer` contents by reading from the stream until `delimiter` is found. |
| | 204 | /// Does not write the delimiter itself. |
| | 205 | /// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`, |
| | 206 | /// returns `error.StreamTooLong` and finishes appending. |
| | 207 | /// If `optional_max_size` is null, appending is unbounded. |
| | 208 | pub fn streamUntilDelimiter(self: Self, writer: anytype, delimiter: u8, optional_max_size: ?usize) (Error || error{ EndOfStream, StreamTooLong } || @TypeOf(writer).Error)!void { |
| | 209 | if (optional_max_size) |max_size| { |
| | 210 | for (0..max_size) |_| { |
| | 211 | const byte: u8 = try self.readByte(); // (Error || error{EndOfStream}) |
| | 212 | if (byte == delimiter) return; |
| | 213 | try writer.writeByte(byte); // @TypeOf(writer).Error |
| | 214 | } |
| | 215 | return error.StreamTooLong; |
| | 216 | } else { |
| | 217 | while (true) { |
| | 218 | const byte: u8 = try self.readByte(); // (Error || error{EndOfStream}) |
| | 219 | if (byte == delimiter) return; |
| | 220 | try writer.writeByte(byte); // @TypeOf(writer).Error |
| | 221 | } |
| | 222 | // Can not throw `error.StreamTooLong` since there are no boundary. |
| 224 | } | 223 | } |
| 225 | } | 224 | } |
| 226 | | 225 | |
| 227 | /// Reads from the stream until specified byte is found, discarding all data, | 226 | /// Reads from the stream until specified byte is found, discarding all data, |
| 228 | /// including the delimiter. | 227 | /// including the delimiter. |
| 229 | /// If end-of-stream is found, this function succeeds. | 228 | /// If end-of-stream is found, this function succeeds. |
| 230 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void { | 229 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void { |
| 231 | while (true) { | 230 | while (true) { |
| 232 | const byte = self.readByte() catch |err| switch (err) { | 231 | const byte = self.readByte() catch |err| switch (err) { |
| 233 | error.EndOfStream => return, | 232 | error.EndOfStream => return, |
| ... | @@ -238,7 +237,7 @@ pub fn Reader( | ... | @@ -238,7 +237,7 @@ pub fn Reader( |
| 238 | } | 237 | } |
| 239 | | 238 | |
| 240 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | 239 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. |
| 241 | pub fn readByte(self: Self) !u8 { | 240 | pub fn readByte(self: Self) (Error || error{EndOfStream})!u8 { |
| 242 | var result: [1]u8 = undefined; | 241 | var result: [1]u8 = undefined; |
| 243 | const amt_read = try self.read(result[0..]); | 242 | const amt_read = try self.read(result[0..]); |
| 244 | if (amt_read < 1) return error.EndOfStream; | 243 | if (amt_read < 1) return error.EndOfStream; |
| ... | @@ -246,13 +245,13 @@ pub fn Reader( | ... | @@ -246,13 +245,13 @@ pub fn Reader( |
| 246 | } | 245 | } |
| 247 | | 246 | |
| 248 | /// Same as `readByte` except the returned byte is signed. | 247 | /// Same as `readByte` except the returned byte is signed. |
| 249 | pub fn readByteSigned(self: Self) !i8 { | 248 | pub fn readByteSigned(self: Self) (Error || error{EndOfStream})!i8 { |
| 250 | return @bitCast(i8, try self.readByte()); | 249 | return @bitCast(i8, try self.readByte()); |
| 251 | } | 250 | } |
| 252 | | 251 | |
| 253 | /// Reads exactly `num_bytes` bytes and returns as an array. | 252 | /// Reads exactly `num_bytes` bytes and returns as an array. |
| 254 | /// `num_bytes` must be comptime-known | 253 | /// `num_bytes` must be comptime-known |
| 255 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) ![num_bytes]u8 { | 254 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) (Error || error{EndOfStream})![num_bytes]u8 { |
| 256 | var bytes: [num_bytes]u8 = undefined; | 255 | var bytes: [num_bytes]u8 = undefined; |
| 257 | try self.readNoEof(&bytes); | 256 | try self.readNoEof(&bytes); |
| 258 | return bytes; | 257 | return bytes; |
| ... | @@ -264,7 +263,7 @@ pub fn Reader( | ... | @@ -264,7 +263,7 @@ pub fn Reader( |
| 264 | self: Self, | 263 | self: Self, |
| 265 | comptime num_bytes: usize, | 264 | comptime num_bytes: usize, |
| 266 | bounded: *std.BoundedArray(u8, num_bytes), | 265 | bounded: *std.BoundedArray(u8, num_bytes), |
| 267 | ) !void { | 266 | ) Error!void { |
| 268 | while (bounded.len < num_bytes) { | 267 | while (bounded.len < num_bytes) { |
| 269 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); | 268 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); |
| 270 | if (bytes_read == 0) return; | 269 | if (bytes_read == 0) return; |
| ... | @@ -273,20 +272,20 @@ pub fn Reader( | ... | @@ -273,20 +272,20 @@ pub fn Reader( |
| 273 | } | 272 | } |
| 274 | | 273 | |
| 275 | /// Reads at most `num_bytes` and returns as a bounded array. | 274 | /// Reads at most `num_bytes` and returns as a bounded array. |
| 276 | pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) !std.BoundedArray(u8, num_bytes) { | 275 | pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) Error!std.BoundedArray(u8, num_bytes) { |
| 277 | var result = std.BoundedArray(u8, num_bytes){}; | 276 | var result = std.BoundedArray(u8, num_bytes){}; |
| 278 | try self.readIntoBoundedBytes(num_bytes, &result); | 277 | try self.readIntoBoundedBytes(num_bytes, &result); |
| 279 | return result; | 278 | return result; |
| 280 | } | 279 | } |
| 281 | | 280 | |
| 282 | /// Reads a native-endian integer | 281 | /// Reads a native-endian integer |
| 283 | pub fn readIntNative(self: Self, comptime T: type) !T { | 282 | pub fn readIntNative(self: Self, comptime T: type) (Error || error{EndOfStream})!T { |
| 284 | const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8); | 283 | const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8); |
| 285 | return mem.readIntNative(T, &bytes); | 284 | return mem.readIntNative(T, &bytes); |
| 286 | } | 285 | } |
| 287 | | 286 | |
| 288 | /// Reads a foreign-endian integer | 287 | /// Reads a foreign-endian integer |
| 289 | pub fn readIntForeign(self: Self, comptime T: type) !T { | 288 | pub fn readIntForeign(self: Self, comptime T: type) (Error || error{EndOfStream})!T { |
| 290 | const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8); | 289 | const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8); |
| 291 | return mem.readIntForeign(T, &bytes); | 290 | return mem.readIntForeign(T, &bytes); |
| 292 | } | 291 | } |
| ... | @@ -361,7 +360,7 @@ pub fn Reader( | ... | @@ -361,7 +360,7 @@ pub fn Reader( |
| 361 | } | 360 | } |
| 362 | | 361 | |
| 363 | /// Reads an integer with the same size as the given enum's tag type. If the integer matches | 362 | /// Reads an integer with the same size as the given enum's tag type. If the integer matches |
| 364 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error. | 363 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`. |
| 365 | /// TODO optimization taking advantage of most fields being in order | 364 | /// TODO optimization taking advantage of most fields being in order |
| 366 | pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum { | 365 | pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum { |
| 367 | const E = error{ | 366 | const E = error{ |
| ... | @@ -710,3 +709,22 @@ test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" | ... | @@ -710,3 +709,22 @@ test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" |
| 710 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | 709 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); |
| 711 | try std.testing.expectEqualStrings("12345", &buf); | 710 | try std.testing.expectEqualStrings("12345", &buf); |
| 712 | } | 711 | } |
| | 712 | |
| | 713 | test "Reader.streamUntilDelimiter writes all bytes without delimiter to the output" { |
| | 714 | const input_string = "some_string_with_delimiter!"; |
| | 715 | var input_fbs = std.io.fixedBufferStream(input_string); |
| | 716 | const reader = input_fbs.reader(); |
| | 717 | |
| | 718 | var output: [input_string.len]u8 = undefined; |
| | 719 | var output_fbs = std.io.fixedBufferStream(&output); |
| | 720 | const writer = output_fbs.writer(); |
| | 721 | |
| | 722 | try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len); |
| | 723 | try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten()); |
| | 724 | try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len)); |
| | 725 | |
| | 726 | input_fbs.reset(); |
| | 727 | output_fbs.reset(); |
| | 728 | |
| | 729 | try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5)); |
| | 730 | } |