| author | |
| committer | |
| log | 24441b184f989dc889c33b6422ec5ddd8f385c5e |
| tree | 597e52884b28e688e417e397074ce19d0d275a7c |
| parent | 9fe0ce377c8d74bdfe19b426922a57083de7a94a |
only posix is updated so far
also implement `BufferedReader.readVec`4 files changed, 277 insertions(+), 100 deletions(-)
lib/compiler/std-docs.zig+55-58| ... | @@ -320,69 +320,19 @@ fn buildWasmBinary( | ... | @@ -320,69 +320,19 @@ fn buildWasmBinary( |
| 320 | try sendMessage(child.stdin.?, .update); | 320 | try sendMessage(child.stdin.?, .update); |
| 321 | try sendMessage(child.stdin.?, .exit); | 321 | try sendMessage(child.stdin.?, .exit); |
| 322 | 322 | ||
| 323 | const Header = std.zig.Server.Message.Header; | ||
| 324 | var result: ?Cache.Path = null; | 323 | var result: ?Cache.Path = null; |
| 325 | var result_error_bundle = std.zig.ErrorBundle.empty; | 324 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 326 | 325 | ||
| 327 | const stdout = poller.fifo(.stdout); | 326 | while (true) { |
| 328 | 327 | receiveWasmMessage(arena, context, poller.reader(.stdout), &result, &result_error_bundle) catch |err| switch (err) { | |
| 329 | poll: while (true) { | 328 | error.EndOfStream => break, |
| 330 | while (stdout.readableLength() < @sizeOf(Header)) { | 329 | error.ReadFailed => if (!(try poller.poll())) break, |
| 331 | if (!(try poller.poll())) break :poll; | 330 | else => |e| return e, |
| 332 | } | 331 | }; |
| 333 | const header = stdout.reader().readStruct(Header) catch unreachable; | ||
| 334 | while (stdout.readableLength() < header.bytes_len) { | ||
| 335 | if (!(try poller.poll())) break :poll; | ||
| 336 | } | ||
| 337 | const body = stdout.readableSliceOfLen(header.bytes_len); | ||
| 338 | |||
| 339 | switch (header.tag) { | ||
| 340 | .zig_version => { | ||
| 341 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | ||
| 342 | return error.ZigProtocolVersionMismatch; | ||
| 343 | } | ||
| 344 | }, | ||
| 345 | .error_bundle => { | ||
| 346 | const EbHdr = std.zig.Server.Message.ErrorBundle; | ||
| 347 | const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); | ||
| 348 | const extra_bytes = | ||
| 349 | body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; | ||
| 350 | const string_bytes = | ||
| 351 | body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; | ||
| 352 | // TODO: use @ptrCast when the compiler supports it | ||
| 353 | const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); | ||
| 354 | const extra_array = try arena.alloc(u32, unaligned_extra.len); | ||
| 355 | @memcpy(extra_array, unaligned_extra); | ||
| 356 | result_error_bundle = .{ | ||
| 357 | .string_bytes = try arena.dupe(u8, string_bytes), | ||
| 358 | .extra = extra_array, | ||
| 359 | }; | ||
| 360 | }, | ||
| 361 | .emit_digest => { | ||
| 362 | const EmitDigest = std.zig.Server.Message.EmitDigest; | ||
| 363 | const emit_digest = @as(*align(1) const EmitDigest, @ptrCast(body)); | ||
| 364 | if (!emit_digest.flags.cache_hit) { | ||
| 365 | std.log.info("source changes detected; rebuilt wasm component", .{}); | ||
| 366 | } | ||
| 367 | const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; | ||
| 368 | result = .{ | ||
| 369 | .root_dir = Cache.Directory.cwd(), | ||
| 370 | .sub_path = try std.fs.path.join(arena, &.{ | ||
| 371 | context.global_cache_path, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*), | ||
| 372 | }), | ||
| 373 | }; | ||
| 374 | }, | ||
| 375 | else => {}, // ignore other messages | ||
| 376 | } | ||
| 377 | |||
| 378 | stdout.discard(body.len); | ||
| 379 | } | 332 | } |
| 380 | 333 | ||
| 381 | const stderr = poller.fifo(.stderr); | 334 | if (poller.reader(.stderr).buffer.len > 0) { |
| 382 | if (stderr.readableLength() > 0) { | 335 | std.debug.print("{s}", .{poller.reader(.stderr).bufferContents()}); |
| 383 | const owned_stderr = try stderr.toOwnedSlice(); | ||
| 384 | defer gpa.free(owned_stderr); | ||
| 385 | std.debug.print("{s}", .{owned_stderr}); | ||
| 386 | } | 336 | } |
| 387 | 337 | ||
| 388 | // Send EOF to stdin. | 338 | // Send EOF to stdin. |
| ... | @@ -426,6 +376,53 @@ fn buildWasmBinary( | ... | @@ -426,6 +376,53 @@ fn buildWasmBinary( |
| 426 | }; | 376 | }; |
| 427 | } | 377 | } |
| 428 | 378 | ||
| 379 | fn receiveWasmMessage( | ||
| 380 | arena: Allocator, | ||
| 381 | context: *Context, | ||
| 382 | br: *std.io.BufferedReader, | ||
| 383 | result: *?Cache.Path, | ||
| 384 | result_error_bundle: *std.zig.ErrorBundle, | ||
| 385 | ) !void { | ||
| 386 | // Ensure that we will be able to read the entire message without blocking. | ||
| 387 | const header = try br.peekStructEndian(std.zig.Server.Message.Header, .little); | ||
| 388 | try br.fill(@sizeOf(std.zig.Server.Message.Header) + header.bytes_len); | ||
| 389 | br.toss(@sizeOf(std.zig.Server.Message.Header)); | ||
| 390 | switch (header.tag) { | ||
| 391 | .zig_version => { | ||
| 392 | const body = try br.take(header.bytes_len); | ||
| 393 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | ||
| 394 | return error.ZigProtocolVersionMismatch; | ||
| 395 | } | ||
| 396 | }, | ||
| 397 | .error_bundle => { | ||
| 398 | const eb_hdr = try br.takeStructEndian(std.zig.Server.Message.ErrorBundle, .little); | ||
| 399 | const extra_array = try br.readArrayEndianAlloc(arena, u32, eb_hdr.extra_len, .little); | ||
| 400 | const string_bytes = try br.readAlloc(arena, eb_hdr.string_bytes_len); | ||
| 401 | result_error_bundle.* = .{ | ||
| 402 | .string_bytes = string_bytes, | ||
| 403 | .extra = extra_array, | ||
| 404 | }; | ||
| 405 | }, | ||
| 406 | .emit_digest => { | ||
| 407 | const emit_digest = try br.takeStructEndian(std.zig.Server.Message.EmitDigest, .little); | ||
| 408 | if (!emit_digest.flags.cache_hit) { | ||
| 409 | std.log.info("source changes detected; rebuilt wasm component", .{}); | ||
| 410 | } | ||
| 411 | const digest = try br.takeArray(Cache.bin_digest_len); | ||
| 412 | result.* = .{ | ||
| 413 | .root_dir = Cache.Directory.cwd(), | ||
| 414 | .sub_path = try std.fs.path.join(arena, &.{ | ||
| 415 | context.global_cache_path, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*), | ||
| 416 | }), | ||
| 417 | }; | ||
| 418 | }, | ||
| 419 | else => { | ||
| 420 | // Ignore other messages. | ||
| 421 | try br.discard(header.bytes_len); | ||
| 422 | }, | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 429 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { | 426 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { |
| 430 | const header: std.zig.Client.Message.Header = .{ | 427 | const header: std.zig.Client.Message.Header = .{ |
| 431 | .tag = tag, | 428 | .tag = tag, |
lib/std/fs/File.zig+29| ... | @@ -1341,14 +1341,43 @@ pub const Writer = struct { | ... | @@ -1341,14 +1341,43 @@ pub const Writer = struct { |
| 1341 | } | 1341 | } |
| 1342 | }; | 1342 | }; |
| 1343 | 1343 | ||
| 1344 | /// Defaults to positional reading; falls back to streaming. | ||
| 1345 | /// | ||
| 1346 | /// Positional is more threadsafe, since the global seek position is not | ||
| 1347 | /// affected. | ||
| 1344 | pub fn reader(file: File) Reader { | 1348 | pub fn reader(file: File) Reader { |
| 1345 | return .{ .file = file }; | 1349 | return .{ .file = file }; |
| 1346 | } | 1350 | } |
| 1347 | 1351 | ||
| 1352 | /// Positional is more threadsafe, since the global seek position is not | ||
| 1353 | /// affected, but when such syscalls are not available, preemptively choosing | ||
| 1354 | /// `Reader.Mode.streaming` will skip a failed syscall. | ||
| 1355 | pub fn readerStreaming(file: File) Reader { | ||
| 1356 | return .{ | ||
| 1357 | .file = file, | ||
| 1358 | .mode = .streaming, | ||
| 1359 | .seek_err = error.Unseekable, | ||
| 1360 | }; | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | /// Defaults to positional reading; falls back to streaming. | ||
| 1364 | /// | ||
| 1365 | /// Positional is more threadsafe, since the global seek position is not | ||
| 1366 | /// affected. | ||
| 1348 | pub fn writer(file: File) Writer { | 1367 | pub fn writer(file: File) Writer { |
| 1349 | return .{ .file = file }; | 1368 | return .{ .file = file }; |
| 1350 | } | 1369 | } |
| 1351 | 1370 | ||
| 1371 | /// Positional is more threadsafe, since the global seek position is not | ||
| 1372 | /// affected, but when such syscalls are not available, preemptively choosing | ||
| 1373 | /// `Writer.Mode.streaming` will skip a failed syscall. | ||
| 1374 | pub fn writerStreaming(file: File) Writer { | ||
| 1375 | return .{ | ||
| 1376 | .file = file, | ||
| 1377 | .mode = .streaming, | ||
| 1378 | }; | ||
| 1379 | } | ||
| 1380 | |||
| 1352 | const range_off: windows.LARGE_INTEGER = 0; | 1381 | const range_off: windows.LARGE_INTEGER = 0; |
| 1353 | const range_len: windows.LARGE_INTEGER = 1; | 1382 | const range_len: windows.LARGE_INTEGER = 1; |
| 1354 | 1383 |
lib/std/io.zig+42-37| ... | @@ -46,54 +46,57 @@ pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAt | ... | @@ -46,54 +46,57 @@ pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAt |
| 46 | pub const tty = @import("io/tty.zig"); | 46 | pub const tty = @import("io/tty.zig"); |
| 47 | 47 | ||
| 48 | pub fn poll( | 48 | pub fn poll( |
| 49 | allocator: Allocator, | 49 | gpa: Allocator, |
| 50 | comptime StreamEnum: type, | 50 | comptime StreamEnum: type, |
| 51 | files: PollFiles(StreamEnum), | 51 | files: PollFiles(StreamEnum), |
| 52 | ) Poller(StreamEnum) { | 52 | ) Poller(StreamEnum) { |
| 53 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; | 53 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 54 | var result: Poller(StreamEnum) = undefined; | 54 | var result: Poller(StreamEnum) = .{ |
| 55 | 55 | .gpa = gpa, | |
| 56 | if (is_windows) result.windows = .{ | 56 | .readers = undefined, |
| 57 | .first_read_done = false, | 57 | .poll_fds = undefined, |
| 58 | .overlapped = [1]windows.OVERLAPPED{ | 58 | .windows = if (is_windows) .{ |
| 59 | mem.zeroes(windows.OVERLAPPED), | 59 | .first_read_done = false, |
| 60 | } ** enum_fields.len, | 60 | .overlapped = [1]windows.OVERLAPPED{ |
| 61 | .small_bufs = undefined, | 61 | mem.zeroes(windows.OVERLAPPED), |
| 62 | .active = .{ | 62 | } ** enum_fields.len, |
| 63 | .count = 0, | 63 | .small_bufs = undefined, |
| 64 | .handles_buf = undefined, | 64 | .active = .{ |
| 65 | .stream_map = undefined, | 65 | .count = 0, |
| 66 | }, | 66 | .handles_buf = undefined, |
| 67 | .stream_map = undefined, | ||
| 68 | }, | ||
| 69 | } else {}, | ||
| 67 | }; | 70 | }; |
| 68 | 71 | ||
| 69 | inline for (0..enum_fields.len) |i| { | 72 | inline for (enum_fields, 0..) |field, i| { |
| 70 | result.fifos[i] = .{ | 73 | result.readers[i] = .{ |
| 71 | .allocator = allocator, | 74 | .unbuffered_reader = .failing, |
| 72 | .buf = &.{}, | 75 | .buffer = &.{}, |
| 73 | .head = 0, | 76 | .end = 0, |
| 74 | .count = 0, | 77 | .seek = 0, |
| 75 | }; | 78 | }; |
| 76 | if (is_windows) { | 79 | if (is_windows) { |
| 77 | result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle; | 80 | result.windows.active.handles_buf[i] = @field(files, field.name).handle; |
| 78 | } else { | 81 | } else { |
| 79 | result.poll_fds[i] = .{ | 82 | result.poll_fds[i] = .{ |
| 80 | .fd = @field(files, enum_fields[i].name).handle, | 83 | .fd = @field(files, field.name).handle, |
| 81 | .events = posix.POLL.IN, | 84 | .events = posix.POLL.IN, |
| 82 | .revents = undefined, | 85 | .revents = undefined, |
| 83 | }; | 86 | }; |
| 84 | } | 87 | } |
| 85 | } | 88 | } |
| 89 | |||
| 86 | return result; | 90 | return result; |
| 87 | } | 91 | } |
| 88 | 92 | ||
| 89 | pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic); | ||
| 90 | |||
| 91 | pub fn Poller(comptime StreamEnum: type) type { | 93 | pub fn Poller(comptime StreamEnum: type) type { |
| 92 | return struct { | 94 | return struct { |
| 93 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; | 95 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 94 | const PollFd = if (is_windows) void else posix.pollfd; | 96 | const PollFd = if (is_windows) void else posix.pollfd; |
| 95 | 97 | ||
| 96 | fifos: [enum_fields.len]PollFifo, | 98 | gpa: Allocator, |
| 99 | readers: [enum_fields.len]BufferedReader, | ||
| 97 | poll_fds: [enum_fields.len]PollFd, | 100 | poll_fds: [enum_fields.len]PollFd, |
| 98 | windows: if (is_windows) struct { | 101 | windows: if (is_windows) struct { |
| 99 | first_read_done: bool, | 102 | first_read_done: bool, |
| ... | @@ -105,7 +108,7 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -105,7 +108,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 105 | stream_map: [enum_fields.len]StreamEnum, | 108 | stream_map: [enum_fields.len]StreamEnum, |
| 106 | 109 | ||
| 107 | pub fn removeAt(self: *@This(), index: u32) void { | 110 | pub fn removeAt(self: *@This(), index: u32) void { |
| 108 | std.debug.assert(index < self.count); | 111 | assert(index < self.count); |
| 109 | for (index + 1..self.count) |i| { | 112 | for (index + 1..self.count) |i| { |
| 110 | self.handles_buf[i - 1] = self.handles_buf[i]; | 113 | self.handles_buf[i - 1] = self.handles_buf[i]; |
| 111 | self.stream_map[i - 1] = self.stream_map[i]; | 114 | self.stream_map[i - 1] = self.stream_map[i]; |
| ... | @@ -118,13 +121,14 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -118,13 +121,14 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 118 | const Self = @This(); | 121 | const Self = @This(); |
| 119 | 122 | ||
| 120 | pub fn deinit(self: *Self) void { | 123 | pub fn deinit(self: *Self) void { |
| 124 | const gpa = self.gpa; | ||
| 121 | if (is_windows) { | 125 | if (is_windows) { |
| 122 | // cancel any pending IO to prevent clobbering OVERLAPPED value | 126 | // cancel any pending IO to prevent clobbering OVERLAPPED value |
| 123 | for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| { | 127 | for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| { |
| 124 | _ = windows.kernel32.CancelIo(h); | 128 | _ = windows.kernel32.CancelIo(h); |
| 125 | } | 129 | } |
| 126 | } | 130 | } |
| 127 | inline for (&self.fifos) |*q| q.deinit(); | 131 | inline for (&self.readers) |*br| gpa.free(br.buffer); |
| 128 | self.* = undefined; | 132 | self.* = undefined; |
| 129 | } | 133 | } |
| 130 | 134 | ||
| ... | @@ -144,8 +148,8 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -144,8 +148,8 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 144 | } | 148 | } |
| 145 | } | 149 | } |
| 146 | 150 | ||
| 147 | pub inline fn fifo(self: *Self, comptime which: StreamEnum) *PollFifo { | 151 | pub inline fn reader(self: *Self, comptime which: StreamEnum) *BufferedReader { |
| 148 | return &self.fifos[@intFromEnum(which)]; | 152 | return &self.readers[@intFromEnum(which)]; |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 151 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { | 155 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { |
| ... | @@ -236,6 +240,7 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -236,6 +240,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 236 | } | 240 | } |
| 237 | 241 | ||
| 238 | fn pollPosix(self: *Self, nanoseconds: ?u64) !bool { | 242 | fn pollPosix(self: *Self, nanoseconds: ?u64) !bool { |
| 243 | const gpa = self.gpa; | ||
| 239 | // We ask for ensureUnusedCapacity with this much extra space. This | 244 | // We ask for ensureUnusedCapacity with this much extra space. This |
| 240 | // has more of an effect on small reads because once the reads | 245 | // has more of an effect on small reads because once the reads |
| 241 | // start to get larger the amount of space an ArrayList will | 246 | // start to get larger the amount of space an ArrayList will |
| ... | @@ -255,18 +260,18 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -255,18 +260,18 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 255 | } | 260 | } |
| 256 | 261 | ||
| 257 | var keep_polling = false; | 262 | var keep_polling = false; |
| 258 | inline for (&self.poll_fds, &self.fifos) |*poll_fd, *q| { | 263 | inline for (&self.poll_fds, &self.readers) |*poll_fd, *br| { |
| 259 | // Try reading whatever is available before checking the error | 264 | // Try reading whatever is available before checking the error |
| 260 | // conditions. | 265 | // conditions. |
| 261 | // It's still possible to read after a POLL.HUP is received, | 266 | // It's still possible to read after a POLL.HUP is received, |
| 262 | // always check if there's some data waiting to be read first. | 267 | // always check if there's some data waiting to be read first. |
| 263 | if (poll_fd.revents & posix.POLL.IN != 0) { | 268 | if (poll_fd.revents & posix.POLL.IN != 0) { |
| 264 | const buf = try q.writableWithSize(bump_amt); | 269 | const buf = try br.writableSliceGreedyAlloc(gpa, bump_amt); |
| 265 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { | 270 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { |
| 266 | error.BrokenPipe => 0, // Handle the same as EOF. | 271 | error.BrokenPipe => 0, // Handle the same as EOF. |
| 267 | else => |e| return e, | 272 | else => |e| return e, |
| 268 | }; | 273 | }; |
| 269 | q.update(amt); | 274 | br.advanceBufferEnd(amt); |
| 270 | if (amt == 0) { | 275 | if (amt == 0) { |
| 271 | // Remove the fd when the EOF condition is met. | 276 | // Remove the fd when the EOF condition is met. |
| 272 | poll_fd.fd = -1; | 277 | poll_fd.fd = -1; |
| ... | @@ -297,14 +302,14 @@ var win_dummy_bytes_read: u32 = undefined; | ... | @@ -297,14 +302,14 @@ var win_dummy_bytes_read: u32 = undefined; |
| 297 | fn windowsAsyncReadToFifoAndQueueSmallRead( | 302 | fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 298 | handle: windows.HANDLE, | 303 | handle: windows.HANDLE, |
| 299 | overlapped: *windows.OVERLAPPED, | 304 | overlapped: *windows.OVERLAPPED, |
| 300 | fifo: *PollFifo, | 305 | br: *BufferedReader, |
| 301 | small_buf: *[128]u8, | 306 | small_buf: *[128]u8, |
| 302 | bump_amt: usize, | 307 | bump_amt: usize, |
| 303 | ) !enum { empty, populated, closed_populated, closed } { | 308 | ) !enum { empty, populated, closed_populated, closed } { |
| 304 | var read_any_data = false; | 309 | var read_any_data = false; |
| 305 | while (true) { | 310 | while (true) { |
| 306 | const fifo_read_pending = while (true) { | 311 | const fifo_read_pending = while (true) { |
| 307 | const buf = try fifo.writableWithSize(bump_amt); | 312 | const buf = try br.writableWithSize(bump_amt); |
| 308 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); | 313 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); |
| 309 | 314 | ||
| 310 | if (0 == windows.kernel32.ReadFile( | 315 | if (0 == windows.kernel32.ReadFile( |
| ... | @@ -326,7 +331,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( | ... | @@ -326,7 +331,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 326 | }; | 331 | }; |
| 327 | 332 | ||
| 328 | read_any_data = true; | 333 | read_any_data = true; |
| 329 | fifo.update(num_bytes_read); | 334 | br.update(num_bytes_read); |
| 330 | 335 | ||
| 331 | if (num_bytes_read == buf_len) { | 336 | if (num_bytes_read == buf_len) { |
| 332 | // We filled the buffer, so there's probably more data available. | 337 | // We filled the buffer, so there's probably more data available. |
| ... | @@ -356,7 +361,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( | ... | @@ -356,7 +361,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 356 | .aborted => break :cancel_read, | 361 | .aborted => break :cancel_read, |
| 357 | }; | 362 | }; |
| 358 | read_any_data = true; | 363 | read_any_data = true; |
| 359 | fifo.update(num_bytes_read); | 364 | br.update(num_bytes_read); |
| 360 | } | 365 | } |
| 361 | 366 | ||
| 362 | // Try to queue the 1-byte read. | 367 | // Try to queue the 1-byte read. |
| ... | @@ -381,7 +386,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( | ... | @@ -381,7 +386,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 381 | .closed => return if (read_any_data) .closed_populated else .closed, | 386 | .closed => return if (read_any_data) .closed_populated else .closed, |
| 382 | .aborted => unreachable, | 387 | .aborted => unreachable, |
| 383 | }; | 388 | }; |
| 384 | try fifo.write(small_buf[0..num_bytes_read]); | 389 | try br.write(small_buf[0..num_bytes_read]); |
| 385 | read_any_data = true; | 390 | read_any_data = true; |
| 386 | } | 391 | } |
| 387 | } | 392 | } |
lib/std/io/BufferedReader.zig+151-5| ... | @@ -6,6 +6,7 @@ const assert = std.debug.assert; | ... | @@ -6,6 +6,7 @@ const assert = std.debug.assert; |
| 6 | const testing = std.testing; | 6 | const testing = std.testing; |
| 7 | const BufferedWriter = std.io.BufferedWriter; | 7 | const BufferedWriter = std.io.BufferedWriter; |
| 8 | const Reader = std.io.Reader; | 8 | const Reader = std.io.Reader; |
| 9 | const Allocator = std.mem.Allocator; | ||
| 9 | 10 | ||
| 10 | const BufferedReader = @This(); | 11 | const BufferedReader = @This(); |
| 11 | 12 | ||
| ... | @@ -46,13 +47,17 @@ pub fn reader(br: *BufferedReader) Reader { | ... | @@ -46,13 +47,17 @@ pub fn reader(br: *BufferedReader) Reader { |
| 46 | }; | 47 | }; |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 50 | pub fn readVec(br: *BufferedReader, data: []const []u8) Reader.Error!usize { | ||
| 51 | return passthruReadVec(br, data); | ||
| 52 | } | ||
| 53 | |||
| 49 | fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize { | 54 | fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize { |
| 50 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); | 55 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 51 | const buffer = br.buffer[0..br.end]; | 56 | const buffer = br.buffer[0..br.end]; |
| 52 | const buffered = buffer[br.seek..]; | 57 | const buffered = buffer[br.seek..]; |
| 53 | const limited = buffered[0..limit.min(buffered.len)]; | 58 | const limited = buffered[0..limit.min(buffered.len)]; |
| 54 | if (limited.len > 0) { | 59 | if (limited.len > 0) { |
| 55 | const n = try bw.writeSplat(limited, 1); | 60 | const n = try bw.write(limited); |
| 56 | br.seek += n; | 61 | br.seek += n; |
| 57 | return n; | 62 | return n; |
| 58 | } | 63 | } |
| ... | @@ -61,9 +66,44 @@ fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Read | ... | @@ -61,9 +66,44 @@ fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Read |
| 61 | 66 | ||
| 62 | fn passthruReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Error!usize { | 67 | fn passthruReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Error!usize { |
| 63 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); | 68 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 64 | _ = br; | 69 | var total: usize = 0; |
| 65 | _ = data; | 70 | for (data, 0..) |buf, i| { |
| 66 | @panic("TODO"); | 71 | const buffered = br.buffer[br.seek..br.end]; |
| 72 | const copy_len = @min(buffered.len, buf.len); | ||
| 73 | @memcpy(buf[0..copy_len], buffered[0..copy_len]); | ||
| 74 | total += copy_len; | ||
| 75 | br.seek += copy_len; | ||
| 76 | if (copy_len < buf.len) { | ||
| 77 | br.seek = 0; | ||
| 78 | br.end = 0; | ||
| 79 | var vecs: [8][]u8 = undefined; // Arbitrarily chosen value. | ||
| 80 | vecs[0] = buf[copy_len..]; | ||
| 81 | const vecs_len: usize = @min(vecs.len, data.len - i); | ||
| 82 | var vec_data_len: usize = vecs[0].len; | ||
| 83 | for (&vecs[1..vecs_len], data[i + 1 ..][0 .. vecs_len - 1]) |*v, d| { | ||
| 84 | vec_data_len += d.len; | ||
| 85 | v.* = d; | ||
| 86 | } | ||
| 87 | if (vecs_len < vecs.len) { | ||
| 88 | vecs[vecs_len] = br.buffer; | ||
| 89 | const n = try br.unbuffered_reader.readVec(vecs[0 .. vecs_len + 1]); | ||
| 90 | total += @min(n, vec_data_len); | ||
| 91 | br.end = n -| vec_data_len; | ||
| 92 | return total; | ||
| 93 | } | ||
| 94 | if (vecs[vecs.len - 1].len >= br.buffer.len) { | ||
| 95 | total += try br.unbuffered_reader.readVec(&vecs); | ||
| 96 | return total; | ||
| 97 | } | ||
| 98 | vec_data_len -= vecs[vecs.len - 1].len; | ||
| 99 | vecs[vecs.len - 1] = br.buffer; | ||
| 100 | const n = try br.unbuffered_reader.readVec(&vecs); | ||
| 101 | total += @min(n, vec_data_len); | ||
| 102 | br.end = n -| vec_data_len; | ||
| 103 | return total; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | return total; | ||
| 67 | } | 107 | } |
| 68 | 108 | ||
| 69 | pub fn seekBy(br: *BufferedReader, seek_by: i64) !void { | 109 | pub fn seekBy(br: *BufferedReader, seek_by: i64) !void { |
| ... | @@ -147,7 +187,7 @@ pub fn take(br: *BufferedReader, n: usize) Reader.Error![]u8 { | ... | @@ -147,7 +187,7 @@ pub fn take(br: *BufferedReader, n: usize) Reader.Error![]u8 { |
| 147 | } | 187 | } |
| 148 | 188 | ||
| 149 | /// Returns the next `n` bytes from `unbuffered_reader` as an array, filling | 189 | /// Returns the next `n` bytes from `unbuffered_reader` as an array, filling |
| 150 | /// the buffer as necessary. | 190 | /// the buffer as necessary and advancing the seek position `n` bytes. |
| 151 | /// | 191 | /// |
| 152 | /// Asserts that the `BufferedReader` was initialized with a buffer capacity at | 192 | /// Asserts that the `BufferedReader` was initialized with a buffer capacity at |
| 153 | /// least as big as `n`. | 193 | /// least as big as `n`. |
| ... | @@ -161,6 +201,22 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 { | ... | @@ -161,6 +201,22 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 { |
| 161 | return (try br.take(n))[0..n]; | 201 | return (try br.take(n))[0..n]; |
| 162 | } | 202 | } |
| 163 | 203 | ||
| 204 | /// Returns the next `n` bytes from `unbuffered_reader` as an array, filling | ||
| 205 | /// the buffer as necessary, without advancing the seek position. | ||
| 206 | /// | ||
| 207 | /// Asserts that the `BufferedReader` was initialized with a buffer capacity at | ||
| 208 | /// least as big as `n`. | ||
| 209 | /// | ||
| 210 | /// If there are fewer than `n` bytes left in the stream, `error.EndOfStream` | ||
| 211 | /// is returned instead. | ||
| 212 | /// | ||
| 213 | /// See also: | ||
| 214 | /// * `peek` | ||
| 215 | /// * `takeArray` | ||
| 216 | pub fn peekArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 { | ||
| 217 | return (try br.peek(n))[0..n]; | ||
| 218 | } | ||
| 219 | |||
| 164 | /// Skips the next `n` bytes from the stream, advancing the seek position. | 220 | /// Skips the next `n` bytes from the stream, advancing the seek position. |
| 165 | /// | 221 | /// |
| 166 | /// Unlike `toss` which is infallible, in this function `n` can be any amount. | 222 | /// Unlike `toss` which is infallible, in this function `n` can be any amount. |
| ... | @@ -255,6 +311,31 @@ pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize { | ... | @@ -255,6 +311,31 @@ pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize { |
| 255 | @panic("TODO"); | 311 | @panic("TODO"); |
| 256 | } | 312 | } |
| 257 | 313 | ||
| 314 | /// The function is inline to avoid the dead code in case `endian` is | ||
| 315 | /// comptime-known and matches host endianness. | ||
| 316 | pub inline fn readArrayEndianAlloc( | ||
| 317 | br: *BufferedReader, | ||
| 318 | allocator: Allocator, | ||
| 319 | Elem: type, | ||
| 320 | len: usize, | ||
| 321 | endian: std.builtin.Endian, | ||
| 322 | ) ReadAllocError![]Elem { | ||
| 323 | const dest = try allocator.alloc(Elem, len); | ||
| 324 | errdefer allocator.free(dest); | ||
| 325 | try read(br, @ptrCast(dest)); | ||
| 326 | if (native_endian != endian) std.mem.byteSwapAllFields(Elem, dest); | ||
| 327 | return dest; | ||
| 328 | } | ||
| 329 | |||
| 330 | pub const ReadAllocError = Reader.Error || Allocator.Error; | ||
| 331 | |||
| 332 | pub fn readAlloc(br: *BufferedReader, allocator: Allocator, len: usize) ReadAllocError![]u8 { | ||
| 333 | const dest = try allocator.alloc(u8, len); | ||
| 334 | errdefer allocator.free(dest); | ||
| 335 | try read(br, dest); | ||
| 336 | return dest; | ||
| 337 | } | ||
| 338 | |||
| 258 | pub const DelimiterInclusiveError = error{ | 339 | pub const DelimiterInclusiveError = error{ |
| 259 | /// See the `Reader` implementation for detailed diagnostics. | 340 | /// See the `Reader` implementation for detailed diagnostics. |
| 260 | ReadFailed, | 341 | ReadFailed, |
| ... | @@ -498,12 +579,29 @@ pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.E | ... | @@ -498,12 +579,29 @@ pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.E |
| 498 | } | 579 | } |
| 499 | 580 | ||
| 500 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. | 581 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 582 | /// | ||
| 583 | /// Advances the seek position. | ||
| 584 | /// | ||
| 585 | /// See also: | ||
| 586 | /// * `peekStruct` | ||
| 501 | pub fn takeStruct(br: *BufferedReader, comptime T: type) Reader.Error!*align(1) T { | 587 | pub fn takeStruct(br: *BufferedReader, comptime T: type) Reader.Error!*align(1) T { |
| 502 | // Only extern and packed structs have defined in-memory layout. | 588 | // Only extern and packed structs have defined in-memory layout. |
| 503 | comptime assert(@typeInfo(T).@"struct".layout != .auto); | 589 | comptime assert(@typeInfo(T).@"struct".layout != .auto); |
| 504 | return @ptrCast(try br.takeArray(@sizeOf(T))); | 590 | return @ptrCast(try br.takeArray(@sizeOf(T))); |
| 505 | } | 591 | } |
| 506 | 592 | ||
| 593 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. | ||
| 594 | /// | ||
| 595 | /// Does not advance the seek position. | ||
| 596 | /// | ||
| 597 | /// See also: | ||
| 598 | /// * `takeStruct` | ||
| 599 | pub fn peekStruct(br: *BufferedReader, comptime T: type) Reader.Error!*align(1) T { | ||
| 600 | // Only extern and packed structs have defined in-memory layout. | ||
| 601 | comptime assert(@typeInfo(T).@"struct".layout != .auto); | ||
| 602 | return @ptrCast(try br.peekArray(@sizeOf(T))); | ||
| 603 | } | ||
| 604 | |||
| 507 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. | 605 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 508 | /// | 606 | /// |
| 509 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` | 607 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` |
| ... | @@ -514,6 +612,16 @@ pub inline fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: st | ... | @@ -514,6 +612,16 @@ pub inline fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: st |
| 514 | return res; | 612 | return res; |
| 515 | } | 613 | } |
| 516 | 614 | ||
| 615 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. | ||
| 616 | /// | ||
| 617 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` | ||
| 618 | /// when `endian` is comptime-known and matches the host endianness. | ||
| 619 | pub inline fn peekStructEndian(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) Reader.Error!T { | ||
| 620 | var res = (try br.peekStruct(T)).*; | ||
| 621 | if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); | ||
| 622 | return res; | ||
| 623 | } | ||
| 624 | |||
| 517 | /// Reads an integer with the same size as the given enum's tag type. If the | 625 | /// Reads an integer with the same size as the given enum's tag type. If the |
| 518 | /// integer matches an enum tag, casts the integer to the enum tag and returns | 626 | /// integer matches an enum tag, casts the integer to the enum tag and returns |
| 519 | /// it. Otherwise, returns `error.InvalidEnumTag`. | 627 | /// it. Otherwise, returns `error.InvalidEnumTag`. |
| ... | @@ -536,6 +644,28 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Re | ... | @@ -536,6 +644,28 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Re |
| 536 | } }))) orelse error.Overflow; | 644 | } }))) orelse error.Overflow; |
| 537 | } | 645 | } |
| 538 | 646 | ||
| 647 | /// Returns a slice into the unused capacity of `buffer` with at least | ||
| 648 | /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary. | ||
| 649 | /// | ||
| 650 | /// After calling this function, typically the caller will follow up with a | ||
| 651 | /// call to `advanceBufferEnd` to report the actual number of bytes buffered. | ||
| 652 | pub fn writableSliceGreedyAlloc( | ||
| 653 | br: *BufferedReader, | ||
| 654 | allocator: Allocator, | ||
| 655 | min_len: usize, | ||
| 656 | ) error{OutOfMemory}![]u8 { | ||
| 657 | _ = br; | ||
| 658 | _ = allocator; | ||
| 659 | _ = min_len; | ||
| 660 | @panic("TODO"); | ||
| 661 | } | ||
| 662 | |||
| 663 | /// After writing directly into the unused capacity of `buffer`, this function | ||
| 664 | /// updates `end` so that users of `BufferedReader` can receive the data. | ||
| 665 | pub fn advanceBufferEnd(br: *BufferedReader, n: usize) void { | ||
| 666 | br.end += n; | ||
| 667 | } | ||
| 668 | |||
| 539 | fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result { | 669 | fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result { |
| 540 | const result_info = @typeInfo(Result).int; | 670 | const result_info = @typeInfo(Result).int; |
| 541 | comptime assert(result_info.bits % 7 == 0); | 671 | comptime assert(result_info.bits % 7 == 0); |
| ... | @@ -599,6 +729,10 @@ test takeArray { | ... | @@ -599,6 +729,10 @@ test takeArray { |
| 599 | return error.Unimplemented; | 729 | return error.Unimplemented; |
| 600 | } | 730 | } |
| 601 | 731 | ||
| 732 | test peekArray { | ||
| 733 | return error.Unimplemented; | ||
| 734 | } | ||
| 735 | |||
| 602 | test discard { | 736 | test discard { |
| 603 | var br: BufferedReader = undefined; | 737 | var br: BufferedReader = undefined; |
| 604 | br.initFixed("foobar"); | 738 | br.initFixed("foobar"); |
| ... | @@ -684,10 +818,18 @@ test takeStruct { | ... | @@ -684,10 +818,18 @@ test takeStruct { |
| 684 | return error.Unimplemented; | 818 | return error.Unimplemented; |
| 685 | } | 819 | } |
| 686 | 820 | ||
| 821 | test peekStruct { | ||
| 822 | return error.Unimplemented; | ||
| 823 | } | ||
| 824 | |||
| 687 | test takeStructEndian { | 825 | test takeStructEndian { |
| 688 | return error.Unimplemented; | 826 | return error.Unimplemented; |
| 689 | } | 827 | } |
| 690 | 828 | ||
| 829 | test peekStructEndian { | ||
| 830 | return error.Unimplemented; | ||
| 831 | } | ||
| 832 | |||
| 691 | test takeEnum { | 833 | test takeEnum { |
| 692 | return error.Unimplemented; | 834 | return error.Unimplemented; |
| 693 | } | 835 | } |
| ... | @@ -699,3 +841,7 @@ test takeLeb128 { | ... | @@ -699,3 +841,7 @@ test takeLeb128 { |
| 699 | test readShort { | 841 | test readShort { |
| 700 | return error.Unimplemented; | 842 | return error.Unimplemented; |
| 701 | } | 843 | } |
| 844 | |||
| 845 | test readVec { | ||
| 846 | return error.Unimplemented; | ||
| 847 | } |