authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-18 18:14:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:27-07:00
logf3332677825f3173de568d643cbaf68f1e1472ed
tree31232ea5d95e8556f826a90f34e2fbad29287be8
parent98f463ad599bebd46fbc1e4f8ff365ef781bfe5f

update std.http.Server to new API

and rename std.io.BufferedWriter.writableSlice to writableSliceGreedy and make writableSlice and writableArray advance the buffer end position introduce std.io.BufferedWriter.writeSplatLimit but it's unimplemented

14 files changed, 343 insertions(+), 218 deletions(-)

lib/compiler/std-docs.zig+11-8
...@@ -90,8 +90,15 @@ pub fn main() !void {...@@ -90,8 +90,15 @@ pub fn main() !void {
90fn accept(context: *Context, connection: std.net.Server.Connection) void {90fn accept(context: *Context, connection: std.net.Server.Connection) void {
91 defer connection.stream.close();91 defer connection.stream.close();
9292
93 var read_buffer: [8000]u8 = undefined;93 var recv_buffer: [8000]u8 = undefined;
94 var server = std.http.Server.init(connection, &read_buffer);94 var send_buffer: [4000]u8 = undefined;
95 var connection_br: std.io.BufferedReader = undefined;
96 var stream_reader = connection.stream.reader();
97 connection_br.init(stream_reader.interface(), &recv_buffer);
98 var stream_writer = connection.stream.writer();
99 var connection_bw = stream_writer.interface().buffered(&send_buffer);
100 var server = std.http.Server.init(&connection_br, &connection_bw);
101
95 while (server.state == .ready) {102 while (server.state == .ready) {
96 var request = server.receiveHead() catch |err| switch (err) {103 var request = server.receiveHead() catch |err| switch (err) {
97 error.HttpConnectionClosing => return,104 error.HttpConnectionClosing => return,
...@@ -160,9 +167,7 @@ fn serveDocsFile(...@@ -160,9 +167,7 @@ fn serveDocsFile(
160 defer file.close();167 defer file.close();
161 const content_length = std.math.cast(usize, (try file.stat()).size) orelse return error.FileTooBig;168 const content_length = std.math.cast(usize, (try file.stat()).size) orelse return error.FileTooBig;
162169
163 var send_buffer: [4000]u8 = undefined;170 var response = try request.respondStreaming(.{
164 var response = request.respondStreaming(.{
165 .send_buffer = &send_buffer,
166 .content_length = content_length,171 .content_length = content_length,
167 .respond_options = .{172 .respond_options = .{
168 .extra_headers = &.{173 .extra_headers = &.{
...@@ -182,9 +187,7 @@ fn serveDocsFile(...@@ -182,9 +187,7 @@ fn serveDocsFile(
182fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {187fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {
183 const gpa = context.gpa;188 const gpa = context.gpa;
184189
185 var send_buffer: [0x4000]u8 = undefined;190 var response = try request.respondStreaming(.{
186 var response = request.respondStreaming(.{
187 .send_buffer = &send_buffer,
188 .respond_options = .{191 .respond_options = .{
189 .extra_headers = &.{192 .extra_headers = &.{
190 .{ .name = "content-type", .value = "application/x-tar" },193 .{ .name = "content-type", .value = "application/x-tar" },
lib/std/compress/flate/inflate.zig+2-2
...@@ -349,8 +349,8 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {...@@ -349,8 +349,8 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
349 limit: std.io.Reader.Limit,349 limit: std.io.Reader.Limit,
350 ) std.io.Reader.RwError!usize {350 ) std.io.Reader.RwError!usize {
351 const self: *Self = @alignCast(@ptrCast(context));351 const self: *Self = @alignCast(@ptrCast(context));
352 const out = try bw.writableSlice(1);352 const out = try bw.writableSliceGreedy(1);
353 const in = self.get(limit.min(out.len)) catch |err| switch (err) {353 const in = self.get(limit.minInt(out.len)) catch |err| switch (err) {
354 error.EndOfStream => return error.EndOfStream,354 error.EndOfStream => return error.EndOfStream,
355 error.ReadFailed => return error.ReadFailed,355 error.ReadFailed => return error.ReadFailed,
356 else => |e| {356 else => |e| {
lib/std/crypto/tls/Client.zig+3-3
...@@ -925,7 +925,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i...@@ -925,7 +925,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i
925 const c: *Client = @alignCast(@ptrCast(context));925 const c: *Client = @alignCast(@ptrCast(context));
926 const sliced_data = if (splat == 0) data[0..data.len -| 1] else data;926 const sliced_data = if (splat == 0) data[0..data.len -| 1] else data;
927 const output = &c.output;927 const output = &c.output;
928 const ciphertext_buf = try output.writableSlice(min_buffer_len);928 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
929 var total_clear: usize = 0;929 var total_clear: usize = 0;
930 var ciphertext_end: usize = 0;930 var ciphertext_end: usize = 0;
931 for (sliced_data) |buf| {931 for (sliced_data) |buf| {
...@@ -943,7 +943,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i...@@ -943,7 +943,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i
943/// attack.943/// attack.
944pub fn end(c: *Client) std.io.Writer.Error!void {944pub fn end(c: *Client) std.io.Writer.Error!void {
945 const output = &c.output;945 const output = &c.output;
946 const ciphertext_buf = try output.writableSlice(min_buffer_len);946 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
947 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);947 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);
948 output.advance(prepared.cleartext_len);948 output.advance(prepared.cleartext_len);
949 return prepared.ciphertext_end;949 return prepared.ciphertext_end;
...@@ -1063,7 +1063,7 @@ fn read(...@@ -1063,7 +1063,7 @@ fn read(
1063 bw: *std.io.BufferedWriter,1063 bw: *std.io.BufferedWriter,
1064 limit: std.io.Reader.Limit,1064 limit: std.io.Reader.Limit,
1065) std.io.Reader.RwError!std.io.Reader.Status {1065) std.io.Reader.RwError!std.io.Reader.Status {
1066 const buf = limit.slice(try bw.writableSlice(1));1066 const buf = limit.slice(try bw.writableSliceGreedy(1));
1067 const status = try readVec(context, &.{buf});1067 const status = try readVec(context, &.{buf});
1068 bw.advance(status.len);1068 bw.advance(status.len);
1069 return status;1069 return status;
lib/std/fs/File.zig+1-1
...@@ -983,7 +983,7 @@ pub const Reader = struct {...@@ -983,7 +983,7 @@ pub const Reader = struct {
983 }983 }
984 return 0;984 return 0;
985 };985 };
986 const new_limit: std.io.Reader.Limit = .limited(limit.min(size - pos));986 const new_limit = limit.min(.limited(size - pos));
987 const n = bw.writeFile(file, .init(pos), new_limit, &.{}, 0) catch |err| switch (err) {987 const n = bw.writeFile(file, .init(pos), new_limit, &.{}, 0) catch |err| switch (err) {
988 error.WriteFailed => return error.WriteFailed,988 error.WriteFailed => return error.WriteFailed,
989 error.Unseekable => {989 error.Unseekable => {
lib/std/http/Server.zig+243-167
...@@ -14,6 +14,7 @@ const Server = @This();...@@ -14,6 +14,7 @@ const Server = @This();
14/// The reader's buffer must be large enough to store the client's entire HTTP14/// The reader's buffer must be large enough to store the client's entire HTTP
15/// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`.15/// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`.
16in: *std.io.BufferedReader,16in: *std.io.BufferedReader,
17/// Data from the HTTP server to the HTTP client.
17out: *std.io.BufferedWriter,18out: *std.io.BufferedWriter,
18/// Keeps track of whether the Server is ready to accept a new request on the19/// Keeps track of whether the Server is ready to accept a new request on the
19/// same connection, and makes invalid API usage cause assertion failures20/// same connection, and makes invalid API usage cause assertion failures
...@@ -479,12 +480,6 @@ pub const Request = struct {...@@ -479,12 +480,6 @@ pub const Request = struct {
479 }480 }
480481
481 pub const RespondStreamingOptions = struct {482 pub const RespondStreamingOptions = struct {
482 /// An externally managed slice of memory used to batch bytes before
483 /// sending. `respondStreaming` asserts this is large enough to store
484 /// the full HTTP response head.
485 ///
486 /// Must outlive the returned Response.
487 send_buffer: []u8,
488 /// If provided, the response will use the content-length header;483 /// If provided, the response will use the content-length header;
489 /// otherwise it will use transfer-encoding: chunked.484 /// otherwise it will use transfer-encoding: chunked.
490 content_length: ?u64 = null,485 content_length: ?u64 = null,
...@@ -492,7 +487,7 @@ pub const Request = struct {...@@ -492,7 +487,7 @@ pub const Request = struct {
492 respond_options: RespondOptions = .{},487 respond_options: RespondOptions = .{},
493 };488 };
494489
495 /// The header is buffered but not sent until Response.flush is called.490 /// The header is buffered but not sent until `Response.flush` is called.
496 ///491 ///
497 /// If the request contains a body and the connection is to be reused,492 /// If the request contains a body and the connection is to be reused,
498 /// discards the request body, leaving the Server in the `ready` state. If493 /// discards the request body, leaving the Server in the `ready` state. If
...@@ -504,69 +499,63 @@ pub const Request = struct {...@@ -504,69 +499,63 @@ pub const Request = struct {
504 /// that flag and skipping any expensive work that would otherwise need to499 /// that flag and skipping any expensive work that would otherwise need to
505 /// be done to satisfy the request.500 /// be done to satisfy the request.
506 ///501 ///
507 /// Asserts `send_buffer` is large enough to store the entire response header.
508 /// Asserts status is not `continue`.502 /// Asserts status is not `continue`.
509 pub fn respondStreaming(request: *Request, options: RespondStreamingOptions) Response {503 pub fn respondStreaming(request: *Request, options: RespondStreamingOptions) std.io.Writer.Error!Response {
510 const o = options.respond_options;504 const o = options.respond_options;
511 assert(o.status != .@"continue");505 assert(o.status != .@"continue");
512 const transfer_encoding_none = (o.transfer_encoding orelse .chunked) == .none;506 const transfer_encoding_none = (o.transfer_encoding orelse .chunked) == .none;
513 const server_keep_alive = !transfer_encoding_none and o.keep_alive;507 const server_keep_alive = !transfer_encoding_none and o.keep_alive;
514 const keep_alive = request.discardBody(server_keep_alive);508 const keep_alive = request.discardBody(server_keep_alive);
515 const phrase = o.reason orelse o.status.phrase() orelse "";509 const phrase = o.reason orelse o.status.phrase() orelse "";
516510 const out = request.server.out;
517 var h = std.ArrayListUnmanaged(u8).initBuffer(options.send_buffer);
518511
519 const elide_body = if (request.head.expect != null) eb: {512 const elide_body = if (request.head.expect != null) eb: {
520 // reader() and hence discardBody() above sets expect to null if it513 // reader() and hence discardBody() above sets expect to null if it
521 // is handled. So the fact that it is not null here means unhandled.514 // is handled. So the fact that it is not null here means unhandled.
522 h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n");515 try out.writeAll("HTTP/1.1 417 Expectation Failed\r\n");
523 if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n");516 if (!keep_alive) try out.writeAll("connection: close\r\n");
524 h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n");517 try out.writeAll("content-length: 0\r\n\r\n");
525 break :eb true;518 break :eb true;
526 } else eb: {519 } else eb: {
527 h.printAssumeCapacity("{s} {d} {s}\r\n", .{520 try out.print("{s} {d} {s}\r\n", .{
528 @tagName(o.version), @intFromEnum(o.status), phrase,521 @tagName(o.version), @intFromEnum(o.status), phrase,
529 });522 });
530523
531 switch (o.version) {524 switch (o.version) {
532 .@"HTTP/1.0" => if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"),525 .@"HTTP/1.0" => if (keep_alive) try out.writeAll("connection: keep-alive\r\n"),
533 .@"HTTP/1.1" => if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n"),526 .@"HTTP/1.1" => if (!keep_alive) try out.writeAll("connection: close\r\n"),
534 }527 }
535528
536 if (o.transfer_encoding) |transfer_encoding| switch (transfer_encoding) {529 if (o.transfer_encoding) |transfer_encoding| switch (transfer_encoding) {
537 .chunked => h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"),530 .chunked => try out.writeAll("transfer-encoding: chunked\r\n"),
538 .none => {},531 .none => {},
539 } else if (options.content_length) |len| {532 } else if (options.content_length) |len| {
540 h.printAssumeCapacity("content-length: {d}\r\n", .{len});533 try out.print("content-length: {d}\r\n", .{len});
541 } else {534 } else {
542 h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n");535 try out.writeAll("transfer-encoding: chunked\r\n");
543 }536 }
544537
545 for (o.extra_headers) |header| {538 for (o.extra_headers) |header| {
546 assert(header.name.len != 0);539 assert(header.name.len != 0);
547 h.appendSliceAssumeCapacity(header.name);540 try out.writeAll(header.name);
548 h.appendSliceAssumeCapacity(": ");541 try out.writeAll(": ");
549 h.appendSliceAssumeCapacity(header.value);542 try out.writeAll(header.value);
550 h.appendSliceAssumeCapacity("\r\n");543 try out.writeAll("\r\n");
551 }544 }
552545
553 h.appendSliceAssumeCapacity("\r\n");546 try out.writeAll("\r\n");
554 break :eb request.head.method == .HEAD;547 break :eb request.head.method == .HEAD;
555 };548 };
556549
557 return .{550 return .{
558 .out = request.server.out,551 .server_output = request.server.out,
559 .send_buffer = options.send_buffer,
560 .send_buffer_start = 0,
561 .send_buffer_end = h.items.len,
562 .transfer_encoding = if (o.transfer_encoding) |te| switch (te) {552 .transfer_encoding = if (o.transfer_encoding) |te| switch (te) {
563 .chunked => .chunked,553 .chunked => .{ .chunked = .init },
564 .none => .none,554 .none => .none,
565 } else if (options.content_length) |len| .{555 } else if (options.content_length) |len| .{
566 .content_length = len,556 .content_length = len,
567 } else .chunked,557 } else .{ .chunked = .init },
568 .elide_body = elide_body,558 .elide_body = elide_body,
569 .chunk_len = 0,
570 };559 };
571 }560 }
572561
...@@ -836,20 +825,32 @@ pub const Request = struct {...@@ -836,20 +825,32 @@ pub const Request = struct {
836};825};
837826
838pub const Response = struct {827pub const Response = struct {
839 out: *std.io.BufferedWriter,828 /// HTTP protocol to the client.
840 send_buffer: []u8,829 ///
841 /// Index of the first byte in `send_buffer`.830 /// This is the underlying stream; use `buffered` to create a
842 /// This is 0 unless a short write happens in `write`.831 /// `BufferedWriter` for this `Response`.
843 send_buffer_start: usize,832 server_output: *std.io.BufferedWriter,
844 /// Index of the last byte + 1 in `send_buffer`.
845 send_buffer_end: usize,
846 /// `null` means transfer-encoding: chunked.833 /// `null` means transfer-encoding: chunked.
847 /// As a debugging utility, counts down to zero as bytes are written.834 /// As a debugging utility, counts down to zero as bytes are written.
848 transfer_encoding: TransferEncoding,835 transfer_encoding: TransferEncoding,
849 elide_body: bool,836 elide_body: bool,
850 /// Indicates how much of the end of the `send_buffer` corresponds to a837 err: Error!void = {},
851 /// chunk. This amount of data will be wrapped by an HTTP chunk header.838
852 chunk_len: usize,839 pub const Error = error{
840 /// Attempted to write a file to the stream, an expensive operation
841 /// that should be avoided when `elide_body` is true.
842 UnableToElideBody,
843 };
844 pub const WriteError = std.io.Writer.Error;
845
846 /// How many zeroes to reserve for hex-encoded chunk length.
847 const chunk_len_digits = 8;
848 const max_chunk_len: usize = std.math.pow(usize, 16, chunk_len_digits) - 1;
849 const chunk_header_template = ("0" ** chunk_len_digits) ++ "\r\n";
850
851 comptime {
852 assert(max_chunk_len == std.math.maxInt(u32));
853 }
853854
854 pub const TransferEncoding = union(enum) {855 pub const TransferEncoding = union(enum) {
855 /// End of connection signals the end of the stream.856 /// End of connection signals the end of the stream.
...@@ -857,7 +858,19 @@ pub const Response = struct {...@@ -857,7 +858,19 @@ pub const Response = struct {
857 /// As a debugging utility, counts down to zero as bytes are written.858 /// As a debugging utility, counts down to zero as bytes are written.
858 content_length: u64,859 content_length: u64,
859 /// Each chunk is wrapped in a header and trailer.860 /// Each chunk is wrapped in a header and trailer.
860 chunked,861 chunked: Chunked,
862
863 pub const Chunked = union(enum) {
864 /// Index of the hex-encoded chunk length in the chunk header
865 /// within the buffer of `Response.server_output`.
866 offset: usize,
867 /// We are in the middle of a chunk and this is how many bytes are
868 /// left until the next header. This includes +2 for "\r"\n", and
869 /// is zero for the beginning of the stream.
870 chunk_len: usize,
871
872 pub const init: Chunked = .{ .chunk_len = 0 };
873 };
861 };874 };
862875
863 /// When using content-length, asserts that the amount of data sent matches876 /// When using content-length, asserts that the amount of data sent matches
...@@ -865,17 +878,17 @@ pub const Response = struct {...@@ -865,17 +878,17 @@ pub const Response = struct {
865 /// Otherwise, transfer-encoding: chunked is being used, and it writes the878 /// Otherwise, transfer-encoding: chunked is being used, and it writes the
866 /// end-of-stream message, then flushes the stream to the system.879 /// end-of-stream message, then flushes the stream to the system.
867 /// Respects the value of `elide_body` to omit all data after the headers.880 /// Respects the value of `elide_body` to omit all data after the headers.
868 pub fn end(r: *Response) std.io.Writer.Error!void {881 pub fn end(r: *Response) WriteError!void {
869 switch (r.transfer_encoding) {882 switch (r.transfer_encoding) {
870 .content_length => |len| {883 .content_length => |len| {
871 assert(len == 0); // Trips when end() called before all bytes written.884 assert(len == 0); // Trips when end() called before all bytes written.
872 try flush_cl(r);885 try flushContentLength(r);
873 },886 },
874 .none => {887 .none => {
875 try flush_cl(r);888 try flushContentLength(r);
876 },889 },
877 .chunked => {890 .chunked => {
878 try flush_chunked(r, &.{});891 try flushChunked(r, &.{});
879 },892 },
880 }893 }
881 r.* = undefined;894 r.* = undefined;
...@@ -890,9 +903,9 @@ pub const Response = struct {...@@ -890,9 +903,9 @@ pub const Response = struct {
890 /// flushes the stream to the system.903 /// flushes the stream to the system.
891 /// Respects the value of `elide_body` to omit all data after the headers.904 /// Respects the value of `elide_body` to omit all data after the headers.
892 /// Asserts there are at most 25 trailers.905 /// Asserts there are at most 25 trailers.
893 pub fn endChunked(r: *Response, options: EndChunkedOptions) std.io.Writer.Error!void {906 pub fn endChunked(r: *Response, options: EndChunkedOptions) WriteError!void {
894 assert(r.transfer_encoding == .chunked);907 assert(r.transfer_encoding == .chunked);
895 try flush_chunked(r, options.trailers);908 try flushChunked(r, options.trailers);
896 r.* = undefined;909 r.* = undefined;
897 }910 }
898911
...@@ -900,163 +913,222 @@ pub const Response = struct {...@@ -900,163 +913,222 @@ pub const Response = struct {
900 /// would not exceed the content-length value sent in the HTTP header.913 /// would not exceed the content-length value sent in the HTTP header.
901 /// May return 0, which does not indicate end of stream. The caller decides914 /// May return 0, which does not indicate end of stream. The caller decides
902 /// when the end of stream occurs by calling `end`.915 /// when the end of stream occurs by calling `end`.
903 pub fn write(r: *Response, bytes: []const u8) std.io.Writer.Error!usize {916 pub fn write(r: *Response, bytes: []const u8) WriteError!usize {
904 switch (r.transfer_encoding) {917 switch (r.transfer_encoding) {
905 .content_length, .none => return cl_writeSplat(r, &.{bytes}, 1),918 .content_length, .none => return contentLengthWriteSplat(r, &.{bytes}, 1),
906 .chunked => return chunked_writeSplat(r, &.{bytes}, 1),919 .chunked => return chunkedWriteSplat(r, &.{bytes}, 1),
920 }
921 }
922
923 fn contentLengthWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) WriteError!usize {
924 const r: *Response = @alignCast(@ptrCast(context));
925 const n = if (r.elide_body) countSplat(data, splat) else try r.server_output.writeSplat(data, splat);
926 r.transfer_encoding.content_length -= n;
927 return n;
928 }
929
930 fn noneWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) WriteError!usize {
931 const r: *Response = @alignCast(@ptrCast(context));
932 if (r.elide_body) return countSplat(data, splat);
933 return r.server_output.writeSplat(data, splat);
934 }
935
936 fn countSplat(data: []const []const u8, splat: usize) usize {
937 if (data.len == 0) return 0;
938 var total: usize = 0;
939 for (data[0 .. data.len - 1]) |buf| total += buf.len;
940 total += data[data.len - 1].len * splat;
941 return total;
942 }
943
944 fn elideWriteFile(
945 r: *Response,
946 offset: std.io.Writer.Offset,
947 limit: std.io.Writer.Limit,
948 headers_and_trailers: []const []const u8,
949 ) WriteError!usize {
950 if (offset != .none) {
951 if (countWriteFile(limit, headers_and_trailers)) |n| {
952 return n;
953 }
907 }954 }
955 r.err = error.UnableToElideBody;
956 return error.WriteFailed;
908 }957 }
909958
910 fn cl_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {959 /// Returns `null` if size cannot be computed without making any syscalls.
911 _ = splat;960 fn countWriteFile(limit: std.io.Writer.Limit, headers_and_trailers: []const []const u8) ?usize {
912 return cl_write(context, data[0]); // TODO: try to send all the data961 var total: usize = limit.toInt() orelse return null;
962 for (headers_and_trailers) |buf| total += buf.len;
963 return total;
913 }964 }
914965
915 fn cl_writeFile(966 fn noneWriteFile(
916 context: ?*anyopaque,967 context: ?*anyopaque,
917 file: std.fs.File,968 file: std.fs.File,
918 offset: std.io.Writer.Offset,969 offset: std.io.Writer.Offset,
919 limit: std.io.Writer.Limit,970 limit: std.io.Writer.Limit,
920 headers_and_trailers: []const []const u8,971 headers_and_trailers: []const []const u8,
921 headers_len: usize,972 headers_len: usize,
922 ) std.io.Writer.Error!usize {973 ) std.io.Writer.FileError!usize {
923 _ = context;974 if (limit == .nothing) return noneWriteSplat(context, headers_and_trailers, 1);
924 _ = file;
925 _ = offset;
926 _ = limit;
927 _ = headers_and_trailers;
928 _ = headers_len;
929 @panic("TODO");
930 }
931
932 fn cl_write(context: ?*anyopaque, bytes: []const u8) std.io.Writer.Error!usize {
933 const r: *Response = @alignCast(@ptrCast(context));975 const r: *Response = @alignCast(@ptrCast(context));
934976 if (r.elide_body) return elideWriteFile(r, offset, limit, headers_and_trailers);
935 var trash: u64 = std.math.maxInt(u64);977 return r.server_output.writeFile(file, offset, limit, headers_and_trailers, headers_len);
936 const len = switch (r.transfer_encoding) {
937 .content_length => |*len| len,
938 else => &trash,
939 };
940
941 if (r.elide_body) {
942 len.* -= bytes.len;
943 return bytes.len;
944 }
945
946 if (bytes.len + r.send_buffer_end > r.send_buffer.len) {
947 const send_buffer_len = r.send_buffer_end - r.send_buffer_start;
948 var iovecs: [2][]const u8 = .{
949 r.send_buffer[r.send_buffer_start..][0..send_buffer_len],
950 bytes,
951 };
952 const n = try r.out.writeVec(&iovecs);
953
954 if (n >= send_buffer_len) {
955 // It was enough to reset the buffer.
956 r.send_buffer_start = 0;
957 r.send_buffer_end = 0;
958 const bytes_n = n - send_buffer_len;
959 len.* -= bytes_n;
960 return bytes_n;
961 }
962
963 // It didn't even make it through the existing buffer, let
964 // alone the new bytes provided.
965 r.send_buffer_start += n;
966 return 0;
967 }
968
969 // All bytes can be stored in the remaining space of the buffer.
970 @memcpy(r.send_buffer[r.send_buffer_end..][0..bytes.len], bytes);
971 r.send_buffer_end += bytes.len;
972 len.* -= bytes.len;
973 return bytes.len;
974 }978 }
975979
976 fn chunked_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {980 fn contentLengthWriteFile(
977 _ = splat;981 context: ?*anyopaque,
978 return chunked_write(context, data[0]); // TODO: try to send all the data982 file: std.fs.File,
983 offset: std.io.Writer.Offset,
984 limit: std.io.Writer.Limit,
985 headers_and_trailers: []const []const u8,
986 headers_len: usize,
987 ) std.io.Writer.FileError!usize {
988 if (limit == .nothing) return contentLengthWriteSplat(context, headers_and_trailers, 1);
989 const r: *Response = @alignCast(@ptrCast(context));
990 if (r.elide_body) return elideWriteFile(r, offset, limit, headers_and_trailers);
991 const n = try r.server_output.writeFile(file, offset, limit, headers_and_trailers, headers_len);
992 r.transfer_encoding.content_length -= n;
993 return n;
979 }994 }
980995
981 fn chunked_writeFile(996 fn chunkedWriteFile(
982 context: ?*anyopaque,997 context: ?*anyopaque,
983 file: std.fs.File,998 file: std.fs.File,
984 offset: std.io.Writer.Offset,999 offset: std.io.Writer.Offset,
985 limit: std.io.Writer.Limit,1000 limit: std.io.Writer.Limit,
986 headers_and_trailers: []const []const u8,1001 headers_and_trailers: []const []const u8,
987 headers_len: usize,1002 headers_len: usize,
988 ) std.io.Writer.Error!usize {1003 ) std.io.Writer.FileError!usize {
989 _ = context;1004 if (limit == .nothing) return chunkedWriteSplat(context, headers_and_trailers, 1);
990 _ = file;1005 const r: *Response = @alignCast(@ptrCast(context));
991 _ = offset;1006 if (r.elide_body) return elideWriteFile(r, offset, limit, headers_and_trailers);
992 _ = limit;1007 const data_len = countWriteFile(limit, headers_and_trailers) orelse @panic("TODO");
993 _ = headers_and_trailers;1008 const bw = r.server_output;
994 _ = headers_len;1009 const chunked = &r.transfer_encoding.chunked;
995 @panic("TODO"); // TODO lower to a call to writeFile on the output1010 state: switch (chunked.*) {
1011 .offset => |off| {
1012 // TODO: is it better perf to read small files into the buffer?
1013 const buffered_len = bw.end - off - chunk_header_template.len;
1014 const chunk_len = data_len + buffered_len;
1015 writeHex(bw.buffer[off..][0..chunk_len_digits], chunk_len);
1016 const n = try bw.writeFile(file, offset, limit, headers_and_trailers, headers_len);
1017 chunked.* = .{ .chunk_len = data_len + 2 - n };
1018 return n;
1019 },
1020 .chunk_len => |chunk_len| {
1021 l: switch (chunk_len) {
1022 0 => {
1023 const header_buf = try bw.writableArray(chunk_header_template.len);
1024 const off = bw.end;
1025 @memcpy(header_buf, chunk_header_template);
1026 chunked.* = .{ .offset = off };
1027 continue :state .{ .offset = off };
1028 },
1029 1 => {
1030 try bw.writeByte('\n');
1031 chunked.chunk_len = 0;
1032 continue :l 0;
1033 },
1034 2 => {
1035 try bw.writeByte('\r');
1036 chunked.chunk_len = 1;
1037 continue :l 1;
1038 },
1039 else => {
1040 const new_limit = limit.min(.limited(chunk_len - 2));
1041 const n = try bw.writeFile(file, offset, new_limit, headers_and_trailers, headers_len);
1042 chunked.chunk_len = chunk_len - n;
1043 return n;
1044 },
1045 }
1046 },
1047 }
996 }1048 }
9971049
998 fn chunked_write(context: ?*anyopaque, bytes: []const u8) std.io.Writer.Error!usize {1050 fn chunkedWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) WriteError!usize {
999 const r: *Response = @alignCast(@ptrCast(context));1051 const r: *Response = @alignCast(@ptrCast(context));
1000 assert(r.transfer_encoding == .chunked);1052 const data_len = countSplat(data, splat);
1053 if (r.elide_body) return data_len;
10011054
1002 if (r.elide_body)1055 const bw = r.server_output;
1003 return bytes.len;1056 const chunked = &r.transfer_encoding.chunked;
1004
1005 if (bytes.len + r.send_buffer_end > r.send_buffer.len) {
1006 const send_buffer_len = r.send_buffer_end - r.send_buffer_start;
1007 const chunk_len = r.chunk_len + bytes.len;
1008 var header_buf: [18]u8 = undefined;
1009 const chunk_header = std.fmt.bufPrint(&header_buf, "{x}\r\n", .{chunk_len}) catch unreachable;
1010
1011 var iovecs: [5][]const u8 = .{
1012 r.send_buffer[r.send_buffer_start .. send_buffer_len - r.chunk_len],
1013 chunk_header,
1014 r.send_buffer[r.send_buffer_end - r.chunk_len ..][0..r.chunk_len],
1015 bytes,
1016 "\r\n",
1017 };
1018 // TODO make this writev instead of writevAll, which involves
1019 // complicating the logic of this function.
1020 try r.out.writeVecAll(&iovecs);
1021 r.send_buffer_start = 0;
1022 r.send_buffer_end = 0;
1023 r.chunk_len = 0;
1024 return bytes.len;
1025 }
10261057
1027 // All bytes can be stored in the remaining space of the buffer.1058 state: switch (chunked.*) {
1028 @memcpy(r.send_buffer[r.send_buffer_end..][0..bytes.len], bytes);1059 .offset => |offset| {
1029 r.send_buffer_end += bytes.len;1060 if (bw.unusedCapacitySlice().len >= data_len) {
1030 r.chunk_len += bytes.len;1061 assert(data_len == (bw.writeSplat(data, splat) catch unreachable));
1031 return bytes.len;1062 return data_len;
1063 }
1064 const buffered_len = bw.end - offset - chunk_header_template.len;
1065 const chunk_len = data_len + buffered_len;
1066 writeHex(bw.buffer[offset..][0..chunk_len_digits], chunk_len);
1067 const n = try bw.writeSplat(data, splat);
1068 chunked.* = .{ .chunk_len = data_len + 2 - n };
1069 return n;
1070 },
1071 .chunk_len => |chunk_len| {
1072 l: switch (chunk_len) {
1073 0 => {
1074 const header_buf = try bw.writableArray(chunk_header_template.len);
1075 const offset = bw.end;
1076 @memcpy(header_buf, chunk_header_template);
1077 chunked.* = .{ .offset = offset };
1078 continue :state .{ .offset = offset };
1079 },
1080 1 => {
1081 try bw.writeByte('\n');
1082 chunked.chunk_len = 0;
1083 continue :l 0;
1084 },
1085 2 => {
1086 try bw.writeByte('\r');
1087 chunked.chunk_len = 1;
1088 continue :l 1;
1089 },
1090 else => {
1091 const n = try bw.writeSplatLimit(data, splat, .limited(chunk_len - 2));
1092 chunked.chunk_len = chunk_len - n;
1093 return n;
1094 },
1095 }
1096 },
1097 }
1032 }1098 }
10331099
1034 /// If using content-length, asserts that writing these bytes to the client1100 /// Writes an integer as base 16 to `buf`, right-aligned, assuming the
1035 /// would not exceed the content-length value sent in the HTTP header.1101 /// buffer has already been filled with zeroes.
1036 pub fn writeAll(r: *Response, bytes: []const u8) std.io.Writer.Error!void {1102 fn writeHex(buf: []u8, x: usize) void {
1037 var index: usize = 0;1103 assert(std.mem.allEqual(u8, buf, '0'));
1038 while (index < bytes.len) {1104 const base = 16;
1039 index += try write(r, bytes[index..]);1105 var index: usize = buf.len;
1106 var a = x;
1107 while (a > 0) {
1108 const digit = a % base;
1109 index -= 1;
1110 buf[index] = std.fmt.digitToChar(@intCast(digit), .lower);
1111 a /= base;
1040 }1112 }
1041 }1113 }
10421114
1043 /// Sends all buffered data to the client.1115 /// Sends all buffered data to the client.
1044 /// This is redundant after calling `end`.1116 /// This is redundant after calling `end`.
1045 /// Respects the value of `elide_body` to omit all data after the headers.1117 /// Respects the value of `elide_body` to omit all data after the headers.
1046 pub fn flush(r: *Response) std.io.Writer.Error!void {1118 pub fn flush(r: *Response) Error!void {
1047 switch (r.transfer_encoding) {1119 switch (r.transfer_encoding) {
1048 .none, .content_length => return flush_cl(r),1120 .none, .content_length => return flushContentLength(r),
1049 .chunked => return flush_chunked(r, null),1121 .chunked => return flushChunked(r, null),
1050 }1122 }
1051 }1123 }
10521124
1053 fn flush_cl(r: *Response) std.io.Writer.Error!void {1125 fn flushContentLength(r: *Response) Error!void {
1054 try r.out.writeAll(r.send_buffer[r.send_buffer_start..r.send_buffer_end]);1126 try r.out.writeAll(r.send_buffer[r.send_buffer_start..r.send_buffer_end]);
1055 r.send_buffer_start = 0;1127 r.send_buffer_start = 0;
1056 r.send_buffer_end = 0;1128 r.send_buffer_end = 0;
1057 }1129 }
10581130
1059 fn flush_chunked(r: *Response, end_trailers: ?[]const http.Header) std.io.Writer.Error!void {1131 fn flushChunked(r: *Response, end_trailers: ?[]const http.Header) Error!void {
1060 const max_trailers = 25;1132 const max_trailers = 25;
1061 if (end_trailers) |trailers| assert(trailers.len <= max_trailers);1133 if (end_trailers) |trailers| assert(trailers.len <= max_trailers);
1062 assert(r.transfer_encoding == .chunked);1134 assert(r.transfer_encoding == .chunked);
...@@ -1123,17 +1195,21 @@ pub const Response = struct {...@@ -1123,17 +1195,21 @@ pub const Response = struct {
11231195
1124 pub fn writer(r: *Response) std.io.Writer {1196 pub fn writer(r: *Response) std.io.Writer {
1125 return .{1197 return .{
1198 .context = r,
1126 .vtable = switch (r.transfer_encoding) {1199 .vtable = switch (r.transfer_encoding) {
1127 .none, .content_length => &.{1200 .none => &.{
1128 .writeSplat = cl_writeSplat,1201 .writeSplat = noneWriteSplat,
1129 .writeFile = cl_writeFile,1202 .writeFile = noneWriteFile,
1203 },
1204 .content_length => &.{
1205 .writeSplat = contentLengthWriteSplat,
1206 .writeFile = contentLengthWriteFile,
1130 },1207 },
1131 .chunked => &.{1208 .chunked => &.{
1132 .writeSplat = chunked_writeSplat,1209 .writeSplat = chunkedWriteSplat,
1133 .writeFile = chunked_writeFile,1210 .writeFile = chunkedWriteFile,
1134 },1211 },
1135 },1212 },
1136 .context = r,
1137 };1213 };
1138 }1214 }
1139};1215};
lib/std/io/BufferedWriter.zig+48-5
...@@ -84,12 +84,29 @@ pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {...@@ -84,12 +84,29 @@ pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
84}84}
8585
86/// Asserts the provided buffer has total capacity enough for `len`.86/// Asserts the provided buffer has total capacity enough for `len`.
87pub fn writableArray(bw: *BufferedWriter, comptime len: usize) anyerror!*[len]u8 {87///
88 return (try bw.writableSlice(len))[0..len];88/// Advances the buffer end position by `len`.
89pub fn writableArray(bw: *BufferedWriter, comptime len: usize) Writer.Error!*[len]u8 {
90 const big_slice = try bw.writableSliceGreedy(len);
91 advance(bw, len);
92 return big_slice[0..len];
93}
94
95/// Asserts the provided buffer has total capacity enough for `len`.
96///
97/// Advances the buffer end position by `len`.
98pub fn writableSlice(bw: *BufferedWriter, len: usize) Writer.Error![]u8 {
99 const big_slice = try bw.writableSliceGreedy(len);
100 advance(bw, len);
101 return big_slice[0..len];
89}102}
90103
91/// Asserts the provided buffer has total capacity enough for `minimum_length`.104/// Asserts the provided buffer has total capacity enough for `minimum_length`.
92pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) Writer.Error![]u8 {105///
106/// Does not `advance` the buffer end position.
107///
108/// If `minimum_length` is zero, this is equivalent to `unusedCapacitySlice`.
109pub fn writableSliceGreedy(bw: *BufferedWriter, minimum_length: usize) Writer.Error![]u8 {
93 assert(bw.buffer.len >= minimum_length);110 assert(bw.buffer.len >= minimum_length);
94 const cap_slice = bw.buffer[bw.end..];111 const cap_slice = bw.buffer[bw.end..];
95 if (cap_slice.len >= minimum_length) {112 if (cap_slice.len >= minimum_length) {
...@@ -111,7 +128,10 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) Writer.Error![]...@@ -111,7 +128,10 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) Writer.Error![]
111 return bw.buffer[bw.end..];128 return bw.buffer[bw.end..];
112}129}
113130
114/// After calling `writableSlice`, this function tracks how many bytes were written to it.131/// After calling `writableSliceGreedy`, this function tracks how many bytes
132/// were written to it.
133///
134/// This is not needed when using `writableSlice` or `writableArray`.
115pub fn advance(bw: *BufferedWriter, n: usize) void {135pub fn advance(bw: *BufferedWriter, n: usize) void {
116 const new_end = bw.end + n;136 const new_end = bw.end + n;
117 assert(new_end <= bw.buffer.len);137 assert(new_end <= bw.buffer.len);
...@@ -135,14 +155,34 @@ pub fn writeVecAll(bw: *BufferedWriter, data: [][]const u8) Writer.Error!void {...@@ -135,14 +155,34 @@ pub fn writeVecAll(bw: *BufferedWriter, data: [][]const u8) Writer.Error!void {
135 }155 }
136}156}
137157
158/// If the number of bytes to write based on `data` and `splat` fits inside
159/// `unusedCapacitySlice`, this function is guaranteed to not fail, not call
160/// into the underlying writer, and return the full number of bytes.
138pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Error!usize {161pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Error!usize {
139 return passthruWriteSplat(bw, data, splat);162 return passthruWriteSplat(bw, data, splat);
140}163}
141164
165/// If the total number of bytes of `data` fits inside `unusedCapacitySlice`,
166/// this function is guaranteed to not fail, not call into the underlying
167/// writer, and return the total bytes inside `data`.
142pub fn writeVec(bw: *BufferedWriter, data: []const []const u8) Writer.Error!usize {168pub fn writeVec(bw: *BufferedWriter, data: []const []const u8) Writer.Error!usize {
143 return passthruWriteSplat(bw, data, 1);169 return passthruWriteSplat(bw, data, 1);
144}170}
145171
172/// Equivalent to `writeSplat` but writes at most `limit` bytes.
173pub fn writeSplatLimit(
174 bw: *BufferedWriter,
175 data: []const []const u8,
176 splat: usize,
177 limit: Writer.Limit,
178) Writer.Error!usize {
179 _ = bw;
180 _ = data;
181 _ = splat;
182 _ = limit;
183 @panic("TODO");
184}
185
146fn passthruWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize {186fn passthruWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize {
147 const bw: *BufferedWriter = @alignCast(@ptrCast(context));187 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
148 const buffer = bw.buffer;188 const buffer = bw.buffer;
...@@ -435,6 +475,9 @@ pub fn writeArraySwap(bw: *BufferedWriter, Elem: type, array: []const Elem) Writ...@@ -435,6 +475,9 @@ pub fn writeArraySwap(bw: *BufferedWriter, Elem: type, array: []const Elem) Writ
435 @panic("TODO");475 @panic("TODO");
436}476}
437477
478/// Unlike `writeSplat` and `writeVec`, this function will call into the
479/// underlying writer even if there is enough buffer capacity for the file
480/// contents.
438pub fn writeFile(481pub fn writeFile(
439 bw: *BufferedWriter,482 bw: *BufferedWriter,
440 file: std.fs.File,483 file: std.fs.File,
...@@ -1400,7 +1443,7 @@ fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) Writer.Error!void...@@ -1400,7 +1443,7 @@ fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) Writer.Error!void
1400 comptime assert(value_info.bits % 7 == 0);1443 comptime assert(value_info.bits % 7 == 0);
1401 var remaining = value;1444 var remaining = value;
1402 while (true) {1445 while (true) {
1403 const buffer: []packed struct(u8) { bits: u7, more: bool } = @ptrCast(try bw.writableSlice(1));1446 const buffer: []packed struct(u8) { bits: u7, more: bool } = @ptrCast(try bw.writableSliceGreedy(1));
1404 for (buffer, 1..) |*byte, len| {1447 for (buffer, 1..) |*byte, len| {
1405 const more = switch (value_info.signedness) {1448 const more = switch (value_info.signedness) {
1406 .signed => remaining >> 6 != remaining >> (value_info.bits - 1),1449 .signed => remaining >> 6 != remaining >> (value_info.bits - 1),
lib/std/io/Reader.zig+5-1
...@@ -77,7 +77,11 @@ pub const Limit = enum(usize) {...@@ -77,7 +77,11 @@ pub const Limit = enum(usize) {
77 return @enumFromInt(n);77 return @enumFromInt(n);
78 }78 }
7979
80 pub fn min(l: Limit, n: usize) usize {80 pub fn min(a: Limit, b: Limit) Limit {
81 return @enumFromInt(@min(@intFromEnum(a), @intFromEnum(b)));
82 }
83
84 pub fn minInt(l: Limit, n: usize) usize {
81 return @min(n, @intFromEnum(l));85 return @min(n, @intFromEnum(l));
82 }86 }
8387
lib/std/io/Writer.zig+5-3
...@@ -33,10 +33,12 @@ pub const VTable = struct {...@@ -33,10 +33,12 @@ pub const VTable = struct {
33 writeFile: *const fn (33 writeFile: *const fn (
34 ctx: ?*anyopaque,34 ctx: ?*anyopaque,
35 file: std.fs.File,35 file: std.fs.File,
36 /// If this is `none`, `file` will be streamed. Otherwise, it will be36 /// If this is `none`, `file` will be streamed, affecting the seek
37 /// read positionally without affecting the seek position.37 /// position. Otherwise, it will be read positionally without affecting
38 /// the seek position.
38 offset: Offset,39 offset: Offset,
39 /// Maximum amount of bytes to read from the file.40 /// Maximum amount of bytes to read from the file. Implementations may
41 /// assume that the file size does not exceed this amount.
40 limit: Limit,42 limit: Limit,
41 /// Headers and trailers must be passed together so that in case `len` is43 /// Headers and trailers must be passed together so that in case `len` is
42 /// zero, they can be forwarded directly to `VTable.writeVec`.44 /// zero, they can be forwarded directly to `VTable.writeVec`.
lib/std/math/big/int.zig+2-2
...@@ -2344,11 +2344,11 @@ pub const Const = struct {...@@ -2344,11 +2344,11 @@ pub const Const = struct {
23442344
2345 const max_str_len = self.sizeInBaseUpperBound(base);2345 const max_str_len = self.sizeInBaseUpperBound(base);
2346 const limbs_len = calcToStringLimbsBufferLen(self.limbs.len, base);2346 const limbs_len = calcToStringLimbsBufferLen(self.limbs.len, base);
2347 if (bw.writableSlice(max_str_len + @alignOf(Limb) - 1 + @sizeOf(Limb) * limbs_len)) |buf| {2347 if (bw.writableSliceGreedy(max_str_len + @alignOf(Limb) - 1 + @sizeOf(Limb) * limbs_len)) |buf| {
2348 const limbs: [*]Limb = @alignCast(@ptrCast(std.mem.alignPointer(buf[max_str_len..].ptr, @alignOf(Limb))));2348 const limbs: [*]Limb = @alignCast(@ptrCast(std.mem.alignPointer(buf[max_str_len..].ptr, @alignOf(Limb))));
2349 bw.advance(self.toString(buf[0..max_str_len], base, case, limbs[0..limbs_len]));2349 bw.advance(self.toString(buf[0..max_str_len], base, case, limbs[0..limbs_len]));
2350 return;2350 return;
2351 } else |_| if (bw.writableSlice(max_str_len)) |buf| {2351 } else |_| if (bw.writableSliceGreedy(max_str_len)) |buf| {
2352 const available_len = 64;2352 const available_len = 64;
2353 var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined;2353 var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined;
2354 if (limbs.len >= limbs_len) {2354 if (limbs.len >= limbs_len) {
lib/std/net.zig+13-6
...@@ -750,7 +750,7 @@ pub fn connectUnixSocket(path: []const u8) !Stream {...@@ -750,7 +750,7 @@ pub fn connectUnixSocket(path: []const u8) !Stream {
750 );750 );
751 errdefer Stream.close(.{ .handle = sockfd });751 errdefer Stream.close(.{ .handle = sockfd });
752752
753 var addr = try std.net.Address.initUnix(path);753 var addr = try Address.initUnix(path);
754 try posix.connect(sockfd, &addr.any, addr.getOsSockLen());754 try posix.connect(sockfd, &addr.any, addr.getOsSockLen());
755755
756 return .{ .handle = sockfd };756 return .{ .handle = sockfd };
...@@ -1859,7 +1859,7 @@ pub const Stream = struct {...@@ -1859,7 +1859,7 @@ pub const Stream = struct {
1859 bw: *std.io.BufferedWriter,1859 bw: *std.io.BufferedWriter,
1860 limit: std.io.Reader.Limit,1860 limit: std.io.Reader.Limit,
1861 ) std.io.Reader.Error!usize {1861 ) std.io.Reader.Error!usize {
1862 const buf = limit.slice(try bw.writableSlice(1));1862 const buf = limit.slice(try bw.writableSliceGreedy(1));
1863 const status = try windows_readVec(context, &.{buf});1863 const status = try windows_readVec(context, &.{buf});
1864 bw.advance(status.len);1864 bw.advance(status.len);
1865 return status;1865 return status;
...@@ -2080,7 +2080,11 @@ pub const Stream = struct {...@@ -2080,7 +2080,11 @@ pub const Stream = struct {
2080 return switch (native_os) {2080 return switch (native_os) {
2081 .windows => .{ .impl = stream },2081 .windows => .{ .impl = stream },
2082 else => .{ .impl = .{2082 else => .{ .impl = .{
2083 .fr = std.fs.File.reader(.{ .handle = stream.handle }),2083 .fr = .{
2084 .file = .{ .handle = stream.handle },
2085 .mode = .streaming,
2086 .seek_err = error.Unseekable,
2087 },
2084 .err = {},2088 .err = {},
2085 } },2089 } },
2086 };2090 };
...@@ -2090,7 +2094,10 @@ pub const Stream = struct {...@@ -2090,7 +2094,10 @@ pub const Stream = struct {
2090 return switch (native_os) {2094 return switch (native_os) {
2091 .windows => .{ .impl = stream },2095 .windows => .{ .impl = stream },
2092 else => .{ .impl = .{2096 else => .{ .impl = .{
2093 .fw = std.fs.File.writer(.{ .handle = stream.handle }),2097 .fw = .{
2098 .file = .{ .handle = stream.handle },
2099 .mode = .streaming,
2100 },
2094 .err = {},2101 .err = {},
2095 } },2102 } },
2096 };2103 };
...@@ -2101,10 +2108,10 @@ pub const Stream = struct {...@@ -2101,10 +2108,10 @@ pub const Stream = struct {
21012108
2102pub const Server = struct {2109pub const Server = struct {
2103 listen_address: Address,2110 listen_address: Address,
2104 stream: std.net.Stream,2111 stream: Stream,
21052112
2106 pub const Connection = struct {2113 pub const Connection = struct {
2107 stream: std.net.Stream,2114 stream: Stream,
2108 address: Address,2115 address: Address,
2109 };2116 };
21102117
src/codegen.zig+3-6
...@@ -386,8 +386,7 @@ pub fn generateSymbolInner(...@@ -386,8 +386,7 @@ pub fn generateSymbolInner(
386 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;386 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
387 var space: Value.BigIntSpace = undefined;387 var space: Value.BigIntSpace = undefined;
388 const int_val = val.toBigInt(&space, zcu);388 const int_val = val.toBigInt(&space, zcu);
389 int_val.writeTwosComplement((try bw.writableSlice(abi_size))[0..abi_size], endian);389 int_val.writeTwosComplement((try bw.writableSlice(abi_size)), endian);
390 bw.advance(abi_size);
391 },390 },
392 .err => |err| {391 .err => |err| {
393 const int = try pt.getErrorValue(err.name);392 const int = try pt.getErrorValue(err.name);
...@@ -498,7 +497,7 @@ pub fn generateSymbolInner(...@@ -498,7 +497,7 @@ pub fn generateSymbolInner(
498 .vector_type => |vector_type| {497 .vector_type => |vector_type| {
499 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;498 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
500 if (vector_type.child == .bool_type) {499 if (vector_type.child == .bool_type) {
501 const buffer = (try bw.writableSlice(abi_size))[0..abi_size];500 const buffer = try bw.writableSlice(abi_size);
502 @memset(buffer, 0xaa);501 @memset(buffer, 0xaa);
503 var index: usize = 0;502 var index: usize = 0;
504 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;503 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;
...@@ -535,7 +534,6 @@ pub fn generateSymbolInner(...@@ -535,7 +534,6 @@ pub fn generateSymbolInner(
535 },534 },
536 }) byte.* |= mask else byte.* &= ~mask;535 }) byte.* |= mask else byte.* &= ~mask;
537 }536 }
538 bw.advance(abi_size);
539 } else {537 } else {
540 switch (aggregate.storage) {538 switch (aggregate.storage) {
541 .bytes => |bytes| try bw.writeAll(bytes.toSlice(vector_type.len, ip)),539 .bytes => |bytes| try bw.writeAll(bytes.toSlice(vector_type.len, ip)),
...@@ -592,7 +590,7 @@ pub fn generateSymbolInner(...@@ -592,7 +590,7 @@ pub fn generateSymbolInner(
592 .@"packed" => {590 .@"packed" => {
593 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;591 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
594 const current_end, const current_count = .{ bw.end, bw.count };592 const current_end, const current_count = .{ bw.end, bw.count };
595 const buffer = (try bw.writableSlice(abi_size))[0..abi_size];593 const buffer = try bw.writableSlice(abi_size);
596 @memset(buffer, 0);594 @memset(buffer, 0);
597 var bits: u16 = 0;595 var bits: u16 = 0;
598596
...@@ -628,7 +626,6 @@ pub fn generateSymbolInner(...@@ -628,7 +626,6 @@ pub fn generateSymbolInner(
628 }626 }
629 bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu));627 bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu));
630 }628 }
631 bw.advance(abi_size);
632 },629 },
633 .auto, .@"extern" => {630 .auto, .@"extern" => {
634 const struct_begin = bw.count;631 const struct_begin = bw.count;
src/link/Dwarf.zig+6-10
...@@ -659,8 +659,7 @@ const Unit = struct {...@@ -659,8 +659,7 @@ const Unit = struct {
659 .eq => {659 .eq => {
660 // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte660 // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte
661 op_len_bytes += 1;661 op_len_bytes += 1;
662 std.leb.writeUnsignedExtended((bw.writableSlice(op_len_bytes) catch unreachable)[0..op_len_bytes], len - extended_op_bytes - op_len_bytes);662 std.leb.writeUnsignedExtended((bw.writableSlice(op_len_bytes) catch unreachable), len - extended_op_bytes - op_len_bytes);
663 bw.advance(op_len_bytes);
664 break;663 break;
665 },664 },
666 .gt => op_len_bytes += 1,665 .gt => op_len_bytes += 1,
...@@ -849,8 +848,7 @@ const Entry = struct {...@@ -849,8 +848,7 @@ const Entry = struct {
849 .eq => {848 .eq => {
850 // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte849 // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte
851 block_len_bytes += 1;850 block_len_bytes += 1;
852 std.leb.writeUnsignedExtended((try bw.writableSlice(block_len_bytes))[0..block_len_bytes], len - abbrev_code_bytes - block_len_bytes);851 std.leb.writeUnsignedExtended((try bw.writableSlice(block_len_bytes)), len - abbrev_code_bytes - block_len_bytes);
853 bw.advance(block_len_bytes);
854 break;852 break;
855 },853 },
856 .gt => block_len_bytes += 1,854 .gt => block_len_bytes += 1,
...@@ -870,10 +868,9 @@ const Entry = struct {...@@ -870,10 +868,9 @@ const Entry = struct {
870 // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte868 // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte
871 op_len_bytes += 1;869 op_len_bytes += 1;
872 std.leb.writeUnsignedExtended(870 std.leb.writeUnsignedExtended(
873 (bw.writableSlice(op_len_bytes) catch unreachable)[0..op_len_bytes],871 (bw.writableSlice(op_len_bytes) catch unreachable),
874 len - extended_op_bytes - op_len_bytes,872 len - extended_op_bytes - op_len_bytes,
875 );873 );
876 bw.advance(op_len_bytes);
877 break;874 break;
878 },875 },
879 .gt => op_len_bytes += 1,876 .gt => op_len_bytes += 1,
...@@ -2009,7 +2006,7 @@ pub const WipNav = struct {...@@ -2009,7 +2006,7 @@ pub const WipNav = struct {
2009 .signed => abbrev_code.sdata,2006 .signed => abbrev_code.sdata,
2010 .unsigned => abbrev_code.udata,2007 .unsigned => abbrev_code.udata,
2011 });2008 });
2012 _ = try dibw.writableSlice(std.math.divCeil(usize, bits, 7) catch unreachable);2009 _ = try dibw.writableSliceGreedy(std.math.divCeil(usize, bits, 7) catch unreachable);
2013 var bit: usize = 0;2010 var bit: usize = 0;
2014 var carry: u1 = 1;2011 var carry: u1 = 1;
2015 while (bit < bits) {2012 while (bit < bits) {
...@@ -2033,7 +2030,7 @@ pub const WipNav = struct {...@@ -2033,7 +2030,7 @@ pub const WipNav = struct {
2033 const bytes = @max(ty.abiSize(zcu), std.math.divCeil(usize, bits, 8) catch unreachable);2030 const bytes = @max(ty.abiSize(zcu), std.math.divCeil(usize, bits, 8) catch unreachable);
2034 try dibw.writeLeb128(bytes);2031 try dibw.writeLeb128(bytes);
2035 big_int.writeTwosComplement(2032 big_int.writeTwosComplement(
2036 try dibw.writableSlice(@intCast(bytes)),2033 try dibw.writableSliceGreedy(@intCast(bytes)),
2037 wip_nav.dwarf.endian,2034 wip_nav.dwarf.endian,
2038 );2035 );
2039 dibw.advance(@intCast(bytes));2036 dibw.advance(@intCast(bytes));
...@@ -6083,8 +6080,7 @@ fn writeInt(dwarf: *Dwarf, buf: []u8, int: u64) void {...@@ -6083,8 +6080,7 @@ fn writeInt(dwarf: *Dwarf, buf: []u8, int: u64) void {
6083}6080}
60846081
6085fn writeIntTo(dwarf: *Dwarf, bw: *std.io.BufferedWriter, len: usize, int: u64) !void {6082fn writeIntTo(dwarf: *Dwarf, bw: *std.io.BufferedWriter, len: usize, int: u64) !void {
6086 dwarf.writeInt((try bw.writableSlice(len))[0..len], int);6083 dwarf.writeInt(try bw.writableSlice(len), int);
6087 bw.advance(len);
6088}6084}
60896085
6090fn resolveReloc(dwarf: *Dwarf, source: u64, target: u64, size: u32) RelocError!void {6086fn resolveReloc(dwarf: *Dwarf, source: u64, target: u64, size: u32) RelocError!void {
src/link/Wasm/Flush.zig-3
...@@ -1254,7 +1254,6 @@ const vec_section_header_size = section_header_size + size_header_size;...@@ -1254,7 +1254,6 @@ const vec_section_header_size = section_header_size + size_header_size;
1254fn reserveVecSectionHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 {1254fn reserveVecSectionHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 {
1255 const offset = bw.count;1255 const offset = bw.count;
1256 _ = try bw.writableSlice(vec_section_header_size);1256 _ = try bw.writableSlice(vec_section_header_size);
1257 bw.advance(vec_section_header_size);
1258 return @intCast(offset);1257 return @intCast(offset);
1259}1258}
12601259
...@@ -1275,7 +1274,6 @@ const section_header_size = 1 + size_header_size;...@@ -1275,7 +1274,6 @@ const section_header_size = 1 + size_header_size;
1275fn reserveSectionHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 {1274fn reserveSectionHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 {
1276 const offset = bw.count;1275 const offset = bw.count;
1277 _ = try bw.writableSlice(section_header_size);1276 _ = try bw.writableSlice(section_header_size);
1278 bw.advance(section_header_size);
1279 return @intCast(offset);1277 return @intCast(offset);
1280}1278}
12811279
...@@ -1290,7 +1288,6 @@ const size_header_size = 5;...@@ -1290,7 +1288,6 @@ const size_header_size = 5;
1290fn reserveSizeHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 {1288fn reserveSizeHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 {
1291 const offset = bw.count;1289 const offset = bw.count;
1292 _ = try bw.writableSlice(size_header_size);1290 _ = try bw.writableSlice(size_header_size);
1293 bw.advance(size_header_size);
1294 return @intCast(offset);1291 return @intCast(offset);
1295}1292}
12961293
src/link/riscv.zig+1-1
...@@ -38,7 +38,7 @@ pub fn writeAddend(...@@ -38,7 +38,7 @@ pub fn writeAddend(
38 bw: *std.io.BufferedWriter,38 bw: *std.io.BufferedWriter,
39) std.io.Writer.Error!void {39) std.io.Writer.Error!void {
40 const n = @divExact(@bitSizeOf(Int), 8);40 const n = @divExact(@bitSizeOf(Int), 8);
41 var V: Int = mem.readInt(Int, (try bw.writableSlice(n))[0..n], .little);41 var V: Int = mem.readInt(Int, (try bw.writableSliceGreedy(n))[0..n], .little);
42 const addend: Int = @truncate(value);42 const addend: Int = @truncate(value);
43 switch (op) {43 switch (op) {
44 .add => V +|= addend, // TODO: I think saturating arithmetic is correct here44 .add => V +|= addend, // TODO: I think saturating arithmetic is correct here