| ... | ... | @@ -51,7 +51,7 @@ pub fn Reader( |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 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 | 55 | const amt_read = try self.readAll(buf); |
| 56 | 56 | if (amt_read < buf.len) return error.EndOfStream; |
| 57 | 57 | } |
| ... | ... | @@ -71,7 +71,7 @@ pub fn Reader( |
| 71 | 71 | array_list: *std.ArrayListAligned(u8, alignment), |
| 72 | 72 | max_append_size: usize, |
| 73 | 73 | ) !void { |
| 74 | | try array_list.ensureTotalCapacity(math.min(max_append_size, 4096)); |
| 74 | try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); |
| 75 | 75 | const original_len = array_list.items.len; |
| 76 | 76 | var start_index: usize = original_len; |
| 77 | 77 | while (true) { |
| ... | ... | @@ -103,9 +103,10 @@ pub fn Reader( |
| 103 | 103 | var array_list = std.ArrayList(u8).init(allocator); |
| 104 | 104 | defer array_list.deinit(); |
| 105 | 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 | 110 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. |
| 110 | 111 | /// Does not include the delimiter in the result. |
| 111 | 112 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the |
| ... | ... | @@ -117,21 +118,10 @@ pub fn Reader( |
| 117 | 118 | max_size: usize, |
| 118 | 119 | ) !void { |
| 119 | 120 | array_list.shrinkRetainingCapacity(0); |
| 120 | | while (true) { |
| 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 | | } |
| 121 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); |
| 133 | 122 | } |
| 134 | 123 | |
| 124 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. |
| 135 | 125 | /// Allocates enough memory to read until `delimiter`. If the allocated |
| 136 | 126 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. |
| 137 | 127 | /// Caller owns returned memory. |
| ... | ... | @@ -144,10 +134,11 @@ pub fn Reader( |
| 144 | 134 | ) ![]u8 { |
| 145 | 135 | var array_list = std.ArrayList(u8).init(allocator); |
| 146 | 136 | defer array_list.deinit(); |
| 147 | | try self.readUntilDelimiterArrayList(&array_list, delimiter, max_size); |
| 148 | | return array_list.toOwnedSlice(); |
| 137 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); |
| 138 | return try array_list.toOwnedSlice(); |
| 149 | 139 | } |
| 150 | 140 | |
| 141 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. |
| 151 | 142 | /// Reads from the stream until specified byte is found. If the buffer is not |
| 152 | 143 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. |
| 153 | 144 | /// If end-of-stream is found, `error.EndOfStream` is returned. |
| ... | ... | @@ -155,19 +146,14 @@ pub fn Reader( |
| 155 | 146 | /// delimiter byte is written to the output buffer but is not included |
| 156 | 147 | /// in the returned slice. |
| 157 | 148 | pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 { |
| 158 | | var index: usize = 0; |
| 159 | | while (true) { |
| 160 | | if (index >= buf.len) return error.StreamTooLong; |
| 161 | | |
| 162 | | const byte = try self.readByte(); |
| 163 | | buf[index] = byte; |
| 164 | | |
| 165 | | if (byte == delimiter) return buf[0..index]; |
| 166 | | |
| 167 | | index += 1; |
| 168 | | } |
| 149 | var fbs = std.io.fixedBufferStream(buf); |
| 150 | try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len); |
| 151 | const output = fbs.getWritten(); |
| 152 | buf[output.len] = delimiter; // emulating old behaviour |
| 153 | return output; |
| 169 | 154 | } |
| 170 | 155 | |
| 156 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead. |
| 171 | 157 | /// Allocates enough memory to read until `delimiter` or end-of-stream. |
| 172 | 158 | /// If the allocated memory would be greater than `max_size`, returns |
| 173 | 159 | /// `error.StreamTooLong`. If end-of-stream is found, returns the rest |
| ... | ... | @@ -183,17 +169,16 @@ pub fn Reader( |
| 183 | 169 | ) !?[]u8 { |
| 184 | 170 | var array_list = std.ArrayList(u8).init(allocator); |
| 185 | 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 | 173 | error.EndOfStream => if (array_list.items.len == 0) { |
| 188 | 174 | return null; |
| 189 | | } else { |
| 190 | | return try array_list.toOwnedSlice(); |
| 191 | 175 | }, |
| 192 | 176 | else => |e| return e, |
| 193 | 177 | }; |
| 194 | 178 | return try array_list.toOwnedSlice(); |
| 195 | 179 | } |
| 196 | 180 | |
| 181 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. |
| 197 | 182 | /// Reads from the stream until specified byte is found. If the buffer is not |
| 198 | 183 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. |
| 199 | 184 | /// If end-of-stream is found, returns the rest of the stream. If this |
| ... | ... | @@ -202,32 +187,46 @@ pub fn Reader( |
| 202 | 187 | /// delimiter byte is written to the output buffer but is not included |
| 203 | 188 | /// in the returned slice. |
| 204 | 189 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 { |
| 205 | | var index: usize = 0; |
| 206 | | while (true) { |
| 207 | | if (index >= buf.len) return error.StreamTooLong; |
| 208 | | |
| 209 | | const byte = self.readByte() catch |err| switch (err) { |
| 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; |
| 190 | var fbs = std.io.fixedBufferStream(buf); |
| 191 | self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) { |
| 192 | error.EndOfStream => if (fbs.getWritten().len == 0) { |
| 193 | return null; |
| 194 | }, |
| 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 | 226 | /// Reads from the stream until specified byte is found, discarding all data, |
| 228 | 227 | /// including the delimiter. |
| 229 | 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 | 230 | while (true) { |
| 232 | 231 | const byte = self.readByte() catch |err| switch (err) { |
| 233 | 232 | error.EndOfStream => return, |
| ... | ... | @@ -238,7 +237,7 @@ pub fn Reader( |
| 238 | 237 | } |
| 239 | 238 | |
| 240 | 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 | 241 | var result: [1]u8 = undefined; |
| 243 | 242 | const amt_read = try self.read(result[0..]); |
| 244 | 243 | if (amt_read < 1) return error.EndOfStream; |
| ... | ... | @@ -246,13 +245,13 @@ pub fn Reader( |
| 246 | 245 | } |
| 247 | 246 | |
| 248 | 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 | 249 | return @bitCast(i8, try self.readByte()); |
| 251 | 250 | } |
| 252 | 251 | |
| 253 | 252 | /// Reads exactly `num_bytes` bytes and returns as an array. |
| 254 | 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 | 255 | var bytes: [num_bytes]u8 = undefined; |
| 257 | 256 | try self.readNoEof(&bytes); |
| 258 | 257 | return bytes; |
| ... | ... | @@ -264,7 +263,7 @@ pub fn Reader( |
| 264 | 263 | self: Self, |
| 265 | 264 | comptime num_bytes: usize, |
| 266 | 265 | bounded: *std.BoundedArray(u8, num_bytes), |
| 267 | | ) !void { |
| 266 | ) Error!void { |
| 268 | 267 | while (bounded.len < num_bytes) { |
| 269 | 268 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); |
| 270 | 269 | if (bytes_read == 0) return; |
| ... | ... | @@ -273,20 +272,20 @@ pub fn Reader( |
| 273 | 272 | } |
| 274 | 273 | |
| 275 | 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 | 276 | var result = std.BoundedArray(u8, num_bytes){}; |
| 278 | 277 | try self.readIntoBoundedBytes(num_bytes, &result); |
| 279 | 278 | return result; |
| 280 | 279 | } |
| 281 | 280 | |
| 282 | 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 | 283 | const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8); |
| 285 | 284 | return mem.readIntNative(T, &bytes); |
| 286 | 285 | } |
| 287 | 286 | |
| 288 | 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 | 289 | const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8); |
| 291 | 290 | return mem.readIntForeign(T, &bytes); |
| 292 | 291 | } |
| ... | ... | @@ -361,7 +360,7 @@ pub fn Reader( |
| 361 | 360 | } |
| 362 | 361 | |
| 363 | 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 | 364 | /// TODO optimization taking advantage of most fields being in order |
| 366 | 365 | pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum { |
| 367 | 366 | const E = error{ |
| ... | ... | @@ -710,3 +709,22 @@ test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" |
| 710 | 709 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); |
| 711 | 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 | } |