| author | |
| committer | |
| log | 35824e4822acbb1922a5890c5753b48627e3982f |
| tree | 0430b2e39c717f942adc769cb5855ccd204369f1 |
| parent | b26aceba7d5a4e0facb6589b3a6d659ae301d70b |
6 files changed, 118 insertions(+), 168 deletions(-)
lib/std/fs/File.zig+7-33| ... | ... | @@ -1591,8 +1591,7 @@ pub fn writer(file: File) std.io.Writer { |
| 1591 | 1591 | return .{ |
| 1592 | 1592 | .context = interface.handleToOpaque(file.handle), |
| 1593 | 1593 | .vtable = &.{ |
| 1594 | .writev = interface.writev, | |
| 1595 | .splat = interface.splat, | |
| 1594 | .writeSplat = interface.writeSplat, | |
| 1596 | 1595 | .writeFile = interface.writeFile, |
| 1597 | 1596 | }, |
| 1598 | 1597 | }; |
| ... | ... | @@ -1610,45 +1609,20 @@ const interface = struct { |
| 1610 | 1609 | /// vectors through the underlying write calls as possible. |
| 1611 | 1610 | const max_buffers_len = 16; |
| 1612 | 1611 | |
| 1613 | fn writev(context: *anyopaque, data: []const []const u8) anyerror!usize { | |
| 1612 | fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 1614 | 1613 | const file = opaqueToHandle(context); |
| 1615 | ||
| 1616 | 1614 | if (is_windows) { |
| 1617 | // TODO improve this to use WriteFileScatter | |
| 1618 | if (data.len == 0) return 0; | |
| 1619 | const first = data[0]; | |
| 1620 | return windows.WriteFile(file, first.base[0..first.len], null); | |
| 1615 | if (data.len == 1 and splat == 0) return 0; | |
| 1616 | return windows.WriteFile(file, data[0], null); | |
| 1621 | 1617 | } |
| 1622 | ||
| 1623 | 1618 | var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined; |
| 1624 | 1619 | const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, data.len)]; |
| 1625 | 1620 | for (iovecs, data[0..iovecs.len]) |*v, d| v.* = .{ |
| 1626 | 1621 | .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length. |
| 1627 | 1622 | .len = d.len, |
| 1628 | 1623 | }; |
| 1629 | return std.posix.writev(file, iovecs); | |
| 1630 | } | |
| 1631 | ||
| 1632 | fn splat(context: *anyopaque, headers: []const []const u8, pattern: []const u8, n: usize) anyerror!usize { | |
| 1633 | const file = opaqueToHandle(context); | |
| 1634 | ||
| 1635 | if (is_windows) { | |
| 1636 | // TODO improve this to use WriteFileScatter | |
| 1637 | if (headers.len > 0) { | |
| 1638 | const first = headers[0]; | |
| 1639 | return windows.WriteFile(file, first, null); | |
| 1640 | } | |
| 1641 | if (n > 0) return windows.WriteFile(file, pattern, null); | |
| 1642 | return 0; | |
| 1643 | } | |
| 1644 | ||
| 1645 | var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined; | |
| 1646 | const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers.len)]; | |
| 1647 | for (iovecs, headers[0..iovecs.len]) |*v, d| v.* = .{ | |
| 1648 | .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length. | |
| 1649 | .len = d.len, | |
| 1650 | }; | |
| 1651 | return std.posix.writev(file, iovecs); | |
| 1624 | const send_iovecs = if (splat == 0) iovecs[0 .. iovecs.len - 1] else iovecs; | |
| 1625 | return std.posix.writev(file, send_iovecs); | |
| 1652 | 1626 | } |
| 1653 | 1627 | |
| 1654 | 1628 | fn writeFile( |
| ... | ... | @@ -1662,7 +1636,7 @@ const interface = struct { |
| 1662 | 1636 | const out_fd = opaqueToHandle(context); |
| 1663 | 1637 | const in_fd = in_file.handle; |
| 1664 | 1638 | const len_int = switch (in_len) { |
| 1665 | .zero => return interface.writev(context, headers_and_trailers), | |
| 1639 | .zero => return interface.writeSplat(context, headers_and_trailers, 1), | |
| 1666 | 1640 | .entire_file => 0, |
| 1667 | 1641 | else => in_len.int(), |
| 1668 | 1642 | }; |
lib/std/io.zig+5-11| ... | ... | @@ -344,22 +344,16 @@ pub const tty = @import("io/tty.zig"); |
| 344 | 344 | pub const null_writer: Writer = .{ |
| 345 | 345 | .context = undefined, |
| 346 | 346 | .vtable = &.{ |
| 347 | .writev = null_writev, | |
| 348 | .splat = null_splat, | |
| 347 | .writeSplat = null_writeSplat, | |
| 349 | 348 | .writeFile = null_writeFile, |
| 350 | 349 | }, |
| 351 | 350 | }; |
| 352 | 351 | |
| 353 | fn null_writev(context: *anyopaque, data: []const []const u8) anyerror!usize { | |
| 352 | fn null_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 354 | 353 | _ = context; |
| 355 | var written: usize = 0; | |
| 356 | for (data) |bytes| written += bytes.len; | |
| 357 | return written; | |
| 358 | } | |
| 359 | ||
| 360 | fn null_splat(context: *anyopaque, headers: []const []const u8, pattern: []const u8, n: usize) anyerror!usize { | |
| 361 | _ = context; | |
| 362 | var written: usize = pattern.len * n; | |
| 354 | const headers = data[0 .. data.len - 1]; | |
| 355 | const pattern = data[headers.len..]; | |
| 356 | var written: usize = pattern.len * splat; | |
| 363 | 357 | for (headers) |bytes| written += bytes.len; |
| 364 | 358 | return written; |
| 365 | 359 | } |
lib/std/io/AllocatingWriter.zig+17-15| ... | ... | @@ -21,7 +21,7 @@ allocator: std.mem.Allocator, |
| 21 | 21 | buffered_writer: std.io.BufferedWriter, |
| 22 | 22 | |
| 23 | 23 | const vtable: std.io.Writer.VTable = .{ |
| 24 | .writev = writev, | |
| 24 | .writeSplat = writeSplat, | |
| 25 | 25 | .writeFile = writeFile, |
| 26 | 26 | }; |
| 27 | 27 | |
| ... | ... | @@ -98,35 +98,37 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void { |
| 98 | 98 | aw.written.len = 0; |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | fn writev(context: *anyopaque, data: []const []const u8) anyerror!usize { | |
| 102 | return splat(context, data, &.{}, 0); | |
| 103 | } | |
| 104 | ||
| 105 | fn splat(context: *anyopaque, headers: []const []const u8, pattern: []const u8, n: usize) anyerror!usize { | |
| 101 | fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 106 | 102 | const aw: *AllocatingWriter = @alignCast(@ptrCast(context)); |
| 107 | 103 | const start_len = aw.written.len; |
| 108 | 104 | const bw = &aw.buffered_writer; |
| 109 | assert(headers[0].ptr == aw.written.ptr + start_len); | |
| 105 | const skip_first = data[0].ptr == aw.written.ptr + start_len; | |
| 106 | const items_len = if (skip_first) start_len + data[0].len else start_len; | |
| 110 | 107 | var list: std.ArrayListUnmanaged(u8) = .{ |
| 111 | .items = aw.written.ptr[0 .. start_len + headers[0].len], | |
| 108 | .items = aw.written.ptr[0..items_len], | |
| 112 | 109 | .capacity = start_len + bw.buffer.len, |
| 113 | 110 | }; |
| 114 | 111 | defer setArrayList(aw, list); |
| 115 | const rest = headers[1..]; | |
| 116 | var new_capacity: usize = list.capacity + pattern.len * n; | |
| 112 | const rest = data[1 .. data.len - 1]; | |
| 113 | const pattern = data[data.len - 1]; | |
| 114 | var new_capacity: usize = list.capacity + pattern.len * splat; | |
| 117 | 115 | for (rest) |bytes| new_capacity += bytes.len; |
| 118 | 116 | try list.ensureTotalCapacity(aw.allocator, new_capacity + 1); |
| 119 | 117 | for (rest) |bytes| list.appendSliceAssumeCapacity(bytes); |
| 120 | if (pattern.len == 1) { | |
| 121 | list.appendNTimesAssumeCapacity(pattern[0], n); | |
| 122 | } else { | |
| 123 | for (0..n) |_| list.appendSliceAssumeCapacity(pattern); | |
| 124 | } | |
| 118 | appendPatternAssumeCapacity(&list, pattern, splat); | |
| 125 | 119 | aw.written = list.items; |
| 126 | 120 | bw.buffer = list.unusedCapacitySlice(); |
| 127 | 121 | return list.items.len - start_len; |
| 128 | 122 | } |
| 129 | 123 | |
| 124 | fn appendPatternAssumeCapacity(list: *std.ArrayListUnmanaged(u8), pattern: []const u8, splat: usize) void { | |
| 125 | if (pattern.len == 1) { | |
| 126 | list.appendNTimesAssumeCapacity(pattern[0], splat); | |
| 127 | } else { | |
| 128 | for (0..splat) |_| list.appendSliceAssumeCapacity(pattern); | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 130 | 132 | fn writeFile( |
| 131 | 133 | context: *anyopaque, |
| 132 | 134 | file: std.fs.File, |
lib/std/io/BufferedWriter.zig+69-76| ... | ... | @@ -33,16 +33,14 @@ pub fn writer(bw: *BufferedWriter) Writer { |
| 33 | 33 | return .{ |
| 34 | 34 | .context = bw, |
| 35 | 35 | .vtable = &.{ |
| 36 | .writev = passthru_writev, | |
| 37 | .splat = passthru_splat, | |
| 36 | .write = passthru_writeSplat, | |
| 38 | 37 | .writeFile = passthru_writeFile, |
| 39 | 38 | }, |
| 40 | 39 | }; |
| 41 | 40 | } |
| 42 | 41 | |
| 43 | 42 | const fixed_vtable: Writer.VTable = .{ |
| 44 | .writev = fixed_writev, | |
| 45 | .splat = fixed_splat, | |
| 43 | .writeSplat = fixed_writeSplat, | |
| 46 | 44 | .writeFile = fixed_writeFile, |
| 47 | 45 | }; |
| 48 | 46 | |
| ... | ... | @@ -81,7 +79,7 @@ pub fn flush(bw: *BufferedWriter) anyerror!void { |
| 81 | 79 | pub fn writevAll(bw: *BufferedWriter, data: []const []const u8) anyerror!void { |
| 82 | 80 | var i: usize = 0; |
| 83 | 81 | while (true) { |
| 84 | var n = try writev(bw, data[i..]); | |
| 82 | var n = try passthru_writeSplat(bw, data[i..], 1); | |
| 85 | 83 | while (n >= data[i].len) { |
| 86 | 84 | n -= data[i].len; |
| 87 | 85 | i += 1; |
| ... | ... | @@ -91,14 +89,16 @@ pub fn writevAll(bw: *BufferedWriter, data: []const []const u8) anyerror!void { |
| 91 | 89 | } |
| 92 | 90 | } |
| 93 | 91 | |
| 94 | pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize { | |
| 95 | return passthru_writev(bw, data); | |
| 92 | pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) anyerror!usize { | |
| 93 | return passthru_writeSplat(bw, data, splat); | |
| 96 | 94 | } |
| 97 | 95 | |
| 98 | fn passthru_writev(context: *anyopaque, data: []const []const u8) anyerror!usize { | |
| 96 | fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 99 | 97 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 100 | 98 | const buffer = bw.buffer; |
| 101 | 99 | const start_end = bw.end; |
| 100 | ||
| 101 | var buffers: [max_buffers_len][]const u8 = undefined; | |
| 102 | 102 | var end = bw.end; |
| 103 | 103 | for (data, 0..) |bytes, i| { |
| 104 | 104 | const new_end = end + bytes.len; |
| ... | ... | @@ -108,102 +108,79 @@ fn passthru_writev(context: *anyopaque, data: []const []const u8) anyerror!usize |
| 108 | 108 | end = new_end; |
| 109 | 109 | continue; |
| 110 | 110 | } |
| 111 | if (end == 0) return bw.unbuffered_writer.writev(data); | |
| 112 | var buffers: [max_buffers_len][]const u8 = undefined; | |
| 111 | if (end == 0) return bw.unbuffered_writer.writeSplat(data, splat); | |
| 113 | 112 | buffers[0] = buffer[0..end]; |
| 114 | 113 | const remaining_data = data[i..]; |
| 115 | 114 | const remaining_buffers = buffers[1..]; |
| 116 | 115 | const len: usize = @min(remaining_data.len, remaining_buffers.len); |
| 117 | 116 | @memcpy(remaining_buffers[0..len], remaining_data[0..len]); |
| 118 | const n = try bw.unbuffered_writer.writev(buffers[0 .. len + 1]); | |
| 119 | if (n < end) { | |
| 120 | @branchHint(.unlikely); | |
| 121 | const remainder = buffer[n..end]; | |
| 122 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); | |
| 123 | bw.end = remainder.len; | |
| 124 | return end - start_end; | |
| 125 | } | |
| 126 | bw.end = 0; | |
| 127 | return n - start_end; | |
| 128 | } | |
| 129 | bw.end = end; | |
| 130 | return end - start_end; | |
| 131 | } | |
| 132 | ||
| 133 | fn passthru_splat(context: *anyopaque, headers: []const []const u8, pattern: []const u8, n: usize) anyerror!usize { | |
| 134 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); | |
| 135 | const buffer = bw.buffer; | |
| 136 | const start_end = bw.end; | |
| 137 | ||
| 138 | var end = bw.end; | |
| 139 | for (headers, 0..) |bytes, i| { | |
| 140 | const new_end = end + bytes.len; | |
| 141 | if (new_end <= buffer.len) { | |
| 142 | @branchHint(.likely); | |
| 143 | @memcpy(buffer[end..new_end], bytes); | |
| 144 | end = new_end; | |
| 145 | continue; | |
| 146 | } | |
| 147 | if (end == 0) return bw.unbuffered_writer.splat(headers, pattern, n); | |
| 148 | var buffers: [max_buffers_len][]const u8 = undefined; | |
| 149 | buffers[0] = buffer[0..end]; | |
| 150 | const remaining_headers = headers[i..]; | |
| 151 | const remaining_buffers = buffers[1..]; | |
| 152 | const len: usize = @min(remaining_headers.len, remaining_buffers.len); | |
| 153 | @memcpy(remaining_buffers[0..len], remaining_headers[0..len]); | |
| 154 | 117 | const send_buffers = buffers[0 .. len + 1]; |
| 155 | if (len >= remaining_headers.len) { | |
| 118 | if (len >= remaining_data.len) { | |
| 156 | 119 | @branchHint(.likely); |
| 157 | // Made it past the headers, so we can call `splat`. | |
| 158 | const written = try bw.unbuffered_writer.splat(send_buffers, pattern, n); | |
| 159 | if (written < end) { | |
| 120 | // Made it past the headers, so we can enable splatting. | |
| 121 | const n = try bw.unbuffered_writer.writeSplat(send_buffers, splat); | |
| 122 | if (n < end) { | |
| 160 | 123 | @branchHint(.unlikely); |
| 161 | const remainder = buffer[written..end]; | |
| 124 | const remainder = buffer[n..end]; | |
| 162 | 125 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 163 | 126 | bw.end = remainder.len; |
| 164 | 127 | return end - start_end; |
| 165 | 128 | } |
| 166 | 129 | bw.end = 0; |
| 167 | return written - start_end; | |
| 130 | return n - start_end; | |
| 168 | 131 | } |
| 169 | const written = try bw.unbuffered_writer.writev(send_buffers); | |
| 170 | if (written < end) { | |
| 132 | const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1); | |
| 133 | if (n < end) { | |
| 171 | 134 | @branchHint(.unlikely); |
| 172 | const remainder = buffer[written..end]; | |
| 135 | const remainder = buffer[n..end]; | |
| 173 | 136 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 174 | 137 | bw.end = remainder.len; |
| 175 | 138 | return end - start_end; |
| 176 | 139 | } |
| 177 | 140 | bw.end = 0; |
| 178 | return written - start_end; | |
| 141 | return n - start_end; | |
| 179 | 142 | } |
| 180 | 143 | |
| 144 | const pattern = data[data.len - 1]; | |
| 145 | ||
| 146 | if (splat == 0) { | |
| 147 | @branchHint(.unlikely); | |
| 148 | // It was added in the loop above; undo it here. | |
| 149 | end -= pattern.len; | |
| 150 | bw.end = end; | |
| 151 | return end - start_end; | |
| 152 | } | |
| 153 | ||
| 154 | const remaining_splat = splat - 1; | |
| 155 | ||
| 181 | 156 | switch (pattern.len) { |
| 182 | 157 | 0 => { |
| 183 | 158 | bw.end = end; |
| 184 | 159 | return end - start_end; |
| 185 | 160 | }, |
| 186 | 161 | 1 => { |
| 187 | const new_end = end + n; | |
| 162 | const new_end = end + remaining_splat; | |
| 188 | 163 | if (new_end <= buffer.len) { |
| 189 | 164 | @branchHint(.likely); |
| 190 | 165 | @memset(buffer[end..new_end], pattern[0]); |
| 191 | 166 | bw.end = new_end; |
| 192 | 167 | return end - start_end; |
| 193 | 168 | } |
| 194 | const written = try bw.unbuffered_writer.splat(buffer[0..end], pattern, n); | |
| 195 | if (written < end) { | |
| 169 | buffers[0] = buffer[0..end]; | |
| 170 | buffers[1] = pattern; | |
| 171 | const n = try bw.unbuffered_writer.writeSplat(buffers[0..2], remaining_splat); | |
| 172 | if (n < end) { | |
| 196 | 173 | @branchHint(.unlikely); |
| 197 | const remainder = buffer[written..end]; | |
| 174 | const remainder = buffer[n..end]; | |
| 198 | 175 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 199 | 176 | bw.end = remainder.len; |
| 200 | 177 | return end - start_end; |
| 201 | 178 | } |
| 202 | 179 | bw.end = 0; |
| 203 | return written - start_end; | |
| 180 | return n - start_end; | |
| 204 | 181 | }, |
| 205 | 182 | else => { |
| 206 | const new_end = end + pattern.len * n; | |
| 183 | const new_end = end + pattern.len * remaining_splat; | |
| 207 | 184 | if (new_end <= buffer.len) { |
| 208 | 185 | @branchHint(.likely); |
| 209 | 186 | while (end < new_end) : (end += pattern.len) { |
| ... | ... | @@ -212,16 +189,18 @@ fn passthru_splat(context: *anyopaque, headers: []const []const u8, pattern: []c |
| 212 | 189 | bw.end = end; |
| 213 | 190 | return end - start_end; |
| 214 | 191 | } |
| 215 | const written = try bw.unbuffered_writer.splat(buffer[0..end], pattern, n); | |
| 216 | if (written < end) { | |
| 192 | buffers[0] = buffer[0..end]; | |
| 193 | buffers[1] = pattern; | |
| 194 | const n = try bw.unbuffered_writer.writeSplat(buffers[0..2], remaining_splat); | |
| 195 | if (n < end) { | |
| 217 | 196 | @branchHint(.unlikely); |
| 218 | const remainder = buffer[written..end]; | |
| 197 | const remainder = buffer[n..end]; | |
| 219 | 198 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 220 | 199 | bw.end = remainder.len; |
| 221 | 200 | return end - start_end; |
| 222 | 201 | } |
| 223 | 202 | bw.end = 0; |
| 224 | return written - start_end; | |
| 203 | return n - start_end; | |
| 225 | 204 | }, |
| 226 | 205 | } |
| 227 | 206 | } |
| ... | ... | @@ -237,15 +216,24 @@ fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize { |
| 237 | 216 | return error.NoSpaceLeft; |
| 238 | 217 | } |
| 239 | 218 | |
| 240 | fn fixed_splat(context: *anyopaque, headers: []const []const u8, pattern: []const u8, n: usize) anyerror!usize { | |
| 219 | /// When this function is called it means the buffer got full, so it's time | |
| 220 | /// to return an error. However, we still need to make sure all of the | |
| 221 | /// available buffer has been filled. | |
| 222 | fn fixed_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 241 | 223 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 224 | for (data) |bytes| { | |
| 225 | const dest = bw.buffer[bw.end..]; | |
| 226 | if (dest.len == 0) return error.NoSpaceLeft; | |
| 227 | const len = @min(bytes.len, dest.len); | |
| 228 | @memcpy(dest[0..len], bytes[0..len]); | |
| 229 | bw.end += len; | |
| 230 | } | |
| 231 | const pattern = data[data.len - 1]; | |
| 242 | 232 | const dest = bw.buffer[bw.end..]; |
| 243 | if (headers.len > 0) { | |
| 244 | @memcpy(dest, headers[0][0..dest.len]); | |
| 245 | } else switch (pattern.len) { | |
| 233 | switch (pattern.len) { | |
| 246 | 234 | 0 => unreachable, |
| 247 | 235 | 1 => @memset(dest, pattern[0]), |
| 248 | else => for (0..n) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern), | |
| 236 | else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern), | |
| 249 | 237 | } |
| 250 | 238 | return error.NoSpaceLeft; |
| 251 | 239 | } |
| ... | ... | @@ -329,21 +317,26 @@ pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void { |
| 329 | 317 | /// |
| 330 | 318 | /// Does maximum of one underlying `Writer.VTable.writev`. |
| 331 | 319 | pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize { |
| 332 | return passthru_splat(bw, &.{}, &.{byte}, n); | |
| 320 | return passthru_writeSplat(bw, &.{&.{byte}}, n); | |
| 333 | 321 | } |
| 334 | 322 | |
| 335 | 323 | /// Writes the same slice many times, performing the underlying write call as |
| 336 | 324 | /// many times as necessary. |
| 337 | pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, n: usize) anyerror!void { | |
| 338 | var remaining: usize = n * bytes.len; | |
| 339 | while (remaining > 0) remaining -= try splatBytes(bw, bytes, remaining); | |
| 325 | pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyerror!void { | |
| 326 | var remaining_bytes: usize = bytes.len * splat; | |
| 327 | remaining_bytes -= try splatBytes(bw, bytes, splat); | |
| 328 | while (remaining_bytes > 0) { | |
| 329 | const leftover = remaining_bytes % bytes.len; | |
| 330 | const buffers: [2][]const u8 = .{ bytes[bytes.len - leftover ..], bytes }; | |
| 331 | remaining_bytes -= try splatBytes(bw, &buffers, splat); | |
| 332 | } | |
| 340 | 333 | } |
| 341 | 334 | |
| 342 | 335 | /// Writes the same slice many times, allowing short writes. |
| 343 | 336 | /// |
| 344 | 337 | /// Does maximum of one underlying `Writer.VTable.writev`. |
| 345 | 338 | pub fn splatBytes(bw: *BufferedWriter, bytes: []const u8, n: usize) anyerror!usize { |
| 346 | return passthru_splat(bw, &.{}, bytes, n); | |
| 339 | return passthru_writeSplat(bw, &.{bytes}, n); | |
| 347 | 340 | } |
| 348 | 341 | |
| 349 | 342 | /// Asserts the `buffer` was initialized with a capacity of at least `@sizeOf(T)` bytes. |
lib/std/io/CountingWriter.zig+8-16| ... | ... | @@ -13,8 +13,7 @@ pub fn writer(cw: *CountingWriter) Writer { |
| 13 | 13 | return .{ |
| 14 | 14 | .context = cw, |
| 15 | 15 | .vtable = &.{ |
| 16 | .writev = passthru_writev, | |
| 17 | .splat = passthru_splat, | |
| 16 | .writeSplat = passthru_writeSplat, | |
| 18 | 17 | .writeFile = passthru_writeFile, |
| 19 | 18 | }, |
| 20 | 19 | }; |
| ... | ... | @@ -27,18 +26,11 @@ pub fn unbufferedWriter(cw: *CountingWriter) std.io.BufferedWriter { |
| 27 | 26 | }; |
| 28 | 27 | } |
| 29 | 28 | |
| 30 | fn passthru_writev(context: *anyopaque, data: []const []const u8) anyerror!usize { | |
| 29 | fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 31 | 30 | const cw: *CountingWriter = @alignCast(@ptrCast(context)); |
| 32 | const written = try cw.child_writer.writev(data); | |
| 33 | cw.bytes_written += written; | |
| 34 | return written; | |
| 35 | } | |
| 36 | ||
| 37 | fn passthru_splat(context: *anyopaque, header: []const u8, pattern: []const u8, n: usize) anyerror!usize { | |
| 38 | const cw: *CountingWriter = @alignCast(@ptrCast(context)); | |
| 39 | const written = try cw.child_writer.splat(header, pattern, n); | |
| 40 | cw.bytes_written += written; | |
| 41 | return written; | |
| 31 | const n = try cw.child_writer.writeSplat(data, splat); | |
| 32 | cw.bytes_written += n; | |
| 33 | return n; | |
| 42 | 34 | } |
| 43 | 35 | |
| 44 | 36 | fn passthru_writeFile( |
| ... | ... | @@ -50,9 +42,9 @@ fn passthru_writeFile( |
| 50 | 42 | headers_len: usize, |
| 51 | 43 | ) anyerror!usize { |
| 52 | 44 | const cw: *CountingWriter = @alignCast(@ptrCast(context)); |
| 53 | const written = try cw.child_writer.writeFile(file, offset, len, headers_and_trailers, headers_len); | |
| 54 | cw.bytes_written += written; | |
| 55 | return written; | |
| 45 | const n = try cw.child_writer.writeFile(file, offset, len, headers_and_trailers, headers_len); | |
| 46 | cw.bytes_written += n; | |
| 47 | return n; | |
| 56 | 48 | } |
| 57 | 49 | |
| 58 | 50 | test CountingWriter { |
lib/std/io/Writer.zig+12-17| ... | ... | @@ -8,25 +8,16 @@ vtable: *const VTable, |
| 8 | 8 | pub const VTable = struct { |
| 9 | 9 | /// Each slice in `data` is written in order. |
| 10 | 10 | /// |
| 11 | /// Number of bytes actually written is returned. | |
| 12 | /// | |
| 13 | /// Number of bytes returned may be zero, which does not mean | |
| 14 | /// end-of-stream. A subsequent call may return nonzero, or may signal end | |
| 15 | /// of stream via an error. | |
| 16 | writev: *const fn (context: *anyopaque, data: []const []const u8) anyerror!usize, | |
| 17 | ||
| 18 | /// `headers_and_pattern` must have length of at least one. The last slice | |
| 19 | /// is `pattern` which is the byte sequence to repeat `n` times. The rest | |
| 20 | /// of the slices are headers to write before the pattern. | |
| 21 | /// | |
| 22 | /// When `n == 1`, this is equivalent to `writev`. | |
| 11 | /// `data.len` must be greater than zero, and the last element of `data` is | |
| 12 | /// special. It is repeated as necessary so that it is written `splat` | |
| 13 | /// number of times. | |
| 23 | 14 | /// |
| 24 | 15 | /// Number of bytes actually written is returned. |
| 25 | 16 | /// |
| 26 | 17 | /// Number of bytes returned may be zero, which does not mean |
| 27 | 18 | /// end-of-stream. A subsequent call may return nonzero, or may signal end |
| 28 | 19 | /// of stream via an error. |
| 29 | splat: *const fn (context: *anyopaque, headers_and_pattern: []const []const u8, n: usize) anyerror!usize, | |
| 20 | writeSplat: *const fn (context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize, | |
| 30 | 21 | |
| 31 | 22 | /// Writes contents from an open file. `headers` are written first, then `len` |
| 32 | 23 | /// bytes of `file` starting from `offset`, then `trailers`. |
| ... | ... | @@ -67,7 +58,11 @@ pub const VTable = struct { |
| 67 | 58 | }; |
| 68 | 59 | |
| 69 | 60 | pub fn writev(w: Writer, data: []const []const u8) anyerror!usize { |
| 70 | return w.vtable.writev(w.context, data); | |
| 61 | return w.vtable.writeSplat(w.context, data, 1); | |
| 62 | } | |
| 63 | ||
| 64 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) anyerror!usize { | |
| 65 | return w.vtable.writeSplat(w.context, data, splat); | |
| 71 | 66 | } |
| 72 | 67 | |
| 73 | 68 | pub fn writeFile( |
| ... | ... | @@ -83,12 +78,12 @@ pub fn writeFile( |
| 83 | 78 | |
| 84 | 79 | pub fn write(w: Writer, bytes: []const u8) anyerror!usize { |
| 85 | 80 | const single: [1][]const u8 = .{bytes}; |
| 86 | return w.vtable.writev(w.context, &single); | |
| 81 | return w.vtable.writeSplat(w.context, &single, 1); | |
| 87 | 82 | } |
| 88 | 83 | |
| 89 | 84 | pub fn writeAll(w: Writer, bytes: []const u8) anyerror!void { |
| 90 | 85 | var index: usize = 0; |
| 91 | while (index < bytes.len) index += try write(w, bytes[index..]); | |
| 86 | while (index < bytes.len) index += try w.vtable.writeSplat(w.context, &.{bytes[index..]}, 1); | |
| 92 | 87 | } |
| 93 | 88 | |
| 94 | 89 | ///// Directly calls `writeAll` many times to render the formatted text. To |
| ... | ... | @@ -102,7 +97,7 @@ pub fn writeAll(w: Writer, bytes: []const u8) anyerror!void { |
| 102 | 97 | pub fn writevAll(w: Writer, data: [][]const u8) anyerror!void { |
| 103 | 98 | var i: usize = 0; |
| 104 | 99 | while (true) { |
| 105 | var n = try w.vtable.writev(w.context, data[i..]); | |
| 100 | var n = try w.vtable.writeSplat(w.context, data[i..], 1); | |
| 106 | 101 | while (n >= data[i].len) { |
| 107 | 102 | n -= data[i].len; |
| 108 | 103 | i += 1; |