| 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,7 +265,7 @@ set(ZIG_STAGE2_SOURCES |
| 265 | "${CMAKE_SOURCE_DIR}/lib/std/io/find_byte_writer.zig" | 265 | "${CMAKE_SOURCE_DIR}/lib/std/io/find_byte_writer.zig" |
| 266 | "${CMAKE_SOURCE_DIR}/lib/std/io/fixed_buffer_stream.zig" | 266 | "${CMAKE_SOURCE_DIR}/lib/std/io/fixed_buffer_stream.zig" |
| 267 | "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig" | 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 | "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig" | 269 | "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig" |
| 270 | "${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig" | 270 | "${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig" |
| 271 | "${CMAKE_SOURCE_DIR}/lib/std/json.zig" | 271 | "${CMAKE_SOURCE_DIR}/lib/std/json.zig" |
lib/std/Build/Step/CheckObject.zig+2-2| ... | @@ -539,7 +539,7 @@ const MachODumper = struct { | ... | @@ -539,7 +539,7 @@ const MachODumper = struct { |
| 539 | strings: []const u8, | 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 | const gpa = step.owner.allocator; | 543 | const gpa = step.owner.allocator; |
| 544 | var stream = std.io.fixedBufferStream(bytes); | 544 | var stream = std.io.fixedBufferStream(bytes); |
| 545 | const reader = stream.reader(); | 545 | const reader = stream.reader(); |
| ... | @@ -556,7 +556,7 @@ const MachODumper = struct { | ... | @@ -556,7 +556,7 @@ const MachODumper = struct { |
| 556 | var sections = std.ArrayList(macho.section_64).init(gpa); | 556 | var sections = std.ArrayList(macho.section_64).init(gpa); |
| 557 | var imports = std.ArrayList([]const u8).init(gpa); | 557 | var imports = std.ArrayList([]const u8).init(gpa); |
| 558 | 558 | ||
| 559 | var it = LoadCommandIterator{ | 559 | var it: LoadCommandIterator = .{ |
| 560 | .ncmds = hdr.ncmds, | 560 | .ncmds = hdr.ncmds, |
| 561 | .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], | 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,6 +10,7 @@ const fs = std.fs; |
| 10 | const mem = std.mem; | 10 | const mem = std.mem; |
| 11 | const meta = std.meta; | 11 | const meta = std.meta; |
| 12 | const File = std.fs.File; | 12 | const File = std.fs.File; |
| 13 | const Allocator = std.mem.Allocator; | ||
| 13 | 14 | ||
| 14 | pub const Mode = enum { | 15 | pub const Mode = enum { |
| 15 | /// I/O operates normally, waiting for the operating system syscalls to complete. | 16 | /// I/O operates normally, waiting for the operating system syscalls to complete. |
| ... | @@ -105,7 +106,255 @@ pub fn getStdIn() File { | ... | @@ -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 | pub const Writer = @import("io/writer.zig").Writer; | 358 | pub const Writer = @import("io/writer.zig").Writer; |
| 110 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | 359 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 111 | 360 | ||
| ... | @@ -168,7 +417,7 @@ test "null_writer" { | ... | @@ -168,7 +417,7 @@ test "null_writer" { |
| 168 | } | 417 | } |
| 169 | 418 | ||
| 170 | pub fn poll( | 419 | pub fn poll( |
| 171 | allocator: std.mem.Allocator, | 420 | allocator: Allocator, |
| 172 | comptime StreamEnum: type, | 421 | comptime StreamEnum: type, |
| 173 | files: PollFiles(StreamEnum), | 422 | files: PollFiles(StreamEnum), |
| 174 | ) Poller(StreamEnum) { | 423 | ) Poller(StreamEnum) { |
| ... | @@ -418,6 +667,7 @@ pub fn PollFiles(comptime StreamEnum: type) type { | ... | @@ -418,6 +667,7 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 418 | } | 667 | } |
| 419 | 668 | ||
| 420 | test { | 669 | test { |
| 670 | _ = AnyReader; | ||
| 421 | _ = @import("io/bit_reader.zig"); | 671 | _ = @import("io/bit_reader.zig"); |
| 422 | _ = @import("io/bit_writer.zig"); | 672 | _ = @import("io/bit_writer.zig"); |
| 423 | _ = @import("io/buffered_atomic_file.zig"); | 673 | _ = @import("io/buffered_atomic_file.zig"); |
| ... | @@ -427,7 +677,6 @@ test { | ... | @@ -427,7 +677,6 @@ test { |
| 427 | _ = @import("io/counting_writer.zig"); | 677 | _ = @import("io/counting_writer.zig"); |
| 428 | _ = @import("io/counting_reader.zig"); | 678 | _ = @import("io/counting_reader.zig"); |
| 429 | _ = @import("io/fixed_buffer_stream.zig"); | 679 | _ = @import("io/fixed_buffer_stream.zig"); |
| 430 | _ = @import("io/reader.zig"); | ||
| 431 | _ = @import("io/writer.zig"); | 680 | _ = @import("io/writer.zig"); |
| 432 | _ = @import("io/peek_stream.zig"); | 681 | _ = @import("io/peek_stream.zig"); |
| 433 | _ = @import("io/seekable_stream.zig"); | 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,7 +1853,7 @@ pub const data_in_code_entry = extern struct { |
| 1853 | 1853 | ||
| 1854 | pub const LoadCommandIterator = struct { | 1854 | pub const LoadCommandIterator = struct { |
| 1855 | ncmds: usize, | 1855 | ncmds: usize, |
| 1856 | buffer: []align(@alignOf(u64)) const u8, | 1856 | buffer: []const u8, |
| 1857 | index: usize = 0, | 1857 | index: usize = 0, |
| 1858 | 1858 | ||
| 1859 | pub const LoadCommand = struct { | 1859 | pub const LoadCommand = struct { |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ | |||