| 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 | 320 | try sendMessage(child.stdin.?, .update); |
| 321 | 321 | try sendMessage(child.stdin.?, .exit); |
| 322 | 322 | |
| 323 | const Header = std.zig.Server.Message.Header; | |
| 324 | 323 | var result: ?Cache.Path = null; |
| 325 | 324 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 326 | 325 | |
| 327 | const stdout = poller.fifo(.stdout); | |
| 328 | ||
| 329 | poll: while (true) { | |
| 330 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 331 | if (!(try poller.poll())) break :poll; | |
| 332 | } | |
| 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); | |
| 326 | while (true) { | |
| 327 | receiveWasmMessage(arena, context, poller.reader(.stdout), &result, &result_error_bundle) catch |err| switch (err) { | |
| 328 | error.EndOfStream => break, | |
| 329 | error.ReadFailed => if (!(try poller.poll())) break, | |
| 330 | else => |e| return e, | |
| 331 | }; | |
| 379 | 332 | } |
| 380 | 333 | |
| 381 | const stderr = poller.fifo(.stderr); | |
| 382 | if (stderr.readableLength() > 0) { | |
| 383 | const owned_stderr = try stderr.toOwnedSlice(); | |
| 384 | defer gpa.free(owned_stderr); | |
| 385 | std.debug.print("{s}", .{owned_stderr}); | |
| 334 | if (poller.reader(.stderr).buffer.len > 0) { | |
| 335 | std.debug.print("{s}", .{poller.reader(.stderr).bufferContents()}); | |
| 386 | 336 | } |
| 387 | 337 | |
| 388 | 338 | // Send EOF to stdin. |
| ... | ... | @@ -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 | 426 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { |
| 430 | 427 | const header: std.zig.Client.Message.Header = .{ |
| 431 | 428 | .tag = tag, |
lib/std/fs/File.zig+29| ... | ... | @@ -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 | 1348 | pub fn reader(file: File) Reader { |
| 1345 | 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 | 1367 | pub fn writer(file: File) Writer { |
| 1349 | 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 | 1381 | const range_off: windows.LARGE_INTEGER = 0; |
| 1353 | 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 | 46 | pub const tty = @import("io/tty.zig"); |
| 47 | 47 | |
| 48 | 48 | pub fn poll( |
| 49 | allocator: Allocator, | |
| 49 | gpa: Allocator, | |
| 50 | 50 | comptime StreamEnum: type, |
| 51 | 51 | files: PollFiles(StreamEnum), |
| 52 | 52 | ) Poller(StreamEnum) { |
| 53 | 53 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 54 | var result: Poller(StreamEnum) = undefined; | |
| 55 | ||
| 56 | if (is_windows) result.windows = .{ | |
| 57 | .first_read_done = false, | |
| 58 | .overlapped = [1]windows.OVERLAPPED{ | |
| 59 | mem.zeroes(windows.OVERLAPPED), | |
| 60 | } ** enum_fields.len, | |
| 61 | .small_bufs = undefined, | |
| 62 | .active = .{ | |
| 63 | .count = 0, | |
| 64 | .handles_buf = undefined, | |
| 65 | .stream_map = undefined, | |
| 66 | }, | |
| 54 | var result: Poller(StreamEnum) = .{ | |
| 55 | .gpa = gpa, | |
| 56 | .readers = undefined, | |
| 57 | .poll_fds = undefined, | |
| 58 | .windows = if (is_windows) .{ | |
| 59 | .first_read_done = false, | |
| 60 | .overlapped = [1]windows.OVERLAPPED{ | |
| 61 | mem.zeroes(windows.OVERLAPPED), | |
| 62 | } ** enum_fields.len, | |
| 63 | .small_bufs = undefined, | |
| 64 | .active = .{ | |
| 65 | .count = 0, | |
| 66 | .handles_buf = undefined, | |
| 67 | .stream_map = undefined, | |
| 68 | }, | |
| 69 | } else {}, | |
| 67 | 70 | }; |
| 68 | 71 | |
| 69 | inline for (0..enum_fields.len) |i| { | |
| 70 | result.fifos[i] = .{ | |
| 71 | .allocator = allocator, | |
| 72 | .buf = &.{}, | |
| 73 | .head = 0, | |
| 74 | .count = 0, | |
| 72 | inline for (enum_fields, 0..) |field, i| { | |
| 73 | result.readers[i] = .{ | |
| 74 | .unbuffered_reader = .failing, | |
| 75 | .buffer = &.{}, | |
| 76 | .end = 0, | |
| 77 | .seek = 0, | |
| 75 | 78 | }; |
| 76 | 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 | 81 | } else { |
| 79 | 82 | result.poll_fds[i] = .{ |
| 80 | .fd = @field(files, enum_fields[i].name).handle, | |
| 83 | .fd = @field(files, field.name).handle, | |
| 81 | 84 | .events = posix.POLL.IN, |
| 82 | 85 | .revents = undefined, |
| 83 | 86 | }; |
| 84 | 87 | } |
| 85 | 88 | } |
| 89 | ||
| 86 | 90 | return result; |
| 87 | 91 | } |
| 88 | 92 | |
| 89 | pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic); | |
| 90 | ||
| 91 | 93 | pub fn Poller(comptime StreamEnum: type) type { |
| 92 | 94 | return struct { |
| 93 | 95 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 94 | 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 | 100 | poll_fds: [enum_fields.len]PollFd, |
| 98 | 101 | windows: if (is_windows) struct { |
| 99 | 102 | first_read_done: bool, |
| ... | ... | @@ -105,7 +108,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 105 | 108 | stream_map: [enum_fields.len]StreamEnum, |
| 106 | 109 | |
| 107 | 110 | pub fn removeAt(self: *@This(), index: u32) void { |
| 108 | std.debug.assert(index < self.count); | |
| 111 | assert(index < self.count); | |
| 109 | 112 | for (index + 1..self.count) |i| { |
| 110 | 113 | self.handles_buf[i - 1] = self.handles_buf[i]; |
| 111 | 114 | self.stream_map[i - 1] = self.stream_map[i]; |
| ... | ... | @@ -118,13 +121,14 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 118 | 121 | const Self = @This(); |
| 119 | 122 | |
| 120 | 123 | pub fn deinit(self: *Self) void { |
| 124 | const gpa = self.gpa; | |
| 121 | 125 | if (is_windows) { |
| 122 | 126 | // cancel any pending IO to prevent clobbering OVERLAPPED value |
| 123 | 127 | for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| { |
| 124 | 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 | 132 | self.* = undefined; |
| 129 | 133 | } |
| 130 | 134 | |
| ... | ... | @@ -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 { | |
| 148 | return &self.fifos[@intFromEnum(which)]; | |
| 151 | pub inline fn reader(self: *Self, comptime which: StreamEnum) *BufferedReader { | |
| 152 | return &self.readers[@intFromEnum(which)]; | |
| 149 | 153 | } |
| 150 | 154 | |
| 151 | 155 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { |
| ... | ... | @@ -236,6 +240,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 236 | 240 | } |
| 237 | 241 | |
| 238 | 242 | fn pollPosix(self: *Self, nanoseconds: ?u64) !bool { |
| 243 | const gpa = self.gpa; | |
| 239 | 244 | // We ask for ensureUnusedCapacity with this much extra space. This |
| 240 | 245 | // has more of an effect on small reads because once the reads |
| 241 | 246 | // start to get larger the amount of space an ArrayList will |
| ... | ... | @@ -255,18 +260,18 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 255 | 260 | } |
| 256 | 261 | |
| 257 | 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 | 264 | // Try reading whatever is available before checking the error |
| 260 | 265 | // conditions. |
| 261 | 266 | // It's still possible to read after a POLL.HUP is received, |
| 262 | 267 | // always check if there's some data waiting to be read first. |
| 263 | 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 | 270 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { |
| 266 | 271 | error.BrokenPipe => 0, // Handle the same as EOF. |
| 267 | 272 | else => |e| return e, |
| 268 | 273 | }; |
| 269 | q.update(amt); | |
| 274 | br.advanceBufferEnd(amt); | |
| 270 | 275 | if (amt == 0) { |
| 271 | 276 | // Remove the fd when the EOF condition is met. |
| 272 | 277 | poll_fd.fd = -1; |
| ... | ... | @@ -297,14 +302,14 @@ var win_dummy_bytes_read: u32 = undefined; |
| 297 | 302 | fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 298 | 303 | handle: windows.HANDLE, |
| 299 | 304 | overlapped: *windows.OVERLAPPED, |
| 300 | fifo: *PollFifo, | |
| 305 | br: *BufferedReader, | |
| 301 | 306 | small_buf: *[128]u8, |
| 302 | 307 | bump_amt: usize, |
| 303 | 308 | ) !enum { empty, populated, closed_populated, closed } { |
| 304 | 309 | var read_any_data = false; |
| 305 | 310 | while (true) { |
| 306 | 311 | const fifo_read_pending = while (true) { |
| 307 | const buf = try fifo.writableWithSize(bump_amt); | |
| 312 | const buf = try br.writableWithSize(bump_amt); | |
| 308 | 313 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); |
| 309 | 314 | |
| 310 | 315 | if (0 == windows.kernel32.ReadFile( |
| ... | ... | @@ -326,7 +331,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 326 | 331 | }; |
| 327 | 332 | |
| 328 | 333 | read_any_data = true; |
| 329 | fifo.update(num_bytes_read); | |
| 334 | br.update(num_bytes_read); | |
| 330 | 335 | |
| 331 | 336 | if (num_bytes_read == buf_len) { |
| 332 | 337 | // We filled the buffer, so there's probably more data available. |
| ... | ... | @@ -356,7 +361,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 356 | 361 | .aborted => break :cancel_read, |
| 357 | 362 | }; |
| 358 | 363 | read_any_data = true; |
| 359 | fifo.update(num_bytes_read); | |
| 364 | br.update(num_bytes_read); | |
| 360 | 365 | } |
| 361 | 366 | |
| 362 | 367 | // Try to queue the 1-byte read. |
| ... | ... | @@ -381,7 +386,7 @@ fn windowsAsyncReadToFifoAndQueueSmallRead( |
| 381 | 386 | .closed => return if (read_any_data) .closed_populated else .closed, |
| 382 | 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 | 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 | const testing = std.testing; |
| 7 | 7 | const BufferedWriter = std.io.BufferedWriter; |
| 8 | 8 | const Reader = std.io.Reader; |
| 9 | const Allocator = std.mem.Allocator; | |
| 9 | 10 | |
| 10 | 11 | const BufferedReader = @This(); |
| 11 | 12 | |
| ... | ... | @@ -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 | 54 | fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize { |
| 50 | 55 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 51 | 56 | const buffer = br.buffer[0..br.end]; |
| 52 | 57 | const buffered = buffer[br.seek..]; |
| 53 | 58 | const limited = buffered[0..limit.min(buffered.len)]; |
| 54 | 59 | if (limited.len > 0) { |
| 55 | const n = try bw.writeSplat(limited, 1); | |
| 60 | const n = try bw.write(limited); | |
| 56 | 61 | br.seek += n; |
| 57 | 62 | return n; |
| 58 | 63 | } |
| ... | ... | @@ -61,9 +66,44 @@ fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Read |
| 61 | 66 | |
| 62 | 67 | fn passthruReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Error!usize { |
| 63 | 68 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 64 | _ = br; | |
| 65 | _ = data; | |
| 66 | @panic("TODO"); | |
| 69 | var total: usize = 0; | |
| 70 | for (data, 0..) |buf, i| { | |
| 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 | 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 | 187 | } |
| 148 | 188 | |
| 149 | 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 | 192 | /// Asserts that the `BufferedReader` was initialized with a buffer capacity at |
| 153 | 193 | /// least as big as `n`. |
| ... | ... | @@ -161,6 +201,22 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 { |
| 161 | 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 | 220 | /// Skips the next `n` bytes from the stream, advancing the seek position. |
| 165 | 221 | /// |
| 166 | 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 | 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 | 339 | pub const DelimiterInclusiveError = error{ |
| 259 | 340 | /// See the `Reader` implementation for detailed diagnostics. |
| 260 | 341 | ReadFailed, |
| ... | ... | @@ -498,12 +579,29 @@ pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.E |
| 498 | 579 | } |
| 499 | 580 | |
| 500 | 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 | 587 | pub fn takeStruct(br: *BufferedReader, comptime T: type) Reader.Error!*align(1) T { |
| 502 | 588 | // Only extern and packed structs have defined in-memory layout. |
| 503 | 589 | comptime assert(@typeInfo(T).@"struct".layout != .auto); |
| 504 | 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 | 605 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 508 | 606 | /// |
| 509 | 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 | 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 | 625 | /// Reads an integer with the same size as the given enum's tag type. If the |
| 518 | 626 | /// integer matches an enum tag, casts the integer to the enum tag and returns |
| 519 | 627 | /// it. Otherwise, returns `error.InvalidEnumTag`. |
| ... | ... | @@ -536,6 +644,28 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Re |
| 536 | 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 | 669 | fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result { |
| 540 | 670 | const result_info = @typeInfo(Result).int; |
| 541 | 671 | comptime assert(result_info.bits % 7 == 0); |
| ... | ... | @@ -599,6 +729,10 @@ test takeArray { |
| 599 | 729 | return error.Unimplemented; |
| 600 | 730 | } |
| 601 | 731 | |
| 732 | test peekArray { | |
| 733 | return error.Unimplemented; | |
| 734 | } | |
| 735 | ||
| 602 | 736 | test discard { |
| 603 | 737 | var br: BufferedReader = undefined; |
| 604 | 738 | br.initFixed("foobar"); |
| ... | ... | @@ -684,10 +818,18 @@ test takeStruct { |
| 684 | 818 | return error.Unimplemented; |
| 685 | 819 | } |
| 686 | 820 | |
| 821 | test peekStruct { | |
| 822 | return error.Unimplemented; | |
| 823 | } | |
| 824 | ||
| 687 | 825 | test takeStructEndian { |
| 688 | 826 | return error.Unimplemented; |
| 689 | 827 | } |
| 690 | 828 | |
| 829 | test peekStructEndian { | |
| 830 | return error.Unimplemented; | |
| 831 | } | |
| 832 | ||
| 691 | 833 | test takeEnum { |
| 692 | 834 | return error.Unimplemented; |
| 693 | 835 | } |
| ... | ... | @@ -699,3 +841,7 @@ test takeLeb128 { |
| 699 | 841 | test readShort { |
| 700 | 842 | return error.Unimplemented; |
| 701 | 843 | } |
| 844 | ||
| 845 | test readVec { | |
| 846 | return error.Unimplemented; | |
| 847 | } |