| author | |
| committer | |
| log | a306bfcd8eec166906ce2839bc7ff86ba6335376 |
| tree | d1ae1ef50bb25e5a54829bcff658d922caa40edc |
| parent | 4d1e8ef1eb1217e0a997b9bfa164142d83b01c0a |
| parent | 9a09651019b24a32945f73dd7a69562f2cf31581 |
| signature |
std: add type-erased reader; base GenericReader on it8 files changed, 1022 insertions(+), 764 deletions(-)
CMakeLists.txt+1-1| ... | ... | @@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES |
| 265 | 265 | "${CMAKE_SOURCE_DIR}/lib/std/io/find_byte_writer.zig" |
| 266 | 266 | "${CMAKE_SOURCE_DIR}/lib/std/io/fixed_buffer_stream.zig" |
| 267 | 267 | "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig" |
| 268 | "${CMAKE_SOURCE_DIR}/lib/std/io/reader.zig" | |
| 268 | "${CMAKE_SOURCE_DIR}/lib/std/io/Reader.zig" | |
| 269 | 269 | "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig" |
| 270 | 270 | "${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig" |
| 271 | 271 | "${CMAKE_SOURCE_DIR}/lib/std/json.zig" |
lib/std/Build/Step/CheckObject.zig+2-2| ... | ... | @@ -539,7 +539,7 @@ const MachODumper = struct { |
| 539 | 539 | strings: []const u8, |
| 540 | 540 | }; |
| 541 | 541 | |
| 542 | fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8) ![]const u8 { | |
| 542 | fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 { | |
| 543 | 543 | const gpa = step.owner.allocator; |
| 544 | 544 | var stream = std.io.fixedBufferStream(bytes); |
| 545 | 545 | const reader = stream.reader(); |
| ... | ... | @@ -556,7 +556,7 @@ const MachODumper = struct { |
| 556 | 556 | var sections = std.ArrayList(macho.section_64).init(gpa); |
| 557 | 557 | var imports = std.ArrayList([]const u8).init(gpa); |
| 558 | 558 | |
| 559 | var it = LoadCommandIterator{ | |
| 559 | var it: LoadCommandIterator = .{ | |
| 560 | 560 | .ncmds = hdr.ncmds, |
| 561 | 561 | .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], |
| 562 | 562 | }; |
lib/std/io.zig+252-3| ... | ... | @@ -10,6 +10,7 @@ const fs = std.fs; |
| 10 | 10 | const mem = std.mem; |
| 11 | 11 | const meta = std.meta; |
| 12 | 12 | const File = std.fs.File; |
| 13 | const Allocator = std.mem.Allocator; | |
| 13 | 14 | |
| 14 | 15 | pub const Mode = enum { |
| 15 | 16 | /// I/O operates normally, waiting for the operating system syscalls to complete. |
| ... | ... | @@ -105,7 +106,255 @@ pub fn getStdIn() File { |
| 105 | 106 | }; |
| 106 | 107 | } |
| 107 | 108 | |
| 108 | pub const Reader = @import("io/reader.zig").Reader; | |
| 109 | pub fn GenericReader( | |
| 110 | comptime Context: type, | |
| 111 | comptime ReadError: type, | |
| 112 | /// Returns the number of bytes read. It may be less than buffer.len. | |
| 113 | /// If the number of bytes read is 0, it means end of stream. | |
| 114 | /// End of stream is not an error condition. | |
| 115 | comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize, | |
| 116 | ) type { | |
| 117 | return struct { | |
| 118 | context: Context, | |
| 119 | ||
| 120 | pub const Error = ReadError; | |
| 121 | pub const NoEofError = ReadError || error{ | |
| 122 | EndOfStream, | |
| 123 | }; | |
| 124 | ||
| 125 | pub inline fn read(self: Self, buffer: []u8) Error!usize { | |
| 126 | return readFn(self.context, buffer); | |
| 127 | } | |
| 128 | ||
| 129 | pub inline fn readAll(self: Self, buffer: []u8) Error!usize { | |
| 130 | return @errorCast(self.any().readAll(buffer)); | |
| 131 | } | |
| 132 | ||
| 133 | pub inline fn readAtLeast(self: Self, buffer: []u8, len: usize) Error!usize { | |
| 134 | return @errorCast(self.any().readAtLeast(buffer, len)); | |
| 135 | } | |
| 136 | ||
| 137 | pub inline fn readNoEof(self: Self, buf: []u8) NoEofError!void { | |
| 138 | return @errorCast(self.any().readNoEof(buf)); | |
| 139 | } | |
| 140 | ||
| 141 | pub inline fn readAllArrayList( | |
| 142 | self: Self, | |
| 143 | array_list: *std.ArrayList(u8), | |
| 144 | max_append_size: usize, | |
| 145 | ) (error{StreamTooLong} || Error)!void { | |
| 146 | return @errorCast(self.any().readAllArrayList(array_list, max_append_size)); | |
| 147 | } | |
| 148 | ||
| 149 | pub inline fn readAllArrayListAligned( | |
| 150 | self: Self, | |
| 151 | comptime alignment: ?u29, | |
| 152 | array_list: *std.ArrayListAligned(u8, alignment), | |
| 153 | max_append_size: usize, | |
| 154 | ) (error{StreamTooLong} || Error)!void { | |
| 155 | return @errorCast(self.any().readAllArrayListAligned( | |
| 156 | alignment, | |
| 157 | array_list, | |
| 158 | max_append_size, | |
| 159 | )); | |
| 160 | } | |
| 161 | ||
| 162 | pub inline fn readAllAlloc( | |
| 163 | self: Self, | |
| 164 | allocator: Allocator, | |
| 165 | max_size: usize, | |
| 166 | ) (Error || error{StreamTooLong})![]u8 { | |
| 167 | return @errorCast(self.any().readAllAlloc(allocator, max_size)); | |
| 168 | } | |
| 169 | ||
| 170 | pub inline fn readUntilDelimiterArrayList( | |
| 171 | self: Self, | |
| 172 | array_list: *std.ArrayList(u8), | |
| 173 | delimiter: u8, | |
| 174 | max_size: usize, | |
| 175 | ) (NoEofError || error{StreamTooLong})!void { | |
| 176 | return @errorCast(self.any().readUntilDelimiterArrayList( | |
| 177 | array_list, | |
| 178 | delimiter, | |
| 179 | max_size, | |
| 180 | )); | |
| 181 | } | |
| 182 | ||
| 183 | pub inline fn readUntilDelimiterAlloc( | |
| 184 | self: Self, | |
| 185 | allocator: Allocator, | |
| 186 | delimiter: u8, | |
| 187 | max_size: usize, | |
| 188 | ) (NoEofError || error{StreamTooLong})![]u8 { | |
| 189 | return @errorCast(self.any().readUntilDelimiterAlloc( | |
| 190 | allocator, | |
| 191 | delimiter, | |
| 192 | max_size, | |
| 193 | )); | |
| 194 | } | |
| 195 | ||
| 196 | pub inline fn readUntilDelimiter( | |
| 197 | self: Self, | |
| 198 | buf: []u8, | |
| 199 | delimiter: u8, | |
| 200 | ) (NoEofError || error{StreamTooLong})![]u8 { | |
| 201 | return @errorCast(self.any().readUntilDelimiter(buf, delimiter)); | |
| 202 | } | |
| 203 | ||
| 204 | pub inline fn readUntilDelimiterOrEofAlloc( | |
| 205 | self: Self, | |
| 206 | allocator: Allocator, | |
| 207 | delimiter: u8, | |
| 208 | max_size: usize, | |
| 209 | ) (Error || error{StreamTooLong})!?[]u8 { | |
| 210 | return @errorCast(self.any().readUntilDelimiterOrEofAlloc( | |
| 211 | allocator, | |
| 212 | delimiter, | |
| 213 | max_size, | |
| 214 | )); | |
| 215 | } | |
| 216 | ||
| 217 | pub inline fn readUntilDelimiterOrEof( | |
| 218 | self: Self, | |
| 219 | buf: []u8, | |
| 220 | delimiter: u8, | |
| 221 | ) (Error || error{StreamTooLong})!?[]u8 { | |
| 222 | return @errorCast(self.any().readUntilDelimiterOrEof(buf, delimiter)); | |
| 223 | } | |
| 224 | ||
| 225 | pub inline fn streamUntilDelimiter( | |
| 226 | self: Self, | |
| 227 | writer: anytype, | |
| 228 | delimiter: u8, | |
| 229 | optional_max_size: ?usize, | |
| 230 | ) (NoEofError || error{StreamTooLong} || @TypeOf(writer).Error)!void { | |
| 231 | return @errorCast(self.any().streamUntilDelimiter( | |
| 232 | writer, | |
| 233 | delimiter, | |
| 234 | optional_max_size, | |
| 235 | )); | |
| 236 | } | |
| 237 | ||
| 238 | pub inline fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void { | |
| 239 | return @errorCast(self.any().skipUntilDelimiterOrEof(delimiter)); | |
| 240 | } | |
| 241 | ||
| 242 | pub inline fn readByte(self: Self) NoEofError!u8 { | |
| 243 | return @errorCast(self.any().readByte()); | |
| 244 | } | |
| 245 | ||
| 246 | pub inline fn readByteSigned(self: Self) NoEofError!i8 { | |
| 247 | return @errorCast(self.any().readByteSigned()); | |
| 248 | } | |
| 249 | ||
| 250 | pub inline fn readBytesNoEof( | |
| 251 | self: Self, | |
| 252 | comptime num_bytes: usize, | |
| 253 | ) NoEofError![num_bytes]u8 { | |
| 254 | return @errorCast(self.any().readBytesNoEof(num_bytes)); | |
| 255 | } | |
| 256 | ||
| 257 | pub inline fn readIntoBoundedBytes( | |
| 258 | self: Self, | |
| 259 | comptime num_bytes: usize, | |
| 260 | bounded: *std.BoundedArray(u8, num_bytes), | |
| 261 | ) Error!void { | |
| 262 | return @errorCast(self.any().readIntoBoundedBytes(num_bytes, bounded)); | |
| 263 | } | |
| 264 | ||
| 265 | pub inline fn readBoundedBytes( | |
| 266 | self: Self, | |
| 267 | comptime num_bytes: usize, | |
| 268 | ) Error!std.BoundedArray(u8, num_bytes) { | |
| 269 | return @errorCast(self.any().readBoundedBytes(num_bytes)); | |
| 270 | } | |
| 271 | ||
| 272 | pub inline fn readIntNative(self: Self, comptime T: type) NoEofError!T { | |
| 273 | return @errorCast(self.any().readIntNative(T)); | |
| 274 | } | |
| 275 | ||
| 276 | pub inline fn readIntForeign(self: Self, comptime T: type) NoEofError!T { | |
| 277 | return @errorCast(self.any().readIntForeign(T)); | |
| 278 | } | |
| 279 | ||
| 280 | pub inline fn readIntLittle(self: Self, comptime T: type) NoEofError!T { | |
| 281 | return @errorCast(self.any().readIntLittle(T)); | |
| 282 | } | |
| 283 | ||
| 284 | pub inline fn readIntBig(self: Self, comptime T: type) NoEofError!T { | |
| 285 | return @errorCast(self.any().readIntBig(T)); | |
| 286 | } | |
| 287 | ||
| 288 | pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) NoEofError!T { | |
| 289 | return @errorCast(self.any().readInt(T, endian)); | |
| 290 | } | |
| 291 | ||
| 292 | pub inline fn readVarInt( | |
| 293 | self: Self, | |
| 294 | comptime ReturnType: type, | |
| 295 | endian: std.builtin.Endian, | |
| 296 | size: usize, | |
| 297 | ) NoEofError!ReturnType { | |
| 298 | return @errorCast(self.any().readVarInt(ReturnType, endian, size)); | |
| 299 | } | |
| 300 | ||
| 301 | pub const SkipBytesOptions = AnyReader.SkipBytesOptions; | |
| 302 | ||
| 303 | pub inline fn skipBytes( | |
| 304 | self: Self, | |
| 305 | num_bytes: u64, | |
| 306 | comptime options: SkipBytesOptions, | |
| 307 | ) NoEofError!void { | |
| 308 | return @errorCast(self.any().skipBytes(num_bytes, options)); | |
| 309 | } | |
| 310 | ||
| 311 | pub inline fn isBytes(self: Self, slice: []const u8) Error!bool { | |
| 312 | return @errorCast(self.any().isBytes(slice)); | |
| 313 | } | |
| 314 | ||
| 315 | pub inline fn readStruct(self: Self, comptime T: type) NoEofError!T { | |
| 316 | return @errorCast(self.any().readStruct(T)); | |
| 317 | } | |
| 318 | ||
| 319 | pub inline fn readStructBig(self: Self, comptime T: type) NoEofError!T { | |
| 320 | return @errorCast(self.any().readStructBig(T)); | |
| 321 | } | |
| 322 | ||
| 323 | pub const ReadEnumError = Error || error{ | |
| 324 | /// An integer was read, but it did not match any of the tags in the supplied enum. | |
| 325 | InvalidValue, | |
| 326 | }; | |
| 327 | ||
| 328 | pub inline fn readEnum( | |
| 329 | self: Self, | |
| 330 | comptime Enum: type, | |
| 331 | endian: std.builtin.Endian, | |
| 332 | ) ReadEnumError!Enum { | |
| 333 | return @errorCast(self.any().readEnum(Enum, endian)); | |
| 334 | } | |
| 335 | ||
| 336 | pub inline fn any(self: *const Self) AnyReader { | |
| 337 | return .{ | |
| 338 | .context = @ptrCast(&self.context), | |
| 339 | .readFn = typeErasedReadFn, | |
| 340 | }; | |
| 341 | } | |
| 342 | ||
| 343 | const Self = @This(); | |
| 344 | ||
| 345 | fn typeErasedReadFn(context: *const anyopaque, buffer: []u8) anyerror!usize { | |
| 346 | const ptr: *const Context = @alignCast(@ptrCast(context)); | |
| 347 | return readFn(ptr.*, buffer); | |
| 348 | } | |
| 349 | }; | |
| 350 | } | |
| 351 | ||
| 352 | /// Deprecated; consider switching to `AnyReader` or use `GenericReader` | |
| 353 | /// to use previous API. | |
| 354 | pub const Reader = GenericReader; | |
| 355 | ||
| 356 | pub const AnyReader = @import("io/Reader.zig"); | |
| 357 | ||
| 109 | 358 | pub const Writer = @import("io/writer.zig").Writer; |
| 110 | 359 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 111 | 360 | |
| ... | ... | @@ -168,7 +417,7 @@ test "null_writer" { |
| 168 | 417 | } |
| 169 | 418 | |
| 170 | 419 | pub fn poll( |
| 171 | allocator: std.mem.Allocator, | |
| 420 | allocator: Allocator, | |
| 172 | 421 | comptime StreamEnum: type, |
| 173 | 422 | files: PollFiles(StreamEnum), |
| 174 | 423 | ) Poller(StreamEnum) { |
| ... | ... | @@ -418,6 +667,7 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 418 | 667 | } |
| 419 | 668 | |
| 420 | 669 | test { |
| 670 | _ = AnyReader; | |
| 421 | 671 | _ = @import("io/bit_reader.zig"); |
| 422 | 672 | _ = @import("io/bit_writer.zig"); |
| 423 | 673 | _ = @import("io/buffered_atomic_file.zig"); |
| ... | ... | @@ -427,7 +677,6 @@ test { |
| 427 | 677 | _ = @import("io/counting_writer.zig"); |
| 428 | 678 | _ = @import("io/counting_reader.zig"); |
| 429 | 679 | _ = @import("io/fixed_buffer_stream.zig"); |
| 430 | _ = @import("io/reader.zig"); | |
| 431 | 680 | _ = @import("io/writer.zig"); |
| 432 | 681 | _ = @import("io/peek_stream.zig"); |
| 433 | 682 | _ = @import("io/seekable_stream.zig"); |
lib/std/io/Reader.zig created+395| ... | ... | @@ -0,0 +1,395 @@ |
| 1 | context: *const anyopaque, | |
| 2 | readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize, | |
| 3 | ||
| 4 | pub const Error = anyerror; | |
| 5 | ||
| 6 | /// Returns the number of bytes read. It may be less than buffer.len. | |
| 7 | /// If the number of bytes read is 0, it means end of stream. | |
| 8 | /// End of stream is not an error condition. | |
| 9 | pub fn read(self: Self, buffer: []u8) anyerror!usize { | |
| 10 | return self.readFn(self.context, buffer); | |
| 11 | } | |
| 12 | ||
| 13 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it | |
| 14 | /// means the stream reached the end. Reaching the end of a stream is not an error | |
| 15 | /// condition. | |
| 16 | pub fn readAll(self: Self, buffer: []u8) anyerror!usize { | |
| 17 | return readAtLeast(self, buffer, buffer.len); | |
| 18 | } | |
| 19 | ||
| 20 | /// Returns the number of bytes read, calling the underlying read | |
| 21 | /// function the minimal number of times until the buffer has at least | |
| 22 | /// `len` bytes filled. If the number read is less than `len` it means | |
| 23 | /// the stream reached the end. Reaching the end of the stream is not | |
| 24 | /// an error condition. | |
| 25 | pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize { | |
| 26 | assert(len <= buffer.len); | |
| 27 | var index: usize = 0; | |
| 28 | while (index < len) { | |
| 29 | const amt = try self.read(buffer[index..]); | |
| 30 | if (amt == 0) break; | |
| 31 | index += amt; | |
| 32 | } | |
| 33 | return index; | |
| 34 | } | |
| 35 | ||
| 36 | /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. | |
| 37 | pub fn readNoEof(self: Self, buf: []u8) anyerror!void { | |
| 38 | const amt_read = try self.readAll(buf); | |
| 39 | if (amt_read < buf.len) return error.EndOfStream; | |
| 40 | } | |
| 41 | ||
| 42 | /// Appends to the `std.ArrayList` contents by reading from the stream | |
| 43 | /// until end of stream is found. | |
| 44 | /// If the number of bytes appended would exceed `max_append_size`, | |
| 45 | /// `error.StreamTooLong` is returned | |
| 46 | /// and the `std.ArrayList` has exactly `max_append_size` bytes appended. | |
| 47 | pub fn readAllArrayList( | |
| 48 | self: Self, | |
| 49 | array_list: *std.ArrayList(u8), | |
| 50 | max_append_size: usize, | |
| 51 | ) anyerror!void { | |
| 52 | return self.readAllArrayListAligned(null, array_list, max_append_size); | |
| 53 | } | |
| 54 | ||
| 55 | pub fn readAllArrayListAligned( | |
| 56 | self: Self, | |
| 57 | comptime alignment: ?u29, | |
| 58 | array_list: *std.ArrayListAligned(u8, alignment), | |
| 59 | max_append_size: usize, | |
| 60 | ) anyerror!void { | |
| 61 | try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); | |
| 62 | const original_len = array_list.items.len; | |
| 63 | var start_index: usize = original_len; | |
| 64 | while (true) { | |
| 65 | array_list.expandToCapacity(); | |
| 66 | const dest_slice = array_list.items[start_index..]; | |
| 67 | const bytes_read = try self.readAll(dest_slice); | |
| 68 | start_index += bytes_read; | |
| 69 | ||
| 70 | if (start_index - original_len > max_append_size) { | |
| 71 | array_list.shrinkAndFree(original_len + max_append_size); | |
| 72 | return error.StreamTooLong; | |
| 73 | } | |
| 74 | ||
| 75 | if (bytes_read != dest_slice.len) { | |
| 76 | array_list.shrinkAndFree(start_index); | |
| 77 | return; | |
| 78 | } | |
| 79 | ||
| 80 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 81 | try array_list.ensureTotalCapacity(start_index + 1); | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | /// Allocates enough memory to hold all the contents of the stream. If the allocated | |
| 86 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 87 | /// Caller owns returned memory. | |
| 88 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 89 | pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 { | |
| 90 | var array_list = std.ArrayList(u8).init(allocator); | |
| 91 | defer array_list.deinit(); | |
| 92 | try self.readAllArrayList(&array_list, max_size); | |
| 93 | return try array_list.toOwnedSlice(); | |
| 94 | } | |
| 95 | ||
| 96 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. | |
| 97 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. | |
| 98 | /// Does not include the delimiter in the result. | |
| 99 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the | |
| 100 | /// `std.ArrayList` is populated with `max_size` bytes from the stream. | |
| 101 | pub fn readUntilDelimiterArrayList( | |
| 102 | self: Self, | |
| 103 | array_list: *std.ArrayList(u8), | |
| 104 | delimiter: u8, | |
| 105 | max_size: usize, | |
| 106 | ) anyerror!void { | |
| 107 | array_list.shrinkRetainingCapacity(0); | |
| 108 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); | |
| 109 | } | |
| 110 | ||
| 111 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. | |
| 112 | /// Allocates enough memory to read until `delimiter`. If the allocated | |
| 113 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 114 | /// Caller owns returned memory. | |
| 115 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 116 | pub fn readUntilDelimiterAlloc( | |
| 117 | self: Self, | |
| 118 | allocator: mem.Allocator, | |
| 119 | delimiter: u8, | |
| 120 | max_size: usize, | |
| 121 | ) anyerror![]u8 { | |
| 122 | var array_list = std.ArrayList(u8).init(allocator); | |
| 123 | defer array_list.deinit(); | |
| 124 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); | |
| 125 | return try array_list.toOwnedSlice(); | |
| 126 | } | |
| 127 | ||
| 128 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. | |
| 129 | /// Reads from the stream until specified byte is found. If the buffer is not | |
| 130 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | |
| 131 | /// If end-of-stream is found, `error.EndOfStream` is returned. | |
| 132 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | |
| 133 | /// delimiter byte is written to the output buffer but is not included | |
| 134 | /// in the returned slice. | |
| 135 | pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 { | |
| 136 | var fbs = std.io.fixedBufferStream(buf); | |
| 137 | try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len); | |
| 138 | const output = fbs.getWritten(); | |
| 139 | buf[output.len] = delimiter; // emulating old behaviour | |
| 140 | return output; | |
| 141 | } | |
| 142 | ||
| 143 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead. | |
| 144 | /// Allocates enough memory to read until `delimiter` or end-of-stream. | |
| 145 | /// If the allocated memory would be greater than `max_size`, returns | |
| 146 | /// `error.StreamTooLong`. If end-of-stream is found, returns the rest | |
| 147 | /// of the stream. If this function is called again after that, returns | |
| 148 | /// null. | |
| 149 | /// Caller owns returned memory. | |
| 150 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 151 | pub fn readUntilDelimiterOrEofAlloc( | |
| 152 | self: Self, | |
| 153 | allocator: mem.Allocator, | |
| 154 | delimiter: u8, | |
| 155 | max_size: usize, | |
| 156 | ) anyerror!?[]u8 { | |
| 157 | var array_list = std.ArrayList(u8).init(allocator); | |
| 158 | defer array_list.deinit(); | |
| 159 | self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) { | |
| 160 | error.EndOfStream => if (array_list.items.len == 0) { | |
| 161 | return null; | |
| 162 | }, | |
| 163 | else => |e| return e, | |
| 164 | }; | |
| 165 | return try array_list.toOwnedSlice(); | |
| 166 | } | |
| 167 | ||
| 168 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. | |
| 169 | /// Reads from the stream until specified byte is found. If the buffer is not | |
| 170 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | |
| 171 | /// If end-of-stream is found, returns the rest of the stream. If this | |
| 172 | /// function is called again after that, returns null. | |
| 173 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | |
| 174 | /// delimiter byte is written to the output buffer but is not included | |
| 175 | /// in the returned slice. | |
| 176 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) anyerror!?[]u8 { | |
| 177 | var fbs = std.io.fixedBufferStream(buf); | |
| 178 | self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) { | |
| 179 | error.EndOfStream => if (fbs.getWritten().len == 0) { | |
| 180 | return null; | |
| 181 | }, | |
| 182 | ||
| 183 | else => |e| return e, | |
| 184 | }; | |
| 185 | const output = fbs.getWritten(); | |
| 186 | buf[output.len] = delimiter; // emulating old behaviour | |
| 187 | return output; | |
| 188 | } | |
| 189 | ||
| 190 | /// Appends to the `writer` contents by reading from the stream until `delimiter` is found. | |
| 191 | /// Does not write the delimiter itself. | |
| 192 | /// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`, | |
| 193 | /// returns `error.StreamTooLong` and finishes appending. | |
| 194 | /// If `optional_max_size` is null, appending is unbounded. | |
| 195 | pub fn streamUntilDelimiter( | |
| 196 | self: Self, | |
| 197 | writer: anytype, | |
| 198 | delimiter: u8, | |
| 199 | optional_max_size: ?usize, | |
| 200 | ) anyerror!void { | |
| 201 | if (optional_max_size) |max_size| { | |
| 202 | for (0..max_size) |_| { | |
| 203 | const byte: u8 = try self.readByte(); | |
| 204 | if (byte == delimiter) return; | |
| 205 | try writer.writeByte(byte); | |
| 206 | } | |
| 207 | return error.StreamTooLong; | |
| 208 | } else { | |
| 209 | while (true) { | |
| 210 | const byte: u8 = try self.readByte(); | |
| 211 | if (byte == delimiter) return; | |
| 212 | try writer.writeByte(byte); | |
| 213 | } | |
| 214 | // Can not throw `error.StreamTooLong` since there are no boundary. | |
| 215 | } | |
| 216 | } | |
| 217 | ||
| 218 | /// Reads from the stream until specified byte is found, discarding all data, | |
| 219 | /// including the delimiter. | |
| 220 | /// If end-of-stream is found, this function succeeds. | |
| 221 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void { | |
| 222 | while (true) { | |
| 223 | const byte = self.readByte() catch |err| switch (err) { | |
| 224 | error.EndOfStream => return, | |
| 225 | else => |e| return e, | |
| 226 | }; | |
| 227 | if (byte == delimiter) return; | |
| 228 | } | |
| 229 | } | |
| 230 | ||
| 231 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | |
| 232 | pub fn readByte(self: Self) anyerror!u8 { | |
| 233 | var result: [1]u8 = undefined; | |
| 234 | const amt_read = try self.read(result[0..]); | |
| 235 | if (amt_read < 1) return error.EndOfStream; | |
| 236 | return result[0]; | |
| 237 | } | |
| 238 | ||
| 239 | /// Same as `readByte` except the returned byte is signed. | |
| 240 | pub fn readByteSigned(self: Self) anyerror!i8 { | |
| 241 | return @as(i8, @bitCast(try self.readByte())); | |
| 242 | } | |
| 243 | ||
| 244 | /// Reads exactly `num_bytes` bytes and returns as an array. | |
| 245 | /// `num_bytes` must be comptime-known | |
| 246 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 { | |
| 247 | var bytes: [num_bytes]u8 = undefined; | |
| 248 | try self.readNoEof(&bytes); | |
| 249 | return bytes; | |
| 250 | } | |
| 251 | ||
| 252 | /// Reads bytes until `bounded.len` is equal to `num_bytes`, | |
| 253 | /// or the stream ends. | |
| 254 | /// | |
| 255 | /// * it is assumed that `num_bytes` will not exceed `bounded.capacity()` | |
| 256 | pub fn readIntoBoundedBytes( | |
| 257 | self: Self, | |
| 258 | comptime num_bytes: usize, | |
| 259 | bounded: *std.BoundedArray(u8, num_bytes), | |
| 260 | ) anyerror!void { | |
| 261 | while (bounded.len < num_bytes) { | |
| 262 | // get at most the number of bytes free in the bounded array | |
| 263 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); | |
| 264 | if (bytes_read == 0) return; | |
| 265 | ||
| 266 | // bytes_read will never be larger than @TypeOf(bounded.len) | |
| 267 | // due to `self.read` being bounded by `bounded.unusedCapacitySlice()` | |
| 268 | bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read)); | |
| 269 | } | |
| 270 | } | |
| 271 | ||
| 272 | /// Reads at most `num_bytes` and returns as a bounded array. | |
| 273 | pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) { | |
| 274 | var result = std.BoundedArray(u8, num_bytes){}; | |
| 275 | try self.readIntoBoundedBytes(num_bytes, &result); | |
| 276 | return result; | |
| 277 | } | |
| 278 | ||
| 279 | /// Reads a native-endian integer | |
| 280 | pub fn readIntNative(self: Self, comptime T: type) anyerror!T { | |
| 281 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 282 | return mem.readIntNative(T, &bytes); | |
| 283 | } | |
| 284 | ||
| 285 | /// Reads a foreign-endian integer | |
| 286 | pub fn readIntForeign(self: Self, comptime T: type) anyerror!T { | |
| 287 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 288 | return mem.readIntForeign(T, &bytes); | |
| 289 | } | |
| 290 | ||
| 291 | pub fn readIntLittle(self: Self, comptime T: type) anyerror!T { | |
| 292 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 293 | return mem.readIntLittle(T, &bytes); | |
| 294 | } | |
| 295 | ||
| 296 | pub fn readIntBig(self: Self, comptime T: type) anyerror!T { | |
| 297 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 298 | return mem.readIntBig(T, &bytes); | |
| 299 | } | |
| 300 | ||
| 301 | pub fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { | |
| 302 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 303 | return mem.readInt(T, &bytes, endian); | |
| 304 | } | |
| 305 | ||
| 306 | pub fn readVarInt( | |
| 307 | self: Self, | |
| 308 | comptime ReturnType: type, | |
| 309 | endian: std.builtin.Endian, | |
| 310 | size: usize, | |
| 311 | ) anyerror!ReturnType { | |
| 312 | assert(size <= @sizeOf(ReturnType)); | |
| 313 | var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; | |
| 314 | const bytes = bytes_buf[0..size]; | |
| 315 | try self.readNoEof(bytes); | |
| 316 | return mem.readVarInt(ReturnType, bytes, endian); | |
| 317 | } | |
| 318 | ||
| 319 | /// Optional parameters for `skipBytes` | |
| 320 | pub const SkipBytesOptions = struct { | |
| 321 | buf_size: usize = 512, | |
| 322 | }; | |
| 323 | ||
| 324 | // `num_bytes` is a `u64` to match `off_t` | |
| 325 | /// Reads `num_bytes` bytes from the stream and discards them | |
| 326 | pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void { | |
| 327 | var buf: [options.buf_size]u8 = undefined; | |
| 328 | var remaining = num_bytes; | |
| 329 | ||
| 330 | while (remaining > 0) { | |
| 331 | const amt = @min(remaining, options.buf_size); | |
| 332 | try self.readNoEof(buf[0..amt]); | |
| 333 | remaining -= amt; | |
| 334 | } | |
| 335 | } | |
| 336 | ||
| 337 | /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice | |
| 338 | pub fn isBytes(self: Self, slice: []const u8) anyerror!bool { | |
| 339 | var i: usize = 0; | |
| 340 | var matches = true; | |
| 341 | while (i < slice.len) : (i += 1) { | |
| 342 | if (slice[i] != try self.readByte()) { | |
| 343 | matches = false; | |
| 344 | } | |
| 345 | } | |
| 346 | return matches; | |
| 347 | } | |
| 348 | ||
| 349 | pub fn readStruct(self: Self, comptime T: type) anyerror!T { | |
| 350 | // Only extern and packed structs have defined in-memory layout. | |
| 351 | comptime assert(@typeInfo(T).Struct.layout != .Auto); | |
| 352 | var res: [1]T = undefined; | |
| 353 | try self.readNoEof(mem.sliceAsBytes(res[0..])); | |
| 354 | return res[0]; | |
| 355 | } | |
| 356 | ||
| 357 | pub fn readStructBig(self: Self, comptime T: type) anyerror!T { | |
| 358 | var res = try self.readStruct(T); | |
| 359 | if (native_endian != std.builtin.Endian.Big) { | |
| 360 | mem.byteSwapAllFields(T, &res); | |
| 361 | } | |
| 362 | return res; | |
| 363 | } | |
| 364 | ||
| 365 | /// Reads an integer with the same size as the given enum's tag type. If the integer matches | |
| 366 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`. | |
| 367 | /// TODO optimization taking advantage of most fields being in order | |
| 368 | pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum { | |
| 369 | const E = error{ | |
| 370 | /// An integer was read, but it did not match any of the tags in the supplied enum. | |
| 371 | InvalidValue, | |
| 372 | }; | |
| 373 | const type_info = @typeInfo(Enum).Enum; | |
| 374 | const tag = try self.readInt(type_info.tag_type, endian); | |
| 375 | ||
| 376 | inline for (std.meta.fields(Enum)) |field| { | |
| 377 | if (tag == field.value) { | |
| 378 | return @field(Enum, field.name); | |
| 379 | } | |
| 380 | } | |
| 381 | ||
| 382 | return E.InvalidValue; | |
| 383 | } | |
| 384 | ||
| 385 | const std = @import("../std.zig"); | |
| 386 | const Self = @This(); | |
| 387 | const math = std.math; | |
| 388 | const assert = std.debug.assert; | |
| 389 | const mem = std.mem; | |
| 390 | const testing = std.testing; | |
| 391 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 392 | ||
| 393 | test { | |
| 394 | _ = @import("Reader/test.zig"); | |
| 395 | } |
lib/std/io/Reader/test.zig created+371| ... | ... | @@ -0,0 +1,371 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | const testing = std.testing; | |
| 3 | ||
| 4 | test "Reader" { | |
| 5 | var buf = "a\x02".*; | |
| 6 | var fis = std.io.fixedBufferStream(&buf); | |
| 7 | const reader = fis.reader(); | |
| 8 | try testing.expect((try reader.readByte()) == 'a'); | |
| 9 | try testing.expect((try reader.readEnum(enum(u8) { | |
| 10 | a = 0, | |
| 11 | b = 99, | |
| 12 | c = 2, | |
| 13 | d = 3, | |
| 14 | }, undefined)) == .c); | |
| 15 | try testing.expectError(error.EndOfStream, reader.readByte()); | |
| 16 | } | |
| 17 | ||
| 18 | test "Reader.isBytes" { | |
| 19 | var fis = std.io.fixedBufferStream("foobar"); | |
| 20 | const reader = fis.reader(); | |
| 21 | try testing.expectEqual(true, try reader.isBytes("foo")); | |
| 22 | try testing.expectEqual(false, try reader.isBytes("qux")); | |
| 23 | } | |
| 24 | ||
| 25 | test "Reader.skipBytes" { | |
| 26 | var fis = std.io.fixedBufferStream("foobar"); | |
| 27 | const reader = fis.reader(); | |
| 28 | try reader.skipBytes(3, .{}); | |
| 29 | try testing.expect(try reader.isBytes("bar")); | |
| 30 | try reader.skipBytes(0, .{}); | |
| 31 | try testing.expectError(error.EndOfStream, reader.skipBytes(1, .{})); | |
| 32 | } | |
| 33 | ||
| 34 | test "Reader.readUntilDelimiterArrayList returns ArrayLists with bytes read until the delimiter, then EndOfStream" { | |
| 35 | const a = std.testing.allocator; | |
| 36 | var list = std.ArrayList(u8).init(a); | |
| 37 | defer list.deinit(); | |
| 38 | ||
| 39 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 40 | const reader = fis.reader(); | |
| 41 | ||
| 42 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 43 | try std.testing.expectEqualStrings("0000", list.items); | |
| 44 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 45 | try std.testing.expectEqualStrings("1234", list.items); | |
| 46 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5)); | |
| 47 | } | |
| 48 | ||
| 49 | test "Reader.readUntilDelimiterArrayList returns an empty ArrayList" { | |
| 50 | const a = std.testing.allocator; | |
| 51 | var list = std.ArrayList(u8).init(a); | |
| 52 | defer list.deinit(); | |
| 53 | ||
| 54 | var fis = std.io.fixedBufferStream("\n"); | |
| 55 | const reader = fis.reader(); | |
| 56 | ||
| 57 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 58 | try std.testing.expectEqualStrings("", list.items); | |
| 59 | } | |
| 60 | ||
| 61 | test "Reader.readUntilDelimiterArrayList returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { | |
| 62 | const a = std.testing.allocator; | |
| 63 | var list = std.ArrayList(u8).init(a); | |
| 64 | defer list.deinit(); | |
| 65 | ||
| 66 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 67 | const reader = fis.reader(); | |
| 68 | ||
| 69 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5)); | |
| 70 | try std.testing.expectEqualStrings("12345", list.items); | |
| 71 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 72 | try std.testing.expectEqualStrings("67", list.items); | |
| 73 | } | |
| 74 | ||
| 75 | test "Reader.readUntilDelimiterArrayList returns EndOfStream" { | |
| 76 | const a = std.testing.allocator; | |
| 77 | var list = std.ArrayList(u8).init(a); | |
| 78 | defer list.deinit(); | |
| 79 | ||
| 80 | var fis = std.io.fixedBufferStream("1234"); | |
| 81 | const reader = fis.reader(); | |
| 82 | ||
| 83 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5)); | |
| 84 | try std.testing.expectEqualStrings("1234", list.items); | |
| 85 | } | |
| 86 | ||
| 87 | test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" { | |
| 88 | const a = std.testing.allocator; | |
| 89 | ||
| 90 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 91 | const reader = fis.reader(); | |
| 92 | ||
| 93 | { | |
| 94 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 95 | defer a.free(result); | |
| 96 | try std.testing.expectEqualStrings("0000", result); | |
| 97 | } | |
| 98 | ||
| 99 | { | |
| 100 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 101 | defer a.free(result); | |
| 102 | try std.testing.expectEqualStrings("1234", result); | |
| 103 | } | |
| 104 | ||
| 105 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5)); | |
| 106 | } | |
| 107 | ||
| 108 | test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" { | |
| 109 | const a = std.testing.allocator; | |
| 110 | ||
| 111 | var fis = std.io.fixedBufferStream("\n"); | |
| 112 | const reader = fis.reader(); | |
| 113 | ||
| 114 | { | |
| 115 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 116 | defer a.free(result); | |
| 117 | try std.testing.expectEqualStrings("", result); | |
| 118 | } | |
| 119 | } | |
| 120 | ||
| 121 | test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { | |
| 122 | const a = std.testing.allocator; | |
| 123 | ||
| 124 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 125 | const reader = fis.reader(); | |
| 126 | ||
| 127 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5)); | |
| 128 | ||
| 129 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 130 | defer a.free(result); | |
| 131 | try std.testing.expectEqualStrings("67", result); | |
| 132 | } | |
| 133 | ||
| 134 | test "Reader.readUntilDelimiterAlloc returns EndOfStream" { | |
| 135 | const a = std.testing.allocator; | |
| 136 | ||
| 137 | var fis = std.io.fixedBufferStream("1234"); | |
| 138 | const reader = fis.reader(); | |
| 139 | ||
| 140 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5)); | |
| 141 | } | |
| 142 | ||
| 143 | test "Reader.readUntilDelimiter returns bytes read until the delimiter" { | |
| 144 | var buf: [5]u8 = undefined; | |
| 145 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 146 | const reader = fis.reader(); | |
| 147 | try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n')); | |
| 148 | try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n')); | |
| 149 | } | |
| 150 | ||
| 151 | test "Reader.readUntilDelimiter returns an empty string" { | |
| 152 | var buf: [5]u8 = undefined; | |
| 153 | var fis = std.io.fixedBufferStream("\n"); | |
| 154 | const reader = fis.reader(); | |
| 155 | try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n')); | |
| 156 | } | |
| 157 | ||
| 158 | test "Reader.readUntilDelimiter returns StreamTooLong, then an empty string" { | |
| 159 | var buf: [5]u8 = undefined; | |
| 160 | var fis = std.io.fixedBufferStream("12345\n"); | |
| 161 | const reader = fis.reader(); | |
| 162 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 163 | try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n')); | |
| 164 | } | |
| 165 | ||
| 166 | test "Reader.readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" { | |
| 167 | var buf: [5]u8 = undefined; | |
| 168 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 169 | const reader = fis.reader(); | |
| 170 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 171 | try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n')); | |
| 172 | } | |
| 173 | ||
| 174 | test "Reader.readUntilDelimiter returns EndOfStream" { | |
| 175 | { | |
| 176 | var buf: [5]u8 = undefined; | |
| 177 | var fis = std.io.fixedBufferStream(""); | |
| 178 | const reader = fis.reader(); | |
| 179 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 180 | } | |
| 181 | { | |
| 182 | var buf: [5]u8 = undefined; | |
| 183 | var fis = std.io.fixedBufferStream("1234"); | |
| 184 | const reader = fis.reader(); | |
| 185 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 186 | } | |
| 187 | } | |
| 188 | ||
| 189 | test "Reader.readUntilDelimiter returns bytes read until delimiter, then EndOfStream" { | |
| 190 | var buf: [5]u8 = undefined; | |
| 191 | var fis = std.io.fixedBufferStream("1234\n"); | |
| 192 | const reader = fis.reader(); | |
| 193 | try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n')); | |
| 194 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 195 | } | |
| 196 | ||
| 197 | test "Reader.readUntilDelimiter returns StreamTooLong, then EndOfStream" { | |
| 198 | var buf: [5]u8 = undefined; | |
| 199 | var fis = std.io.fixedBufferStream("12345"); | |
| 200 | const reader = fis.reader(); | |
| 201 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 202 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 203 | } | |
| 204 | ||
| 205 | test "Reader.readUntilDelimiter writes all bytes read to the output buffer" { | |
| 206 | var buf: [5]u8 = undefined; | |
| 207 | var fis = std.io.fixedBufferStream("0000\n12345"); | |
| 208 | const reader = fis.reader(); | |
| 209 | _ = try reader.readUntilDelimiter(&buf, '\n'); | |
| 210 | try std.testing.expectEqualStrings("0000\n", &buf); | |
| 211 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 212 | try std.testing.expectEqualStrings("12345", &buf); | |
| 213 | } | |
| 214 | ||
| 215 | test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" { | |
| 216 | const a = std.testing.allocator; | |
| 217 | ||
| 218 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 219 | const reader = fis.reader(); | |
| 220 | ||
| 221 | { | |
| 222 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 223 | defer a.free(result); | |
| 224 | try std.testing.expectEqualStrings("0000", result); | |
| 225 | } | |
| 226 | ||
| 227 | { | |
| 228 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 229 | defer a.free(result); | |
| 230 | try std.testing.expectEqualStrings("1234", result); | |
| 231 | } | |
| 232 | ||
| 233 | try std.testing.expect((try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)) == null); | |
| 234 | } | |
| 235 | ||
| 236 | test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" { | |
| 237 | const a = std.testing.allocator; | |
| 238 | ||
| 239 | var fis = std.io.fixedBufferStream("\n"); | |
| 240 | const reader = fis.reader(); | |
| 241 | ||
| 242 | { | |
| 243 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 244 | defer a.free(result); | |
| 245 | try std.testing.expectEqualStrings("", result); | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { | |
| 250 | const a = std.testing.allocator; | |
| 251 | ||
| 252 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 253 | const reader = fis.reader(); | |
| 254 | ||
| 255 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)); | |
| 256 | ||
| 257 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 258 | defer a.free(result); | |
| 259 | try std.testing.expectEqualStrings("67", result); | |
| 260 | } | |
| 261 | ||
| 262 | test "Reader.readUntilDelimiterOrEof returns bytes read until the delimiter" { | |
| 263 | var buf: [5]u8 = undefined; | |
| 264 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 265 | const reader = fis.reader(); | |
| 266 | try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 267 | try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 268 | } | |
| 269 | ||
| 270 | test "Reader.readUntilDelimiterOrEof returns an empty string" { | |
| 271 | var buf: [5]u8 = undefined; | |
| 272 | var fis = std.io.fixedBufferStream("\n"); | |
| 273 | const reader = fis.reader(); | |
| 274 | try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 275 | } | |
| 276 | ||
| 277 | test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then an empty string" { | |
| 278 | var buf: [5]u8 = undefined; | |
| 279 | var fis = std.io.fixedBufferStream("12345\n"); | |
| 280 | const reader = fis.reader(); | |
| 281 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 282 | try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 283 | } | |
| 284 | ||
| 285 | test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" { | |
| 286 | var buf: [5]u8 = undefined; | |
| 287 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 288 | const reader = fis.reader(); | |
| 289 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 290 | try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 291 | } | |
| 292 | ||
| 293 | test "Reader.readUntilDelimiterOrEof returns null" { | |
| 294 | var buf: [5]u8 = undefined; | |
| 295 | var fis = std.io.fixedBufferStream(""); | |
| 296 | const reader = fis.reader(); | |
| 297 | try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null); | |
| 298 | } | |
| 299 | ||
| 300 | test "Reader.readUntilDelimiterOrEof returns bytes read until delimiter, then null" { | |
| 301 | var buf: [5]u8 = undefined; | |
| 302 | var fis = std.io.fixedBufferStream("1234\n"); | |
| 303 | const reader = fis.reader(); | |
| 304 | try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 305 | try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null); | |
| 306 | } | |
| 307 | ||
| 308 | test "Reader.readUntilDelimiterOrEof returns bytes read until end-of-stream" { | |
| 309 | var buf: [5]u8 = undefined; | |
| 310 | var fis = std.io.fixedBufferStream("1234"); | |
| 311 | const reader = fis.reader(); | |
| 312 | try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 313 | } | |
| 314 | ||
| 315 | test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" { | |
| 316 | var buf: [5]u8 = undefined; | |
| 317 | var fis = std.io.fixedBufferStream("1234567"); | |
| 318 | const reader = fis.reader(); | |
| 319 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 320 | try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 321 | } | |
| 322 | ||
| 323 | test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" { | |
| 324 | var buf: [5]u8 = undefined; | |
| 325 | var fis = std.io.fixedBufferStream("0000\n12345"); | |
| 326 | const reader = fis.reader(); | |
| 327 | _ = try reader.readUntilDelimiterOrEof(&buf, '\n'); | |
| 328 | try std.testing.expectEqualStrings("0000\n", &buf); | |
| 329 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 330 | try std.testing.expectEqualStrings("12345", &buf); | |
| 331 | } | |
| 332 | ||
| 333 | test "Reader.streamUntilDelimiter writes all bytes without delimiter to the output" { | |
| 334 | const input_string = "some_string_with_delimiter!"; | |
| 335 | var input_fbs = std.io.fixedBufferStream(input_string); | |
| 336 | const reader = input_fbs.reader(); | |
| 337 | ||
| 338 | var output: [input_string.len]u8 = undefined; | |
| 339 | var output_fbs = std.io.fixedBufferStream(&output); | |
| 340 | const writer = output_fbs.writer(); | |
| 341 | ||
| 342 | try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len); | |
| 343 | try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten()); | |
| 344 | try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len)); | |
| 345 | ||
| 346 | input_fbs.reset(); | |
| 347 | output_fbs.reset(); | |
| 348 | ||
| 349 | try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5)); | |
| 350 | } | |
| 351 | ||
| 352 | test "Reader.readBoundedBytes correctly reads into a new bounded array" { | |
| 353 | const test_string = "abcdefg"; | |
| 354 | var fis = std.io.fixedBufferStream(test_string); | |
| 355 | const reader = fis.reader(); | |
| 356 | ||
| 357 | var array = try reader.readBoundedBytes(10000); | |
| 358 | try testing.expectEqualStrings(array.slice(), test_string); | |
| 359 | } | |
| 360 | ||
| 361 | test "Reader.readIntoBoundedBytes correctly reads into a provided bounded array" { | |
| 362 | const test_string = "abcdefg"; | |
| 363 | var fis = std.io.fixedBufferStream(test_string); | |
| 364 | const reader = fis.reader(); | |
| 365 | ||
| 366 | var bounded_array = std.BoundedArray(u8, 10000){}; | |
| 367 | ||
| 368 | // compile time error if the size is not the same at the provided `bounded.capacity()` | |
| 369 | try reader.readIntoBoundedBytes(10000, &bounded_array); | |
| 370 | try testing.expectEqualStrings(bounded_array.slice(), test_string); | |
| 371 | } |
lib/std/io/reader.zig deleted-757| ... | ... | @@ -1,757 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const math = std.math; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const mem = std.mem; | |
| 5 | const testing = std.testing; | |
| 6 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 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 | return readAtLeast(self, buffer, buffer.len); | |
| 35 | } | |
| 36 | ||
| 37 | /// Returns the number of bytes read, calling the underlying read | |
| 38 | /// function the minimal number of times until the buffer has at least | |
| 39 | /// `len` bytes filled. If the number read is less than `len` it means | |
| 40 | /// the stream reached the end. Reaching the end of the stream is not | |
| 41 | /// an error condition. | |
| 42 | pub fn readAtLeast(self: Self, buffer: []u8, len: usize) Error!usize { | |
| 43 | assert(len <= buffer.len); | |
| 44 | var index: usize = 0; | |
| 45 | while (index < len) { | |
| 46 | const amt = try self.read(buffer[index..]); | |
| 47 | if (amt == 0) break; | |
| 48 | index += amt; | |
| 49 | } | |
| 50 | return index; | |
| 51 | } | |
| 52 | ||
| 53 | /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. | |
| 54 | pub fn readNoEof(self: Self, buf: []u8) (Error || error{EndOfStream})!void { | |
| 55 | const amt_read = try self.readAll(buf); | |
| 56 | if (amt_read < buf.len) return error.EndOfStream; | |
| 57 | } | |
| 58 | ||
| 59 | /// Appends to the `std.ArrayList` contents by reading from the stream | |
| 60 | /// until end of stream is found. | |
| 61 | /// If the number of bytes appended would exceed `max_append_size`, | |
| 62 | /// `error.StreamTooLong` is returned | |
| 63 | /// and the `std.ArrayList` has exactly `max_append_size` bytes appended. | |
| 64 | pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void { | |
| 65 | return self.readAllArrayListAligned(null, array_list, max_append_size); | |
| 66 | } | |
| 67 | ||
| 68 | pub fn readAllArrayListAligned( | |
| 69 | self: Self, | |
| 70 | comptime alignment: ?u29, | |
| 71 | array_list: *std.ArrayListAligned(u8, alignment), | |
| 72 | max_append_size: usize, | |
| 73 | ) !void { | |
| 74 | try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); | |
| 75 | const original_len = array_list.items.len; | |
| 76 | var start_index: usize = original_len; | |
| 77 | while (true) { | |
| 78 | array_list.expandToCapacity(); | |
| 79 | const dest_slice = array_list.items[start_index..]; | |
| 80 | const bytes_read = try self.readAll(dest_slice); | |
| 81 | start_index += bytes_read; | |
| 82 | ||
| 83 | if (start_index - original_len > max_append_size) { | |
| 84 | array_list.shrinkAndFree(original_len + max_append_size); | |
| 85 | return error.StreamTooLong; | |
| 86 | } | |
| 87 | ||
| 88 | if (bytes_read != dest_slice.len) { | |
| 89 | array_list.shrinkAndFree(start_index); | |
| 90 | return; | |
| 91 | } | |
| 92 | ||
| 93 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 94 | try array_list.ensureTotalCapacity(start_index + 1); | |
| 95 | } | |
| 96 | } | |
| 97 | ||
| 98 | /// Allocates enough memory to hold all the contents of the stream. If the allocated | |
| 99 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 100 | /// Caller owns returned memory. | |
| 101 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 102 | pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) ![]u8 { | |
| 103 | var array_list = std.ArrayList(u8).init(allocator); | |
| 104 | defer array_list.deinit(); | |
| 105 | try self.readAllArrayList(&array_list, max_size); | |
| 106 | return try array_list.toOwnedSlice(); | |
| 107 | } | |
| 108 | ||
| 109 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. | |
| 110 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. | |
| 111 | /// Does not include the delimiter in the result. | |
| 112 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the | |
| 113 | /// `std.ArrayList` is populated with `max_size` bytes from the stream. | |
| 114 | pub fn readUntilDelimiterArrayList( | |
| 115 | self: Self, | |
| 116 | array_list: *std.ArrayList(u8), | |
| 117 | delimiter: u8, | |
| 118 | max_size: usize, | |
| 119 | ) !void { | |
| 120 | array_list.shrinkRetainingCapacity(0); | |
| 121 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); | |
| 122 | } | |
| 123 | ||
| 124 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. | |
| 125 | /// Allocates enough memory to read until `delimiter`. If the allocated | |
| 126 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 127 | /// Caller owns returned memory. | |
| 128 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 129 | pub fn readUntilDelimiterAlloc( | |
| 130 | self: Self, | |
| 131 | allocator: mem.Allocator, | |
| 132 | delimiter: u8, | |
| 133 | max_size: usize, | |
| 134 | ) ![]u8 { | |
| 135 | var array_list = std.ArrayList(u8).init(allocator); | |
| 136 | defer array_list.deinit(); | |
| 137 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); | |
| 138 | return try array_list.toOwnedSlice(); | |
| 139 | } | |
| 140 | ||
| 141 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. | |
| 142 | /// Reads from the stream until specified byte is found. If the buffer is not | |
| 143 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | |
| 144 | /// If end-of-stream is found, `error.EndOfStream` is returned. | |
| 145 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | |
| 146 | /// delimiter byte is written to the output buffer but is not included | |
| 147 | /// in the returned slice. | |
| 148 | pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 { | |
| 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; | |
| 154 | } | |
| 155 | ||
| 156 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead. | |
| 157 | /// Allocates enough memory to read until `delimiter` or end-of-stream. | |
| 158 | /// If the allocated memory would be greater than `max_size`, returns | |
| 159 | /// `error.StreamTooLong`. If end-of-stream is found, returns the rest | |
| 160 | /// of the stream. If this function is called again after that, returns | |
| 161 | /// null. | |
| 162 | /// Caller owns returned memory. | |
| 163 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 164 | pub fn readUntilDelimiterOrEofAlloc( | |
| 165 | self: Self, | |
| 166 | allocator: mem.Allocator, | |
| 167 | delimiter: u8, | |
| 168 | max_size: usize, | |
| 169 | ) !?[]u8 { | |
| 170 | var array_list = std.ArrayList(u8).init(allocator); | |
| 171 | defer array_list.deinit(); | |
| 172 | self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) { | |
| 173 | error.EndOfStream => if (array_list.items.len == 0) { | |
| 174 | return null; | |
| 175 | }, | |
| 176 | else => |e| return e, | |
| 177 | }; | |
| 178 | return try array_list.toOwnedSlice(); | |
| 179 | } | |
| 180 | ||
| 181 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. | |
| 182 | /// Reads from the stream until specified byte is found. If the buffer is not | |
| 183 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | |
| 184 | /// If end-of-stream is found, returns the rest of the stream. If this | |
| 185 | /// function is called again after that, returns null. | |
| 186 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | |
| 187 | /// delimiter byte is written to the output buffer but is not included | |
| 188 | /// in the returned slice. | |
| 189 | pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 { | |
| 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 | }, | |
| 195 | ||
| 196 | else => |e| return e, | |
| 197 | }; | |
| 198 | const output = fbs.getWritten(); | |
| 199 | buf[output.len] = delimiter; // emulating old behaviour | |
| 200 | return output; | |
| 201 | } | |
| 202 | ||
| 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. | |
| 223 | } | |
| 224 | } | |
| 225 | ||
| 226 | /// Reads from the stream until specified byte is found, discarding all data, | |
| 227 | /// including the delimiter. | |
| 228 | /// If end-of-stream is found, this function succeeds. | |
| 229 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void { | |
| 230 | while (true) { | |
| 231 | const byte = self.readByte() catch |err| switch (err) { | |
| 232 | error.EndOfStream => return, | |
| 233 | else => |e| return e, | |
| 234 | }; | |
| 235 | if (byte == delimiter) return; | |
| 236 | } | |
| 237 | } | |
| 238 | ||
| 239 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | |
| 240 | pub fn readByte(self: Self) (Error || error{EndOfStream})!u8 { | |
| 241 | var result: [1]u8 = undefined; | |
| 242 | const amt_read = try self.read(result[0..]); | |
| 243 | if (amt_read < 1) return error.EndOfStream; | |
| 244 | return result[0]; | |
| 245 | } | |
| 246 | ||
| 247 | /// Same as `readByte` except the returned byte is signed. | |
| 248 | pub fn readByteSigned(self: Self) (Error || error{EndOfStream})!i8 { | |
| 249 | return @as(i8, @bitCast(try self.readByte())); | |
| 250 | } | |
| 251 | ||
| 252 | /// Reads exactly `num_bytes` bytes and returns as an array. | |
| 253 | /// `num_bytes` must be comptime-known | |
| 254 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) (Error || error{EndOfStream})![num_bytes]u8 { | |
| 255 | var bytes: [num_bytes]u8 = undefined; | |
| 256 | try self.readNoEof(&bytes); | |
| 257 | return bytes; | |
| 258 | } | |
| 259 | ||
| 260 | /// Reads bytes until `bounded.len` is equal to `num_bytes`, | |
| 261 | /// or the stream ends. | |
| 262 | /// | |
| 263 | /// * it is assumed that `num_bytes` will not exceed `bounded.capacity()` | |
| 264 | pub fn readIntoBoundedBytes( | |
| 265 | self: Self, | |
| 266 | comptime num_bytes: usize, | |
| 267 | bounded: *std.BoundedArray(u8, num_bytes), | |
| 268 | ) Error!void { | |
| 269 | while (bounded.len < num_bytes) { | |
| 270 | // get at most the number of bytes free in the bounded array | |
| 271 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); | |
| 272 | if (bytes_read == 0) return; | |
| 273 | ||
| 274 | // bytes_read will never be larger than @TypeOf(bounded.len) | |
| 275 | // due to `self.read` being bounded by `bounded.unusedCapacitySlice()` | |
| 276 | bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read)); | |
| 277 | } | |
| 278 | } | |
| 279 | ||
| 280 | /// Reads at most `num_bytes` and returns as a bounded array. | |
| 281 | pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) Error!std.BoundedArray(u8, num_bytes) { | |
| 282 | var result = std.BoundedArray(u8, num_bytes){}; | |
| 283 | try self.readIntoBoundedBytes(num_bytes, &result); | |
| 284 | return result; | |
| 285 | } | |
| 286 | ||
| 287 | /// Reads a native-endian integer | |
| 288 | pub fn readIntNative(self: Self, comptime T: type) (Error || error{EndOfStream})!T { | |
| 289 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 290 | return mem.readIntNative(T, &bytes); | |
| 291 | } | |
| 292 | ||
| 293 | /// Reads a foreign-endian integer | |
| 294 | pub fn readIntForeign(self: Self, comptime T: type) (Error || error{EndOfStream})!T { | |
| 295 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 296 | return mem.readIntForeign(T, &bytes); | |
| 297 | } | |
| 298 | ||
| 299 | pub fn readIntLittle(self: Self, comptime T: type) !T { | |
| 300 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 301 | return mem.readIntLittle(T, &bytes); | |
| 302 | } | |
| 303 | ||
| 304 | pub fn readIntBig(self: Self, comptime T: type) !T { | |
| 305 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 306 | return mem.readIntBig(T, &bytes); | |
| 307 | } | |
| 308 | ||
| 309 | pub fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) !T { | |
| 310 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 311 | return mem.readInt(T, &bytes, endian); | |
| 312 | } | |
| 313 | ||
| 314 | pub fn readVarInt(self: Self, comptime ReturnType: type, endian: std.builtin.Endian, size: usize) !ReturnType { | |
| 315 | assert(size <= @sizeOf(ReturnType)); | |
| 316 | var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; | |
| 317 | const bytes = bytes_buf[0..size]; | |
| 318 | try self.readNoEof(bytes); | |
| 319 | return mem.readVarInt(ReturnType, bytes, endian); | |
| 320 | } | |
| 321 | ||
| 322 | /// Optional parameters for `skipBytes` | |
| 323 | pub const SkipBytesOptions = struct { | |
| 324 | buf_size: usize = 512, | |
| 325 | }; | |
| 326 | ||
| 327 | // `num_bytes` is a `u64` to match `off_t` | |
| 328 | /// Reads `num_bytes` bytes from the stream and discards them | |
| 329 | pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) !void { | |
| 330 | var buf: [options.buf_size]u8 = undefined; | |
| 331 | var remaining = num_bytes; | |
| 332 | ||
| 333 | while (remaining > 0) { | |
| 334 | const amt = @min(remaining, options.buf_size); | |
| 335 | try self.readNoEof(buf[0..amt]); | |
| 336 | remaining -= amt; | |
| 337 | } | |
| 338 | } | |
| 339 | ||
| 340 | /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice | |
| 341 | pub fn isBytes(self: Self, slice: []const u8) !bool { | |
| 342 | var i: usize = 0; | |
| 343 | var matches = true; | |
| 344 | while (i < slice.len) : (i += 1) { | |
| 345 | if (slice[i] != try self.readByte()) { | |
| 346 | matches = false; | |
| 347 | } | |
| 348 | } | |
| 349 | return matches; | |
| 350 | } | |
| 351 | ||
| 352 | pub fn readStruct(self: Self, comptime T: type) !T { | |
| 353 | // Only extern and packed structs have defined in-memory layout. | |
| 354 | comptime assert(@typeInfo(T).Struct.layout != .Auto); | |
| 355 | var res: [1]T = undefined; | |
| 356 | try self.readNoEof(mem.sliceAsBytes(res[0..])); | |
| 357 | return res[0]; | |
| 358 | } | |
| 359 | ||
| 360 | pub fn readStructBig(self: Self, comptime T: type) !T { | |
| 361 | var res = try self.readStruct(T); | |
| 362 | if (native_endian != std.builtin.Endian.Big) { | |
| 363 | mem.byteSwapAllFields(T, &res); | |
| 364 | } | |
| 365 | return res; | |
| 366 | } | |
| 367 | ||
| 368 | /// Reads an integer with the same size as the given enum's tag type. If the integer matches | |
| 369 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`. | |
| 370 | /// TODO optimization taking advantage of most fields being in order | |
| 371 | pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum { | |
| 372 | const E = error{ | |
| 373 | /// An integer was read, but it did not match any of the tags in the supplied enum. | |
| 374 | InvalidValue, | |
| 375 | }; | |
| 376 | const type_info = @typeInfo(Enum).Enum; | |
| 377 | const tag = try self.readInt(type_info.tag_type, endian); | |
| 378 | ||
| 379 | inline for (std.meta.fields(Enum)) |field| { | |
| 380 | if (tag == field.value) { | |
| 381 | return @field(Enum, field.name); | |
| 382 | } | |
| 383 | } | |
| 384 | ||
| 385 | return E.InvalidValue; | |
| 386 | } | |
| 387 | }; | |
| 388 | } | |
| 389 | ||
| 390 | test "Reader" { | |
| 391 | var buf = "a\x02".*; | |
| 392 | var fis = std.io.fixedBufferStream(&buf); | |
| 393 | const reader = fis.reader(); | |
| 394 | try testing.expect((try reader.readByte()) == 'a'); | |
| 395 | try testing.expect((try reader.readEnum(enum(u8) { | |
| 396 | a = 0, | |
| 397 | b = 99, | |
| 398 | c = 2, | |
| 399 | d = 3, | |
| 400 | }, undefined)) == .c); | |
| 401 | try testing.expectError(error.EndOfStream, reader.readByte()); | |
| 402 | } | |
| 403 | ||
| 404 | test "Reader.isBytes" { | |
| 405 | var fis = std.io.fixedBufferStream("foobar"); | |
| 406 | const reader = fis.reader(); | |
| 407 | try testing.expectEqual(true, try reader.isBytes("foo")); | |
| 408 | try testing.expectEqual(false, try reader.isBytes("qux")); | |
| 409 | } | |
| 410 | ||
| 411 | test "Reader.skipBytes" { | |
| 412 | var fis = std.io.fixedBufferStream("foobar"); | |
| 413 | const reader = fis.reader(); | |
| 414 | try reader.skipBytes(3, .{}); | |
| 415 | try testing.expect(try reader.isBytes("bar")); | |
| 416 | try reader.skipBytes(0, .{}); | |
| 417 | try testing.expectError(error.EndOfStream, reader.skipBytes(1, .{})); | |
| 418 | } | |
| 419 | ||
| 420 | test "Reader.readUntilDelimiterArrayList returns ArrayLists with bytes read until the delimiter, then EndOfStream" { | |
| 421 | const a = std.testing.allocator; | |
| 422 | var list = std.ArrayList(u8).init(a); | |
| 423 | defer list.deinit(); | |
| 424 | ||
| 425 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 426 | const reader = fis.reader(); | |
| 427 | ||
| 428 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 429 | try std.testing.expectEqualStrings("0000", list.items); | |
| 430 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 431 | try std.testing.expectEqualStrings("1234", list.items); | |
| 432 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5)); | |
| 433 | } | |
| 434 | ||
| 435 | test "Reader.readUntilDelimiterArrayList returns an empty ArrayList" { | |
| 436 | const a = std.testing.allocator; | |
| 437 | var list = std.ArrayList(u8).init(a); | |
| 438 | defer list.deinit(); | |
| 439 | ||
| 440 | var fis = std.io.fixedBufferStream("\n"); | |
| 441 | const reader = fis.reader(); | |
| 442 | ||
| 443 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 444 | try std.testing.expectEqualStrings("", list.items); | |
| 445 | } | |
| 446 | ||
| 447 | test "Reader.readUntilDelimiterArrayList returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { | |
| 448 | const a = std.testing.allocator; | |
| 449 | var list = std.ArrayList(u8).init(a); | |
| 450 | defer list.deinit(); | |
| 451 | ||
| 452 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 453 | const reader = fis.reader(); | |
| 454 | ||
| 455 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5)); | |
| 456 | try std.testing.expectEqualStrings("12345", list.items); | |
| 457 | try reader.readUntilDelimiterArrayList(&list, '\n', 5); | |
| 458 | try std.testing.expectEqualStrings("67", list.items); | |
| 459 | } | |
| 460 | ||
| 461 | test "Reader.readUntilDelimiterArrayList returns EndOfStream" { | |
| 462 | const a = std.testing.allocator; | |
| 463 | var list = std.ArrayList(u8).init(a); | |
| 464 | defer list.deinit(); | |
| 465 | ||
| 466 | var fis = std.io.fixedBufferStream("1234"); | |
| 467 | const reader = fis.reader(); | |
| 468 | ||
| 469 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5)); | |
| 470 | try std.testing.expectEqualStrings("1234", list.items); | |
| 471 | } | |
| 472 | ||
| 473 | test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" { | |
| 474 | const a = std.testing.allocator; | |
| 475 | ||
| 476 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 477 | const reader = fis.reader(); | |
| 478 | ||
| 479 | { | |
| 480 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 481 | defer a.free(result); | |
| 482 | try std.testing.expectEqualStrings("0000", result); | |
| 483 | } | |
| 484 | ||
| 485 | { | |
| 486 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 487 | defer a.free(result); | |
| 488 | try std.testing.expectEqualStrings("1234", result); | |
| 489 | } | |
| 490 | ||
| 491 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5)); | |
| 492 | } | |
| 493 | ||
| 494 | test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" { | |
| 495 | const a = std.testing.allocator; | |
| 496 | ||
| 497 | var fis = std.io.fixedBufferStream("\n"); | |
| 498 | const reader = fis.reader(); | |
| 499 | ||
| 500 | { | |
| 501 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 502 | defer a.free(result); | |
| 503 | try std.testing.expectEqualStrings("", result); | |
| 504 | } | |
| 505 | } | |
| 506 | ||
| 507 | test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { | |
| 508 | const a = std.testing.allocator; | |
| 509 | ||
| 510 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 511 | const reader = fis.reader(); | |
| 512 | ||
| 513 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5)); | |
| 514 | ||
| 515 | var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); | |
| 516 | defer a.free(result); | |
| 517 | try std.testing.expectEqualStrings("67", result); | |
| 518 | } | |
| 519 | ||
| 520 | test "Reader.readUntilDelimiterAlloc returns EndOfStream" { | |
| 521 | const a = std.testing.allocator; | |
| 522 | ||
| 523 | var fis = std.io.fixedBufferStream("1234"); | |
| 524 | const reader = fis.reader(); | |
| 525 | ||
| 526 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5)); | |
| 527 | } | |
| 528 | ||
| 529 | test "Reader.readUntilDelimiter returns bytes read until the delimiter" { | |
| 530 | var buf: [5]u8 = undefined; | |
| 531 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 532 | const reader = fis.reader(); | |
| 533 | try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n')); | |
| 534 | try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n')); | |
| 535 | } | |
| 536 | ||
| 537 | test "Reader.readUntilDelimiter returns an empty string" { | |
| 538 | var buf: [5]u8 = undefined; | |
| 539 | var fis = std.io.fixedBufferStream("\n"); | |
| 540 | const reader = fis.reader(); | |
| 541 | try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n')); | |
| 542 | } | |
| 543 | ||
| 544 | test "Reader.readUntilDelimiter returns StreamTooLong, then an empty string" { | |
| 545 | var buf: [5]u8 = undefined; | |
| 546 | var fis = std.io.fixedBufferStream("12345\n"); | |
| 547 | const reader = fis.reader(); | |
| 548 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 549 | try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n')); | |
| 550 | } | |
| 551 | ||
| 552 | test "Reader.readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" { | |
| 553 | var buf: [5]u8 = undefined; | |
| 554 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 555 | const reader = fis.reader(); | |
| 556 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 557 | try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n')); | |
| 558 | } | |
| 559 | ||
| 560 | test "Reader.readUntilDelimiter returns EndOfStream" { | |
| 561 | { | |
| 562 | var buf: [5]u8 = undefined; | |
| 563 | var fis = std.io.fixedBufferStream(""); | |
| 564 | const reader = fis.reader(); | |
| 565 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 566 | } | |
| 567 | { | |
| 568 | var buf: [5]u8 = undefined; | |
| 569 | var fis = std.io.fixedBufferStream("1234"); | |
| 570 | const reader = fis.reader(); | |
| 571 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 572 | } | |
| 573 | } | |
| 574 | ||
| 575 | test "Reader.readUntilDelimiter returns bytes read until delimiter, then EndOfStream" { | |
| 576 | var buf: [5]u8 = undefined; | |
| 577 | var fis = std.io.fixedBufferStream("1234\n"); | |
| 578 | const reader = fis.reader(); | |
| 579 | try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n')); | |
| 580 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 581 | } | |
| 582 | ||
| 583 | test "Reader.readUntilDelimiter returns StreamTooLong, then EndOfStream" { | |
| 584 | var buf: [5]u8 = undefined; | |
| 585 | var fis = std.io.fixedBufferStream("12345"); | |
| 586 | const reader = fis.reader(); | |
| 587 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 588 | try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); | |
| 589 | } | |
| 590 | ||
| 591 | test "Reader.readUntilDelimiter writes all bytes read to the output buffer" { | |
| 592 | var buf: [5]u8 = undefined; | |
| 593 | var fis = std.io.fixedBufferStream("0000\n12345"); | |
| 594 | const reader = fis.reader(); | |
| 595 | _ = try reader.readUntilDelimiter(&buf, '\n'); | |
| 596 | try std.testing.expectEqualStrings("0000\n", &buf); | |
| 597 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); | |
| 598 | try std.testing.expectEqualStrings("12345", &buf); | |
| 599 | } | |
| 600 | ||
| 601 | test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" { | |
| 602 | const a = std.testing.allocator; | |
| 603 | ||
| 604 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 605 | const reader = fis.reader(); | |
| 606 | ||
| 607 | { | |
| 608 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 609 | defer a.free(result); | |
| 610 | try std.testing.expectEqualStrings("0000", result); | |
| 611 | } | |
| 612 | ||
| 613 | { | |
| 614 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 615 | defer a.free(result); | |
| 616 | try std.testing.expectEqualStrings("1234", result); | |
| 617 | } | |
| 618 | ||
| 619 | try std.testing.expect((try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)) == null); | |
| 620 | } | |
| 621 | ||
| 622 | test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" { | |
| 623 | const a = std.testing.allocator; | |
| 624 | ||
| 625 | var fis = std.io.fixedBufferStream("\n"); | |
| 626 | const reader = fis.reader(); | |
| 627 | ||
| 628 | { | |
| 629 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 630 | defer a.free(result); | |
| 631 | try std.testing.expectEqualStrings("", result); | |
| 632 | } | |
| 633 | } | |
| 634 | ||
| 635 | test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { | |
| 636 | const a = std.testing.allocator; | |
| 637 | ||
| 638 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 639 | const reader = fis.reader(); | |
| 640 | ||
| 641 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)); | |
| 642 | ||
| 643 | var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; | |
| 644 | defer a.free(result); | |
| 645 | try std.testing.expectEqualStrings("67", result); | |
| 646 | } | |
| 647 | ||
| 648 | test "Reader.readUntilDelimiterOrEof returns bytes read until the delimiter" { | |
| 649 | var buf: [5]u8 = undefined; | |
| 650 | var fis = std.io.fixedBufferStream("0000\n1234\n"); | |
| 651 | const reader = fis.reader(); | |
| 652 | try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 653 | try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 654 | } | |
| 655 | ||
| 656 | test "Reader.readUntilDelimiterOrEof returns an empty string" { | |
| 657 | var buf: [5]u8 = undefined; | |
| 658 | var fis = std.io.fixedBufferStream("\n"); | |
| 659 | const reader = fis.reader(); | |
| 660 | try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 661 | } | |
| 662 | ||
| 663 | test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then an empty string" { | |
| 664 | var buf: [5]u8 = undefined; | |
| 665 | var fis = std.io.fixedBufferStream("12345\n"); | |
| 666 | const reader = fis.reader(); | |
| 667 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 668 | try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 669 | } | |
| 670 | ||
| 671 | test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" { | |
| 672 | var buf: [5]u8 = undefined; | |
| 673 | var fis = std.io.fixedBufferStream("1234567\n"); | |
| 674 | const reader = fis.reader(); | |
| 675 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 676 | try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 677 | } | |
| 678 | ||
| 679 | test "Reader.readUntilDelimiterOrEof returns null" { | |
| 680 | var buf: [5]u8 = undefined; | |
| 681 | var fis = std.io.fixedBufferStream(""); | |
| 682 | const reader = fis.reader(); | |
| 683 | try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null); | |
| 684 | } | |
| 685 | ||
| 686 | test "Reader.readUntilDelimiterOrEof returns bytes read until delimiter, then null" { | |
| 687 | var buf: [5]u8 = undefined; | |
| 688 | var fis = std.io.fixedBufferStream("1234\n"); | |
| 689 | const reader = fis.reader(); | |
| 690 | try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 691 | try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null); | |
| 692 | } | |
| 693 | ||
| 694 | test "Reader.readUntilDelimiterOrEof returns bytes read until end-of-stream" { | |
| 695 | var buf: [5]u8 = undefined; | |
| 696 | var fis = std.io.fixedBufferStream("1234"); | |
| 697 | const reader = fis.reader(); | |
| 698 | try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 699 | } | |
| 700 | ||
| 701 | test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" { | |
| 702 | var buf: [5]u8 = undefined; | |
| 703 | var fis = std.io.fixedBufferStream("1234567"); | |
| 704 | const reader = fis.reader(); | |
| 705 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 706 | try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); | |
| 707 | } | |
| 708 | ||
| 709 | test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" { | |
| 710 | var buf: [5]u8 = undefined; | |
| 711 | var fis = std.io.fixedBufferStream("0000\n12345"); | |
| 712 | const reader = fis.reader(); | |
| 713 | _ = try reader.readUntilDelimiterOrEof(&buf, '\n'); | |
| 714 | try std.testing.expectEqualStrings("0000\n", &buf); | |
| 715 | try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); | |
| 716 | try std.testing.expectEqualStrings("12345", &buf); | |
| 717 | } | |
| 718 | ||
| 719 | test "Reader.streamUntilDelimiter writes all bytes without delimiter to the output" { | |
| 720 | const input_string = "some_string_with_delimiter!"; | |
| 721 | var input_fbs = std.io.fixedBufferStream(input_string); | |
| 722 | const reader = input_fbs.reader(); | |
| 723 | ||
| 724 | var output: [input_string.len]u8 = undefined; | |
| 725 | var output_fbs = std.io.fixedBufferStream(&output); | |
| 726 | const writer = output_fbs.writer(); | |
| 727 | ||
| 728 | try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len); | |
| 729 | try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten()); | |
| 730 | try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len)); | |
| 731 | ||
| 732 | input_fbs.reset(); | |
| 733 | output_fbs.reset(); | |
| 734 | ||
| 735 | try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5)); | |
| 736 | } | |
| 737 | ||
| 738 | test "Reader.readBoundedBytes correctly reads into a new bounded array" { | |
| 739 | const test_string = "abcdefg"; | |
| 740 | var fis = std.io.fixedBufferStream(test_string); | |
| 741 | const reader = fis.reader(); | |
| 742 | ||
| 743 | var array = try reader.readBoundedBytes(10000); | |
| 744 | try testing.expectEqualStrings(array.slice(), test_string); | |
| 745 | } | |
| 746 | ||
| 747 | test "Reader.readIntoBoundedBytes correctly reads into a provided bounded array" { | |
| 748 | const test_string = "abcdefg"; | |
| 749 | var fis = std.io.fixedBufferStream(test_string); | |
| 750 | const reader = fis.reader(); | |
| 751 | ||
| 752 | var bounded_array = std.BoundedArray(u8, 10000){}; | |
| 753 | ||
| 754 | // compile time error if the size is not the same at the provided `bounded.capacity()` | |
| 755 | try reader.readIntoBoundedBytes(10000, &bounded_array); | |
| 756 | try testing.expectEqualStrings(bounded_array.slice(), test_string); | |
| 757 | } |
lib/std/macho.zig+1-1| ... | ... | @@ -1853,7 +1853,7 @@ pub const data_in_code_entry = extern struct { |
| 1853 | 1853 | |
| 1854 | 1854 | pub const LoadCommandIterator = struct { |
| 1855 | 1855 | ncmds: usize, |
| 1856 | buffer: []align(@alignOf(u64)) const u8, | |
| 1856 | buffer: []const u8, | |
| 1857 | 1857 | index: usize = 0, |
| 1858 | 1858 | |
| 1859 | 1859 | pub const LoadCommand = struct { |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ |