| author | |
| committer | |
| log | f3332677825f3173de568d643cbaf68f1e1472ed |
| tree | 31232ea5d95e8556f826a90f34e2fbad29287be8 |
| parent | 98f463ad599bebd46fbc1e4f8ff365ef781bfe5f |
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 unimplemented14 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 { |
| 90 | fn accept(context: *Context, connection: std.net.Server.Connection) void { | 90 | fn accept(context: *Context, connection: std.net.Server.Connection) void { |
| 91 | defer connection.stream.close(); | 91 | defer connection.stream.close(); |
| 92 | 92 | ||
| 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; |
| 162 | 169 | ||
| 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( |
| 182 | fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { | 187 | fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { |
| 183 | const gpa = context.gpa; | 188 | const gpa = context.gpa; |
| 184 | 189 | ||
| 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. |
| 944 | pub fn end(c: *Client) std.io.Writer.Error!void { | 944 | pub 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 HTTP | 14 | /// 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`. |
| 16 | in: *std.io.BufferedReader, | 16 | in: *std.io.BufferedReader, |
| 17 | /// Data from the HTTP server to the HTTP client. | ||
| 17 | out: *std.io.BufferedWriter, | 18 | out: *std.io.BufferedWriter, |
| 18 | /// Keeps track of whether the Server is ready to accept a new request on the | 19 | /// 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 failures | 20 | /// 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 | } |
| 480 | 481 | ||
| 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 | }; |
| 494 | 489 | ||
| 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. If | 493 | /// 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 to | 499 | /// 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 ""; |
| 516 | 510 | const out = request.server.out; | |
| 517 | var h = std.ArrayListUnmanaged(u8).initBuffer(options.send_buffer); | ||
| 518 | 511 | ||
| 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 it | 513 | // 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 | }); |
| 530 | 523 | ||
| 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 | } |
| 535 | 528 | ||
| 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 | } |
| 544 | 537 | ||
| 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 | } |
| 552 | 545 | ||
| 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 | }; |
| 556 | 549 | ||
| 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 | } |
| 572 | 561 | ||
| ... | @@ -836,20 +825,32 @@ pub const Request = struct { | ... | @@ -836,20 +825,32 @@ pub const Request = struct { |
| 836 | }; | 825 | }; |
| 837 | 826 | ||
| 838 | pub const Response = struct { | 827 | pub 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 a | 837 | 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 | } | ||
| 853 | 854 | ||
| 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 | }; |
| 862 | 875 | ||
| 863 | /// When using content-length, asserts that the amount of data sent matches | 876 | /// 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 the | 878 | /// 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 | } |
| 898 | 911 | ||
| ... | @@ -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 decides | 914 | /// 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 | } |
| 909 | 958 | ||
| 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 data | 961 | var total: usize = limit.toInt() orelse return null; |
| 962 | for (headers_and_trailers) |buf| total += buf.len; | ||
| 963 | return total; | ||
| 913 | } | 964 | } |
| 914 | 965 | ||
| 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)); |
| 934 | 976 | 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 | } |
| 975 | 979 | ||
| 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 data | 982 | 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 | } |
| 980 | 995 | ||
| 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 output | 1010 | 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 | } |
| 997 | 1049 | ||
| 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; | ||
| 1001 | 1054 | ||
| 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 | } | ||
| 1026 | 1057 | ||
| 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 | } |
| 1033 | 1099 | ||
| 1034 | /// If using content-length, asserts that writing these bytes to the client | 1100 | /// 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 | } |
| 1042 | 1114 | ||
| 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 | } |
| 1052 | 1124 | ||
| 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 | } |
| 1058 | 1130 | ||
| 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 { |
| 1123 | 1195 | ||
| 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 | } |
| 85 | 85 | ||
| 86 | /// Asserts the provided buffer has total capacity enough for `len`. | 86 | /// Asserts the provided buffer has total capacity enough for `len`. |
| 87 | pub 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`. |
| 89 | pub 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`. | ||
| 98 | pub 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 | } |
| 90 | 103 | ||
| 91 | /// Asserts the provided buffer has total capacity enough for `minimum_length`. | 104 | /// Asserts the provided buffer has total capacity enough for `minimum_length`. |
| 92 | pub 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`. | ||
| 109 | pub 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 | } |
| 113 | 130 | ||
| 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`. | ||
| 115 | pub fn advance(bw: *BufferedWriter, n: usize) void { | 135 | pub 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 | } |
| 137 | 157 | ||
| 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. | ||
| 138 | pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Error!usize { | 161 | pub 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 | } |
| 141 | 164 | ||
| 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`. | ||
| 142 | pub fn writeVec(bw: *BufferedWriter, data: []const []const u8) Writer.Error!usize { | 168 | pub 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 | } |
| 145 | 171 | ||
| 172 | /// Equivalent to `writeSplat` but writes at most `limit` bytes. | ||
| 173 | pub 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 | |||
| 146 | fn passthruWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize { | 186 | fn 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 | } |
| 437 | 477 | ||
| 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. | ||
| 438 | pub fn writeFile( | 481 | pub 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 | } |
| 79 | 79 | ||
| 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 | } |
| 83 | 87 |
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 be | 36 | /// 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` is | 43 | /// 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 { |
| 2344 | 2344 | ||
| 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 }); |
| 752 | 752 | ||
| 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()); |
| 755 | 755 | ||
| 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 { |
| 2101 | 2108 | ||
| 2102 | pub const Server = struct { | 2109 | pub const Server = struct { |
| 2103 | listen_address: Address, | 2110 | listen_address: Address, |
| 2104 | stream: std.net.Stream, | 2111 | stream: Stream, |
| 2105 | 2112 | ||
| 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 | }; |
| 2110 | 2117 |
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; |
| 598 | 596 | ||
| ... | @@ -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 byte | 660 | // 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 byte | 849 | // 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 byte | 868 | // 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 | } |
| 6084 | 6081 | ||
| 6085 | fn writeIntTo(dwarf: *Dwarf, bw: *std.io.BufferedWriter, len: usize, int: u64) !void { | 6082 | fn 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 | } |
| 6089 | 6085 | ||
| 6090 | fn resolveReloc(dwarf: *Dwarf, source: u64, target: u64, size: u32) RelocError!void { | 6086 | fn 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; |
| 1254 | fn reserveVecSectionHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 { | 1254 | fn 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 | } |
| 1260 | 1259 | ||
| ... | @@ -1275,7 +1274,6 @@ const section_header_size = 1 + size_header_size; | ... | @@ -1275,7 +1274,6 @@ const section_header_size = 1 + size_header_size; |
| 1275 | fn reserveSectionHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 { | 1274 | fn 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 | } |
| 1281 | 1279 | ||
| ... | @@ -1290,7 +1288,6 @@ const size_header_size = 5; | ... | @@ -1290,7 +1288,6 @@ const size_header_size = 5; |
| 1290 | fn reserveSizeHeader(bw: *std.io.BufferedWriter) std.io.Writer.Error!u32 { | 1288 | fn 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 | } |
| 1296 | 1293 |
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 here | 44 | .add => V +|= addend, // TODO: I think saturating arithmetic is correct here |