authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-11 00:06:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:26-07:00
log383afd19d73c7d98c8d1f7fe9d474b980517372e
tree48a66ca514f3b3610feb037fa4295fecc61114b1
parent4ee25345668ff93f1a4a857436b14fc305f8cee7

std.io.BufferedWriter: don't use ArrayList for this

it's the wrong abstraction and is only a footgun when used this way.

7 files changed, 188 insertions(+), 175 deletions(-)

lib/std/fmt.zig+1-1
...@@ -856,7 +856,7 @@ pub fn count(comptime fmt: []const u8, args: anytype) usize {...@@ -856,7 +856,7 @@ pub fn count(comptime fmt: []const u8, args: anytype) usize {
856 var buffer: [std.atomic.cache_line]u8 = undefined;856 var buffer: [std.atomic.cache_line]u8 = undefined;
857 var bw = std.io.Writer.null.buffered(&buffer);857 var bw = std.io.Writer.null.buffered(&buffer);
858 bw.print(fmt, args) catch unreachable;858 bw.print(fmt, args) catch unreachable;
859 return bw.bytes_written;859 return bw.count;
860}860}
861861
862pub const AllocPrintError = error{OutOfMemory};862pub const AllocPrintError = error{OutOfMemory};
lib/std/io/AllocatingWriter.zig+20-10
...@@ -27,19 +27,27 @@ const vtable: std.io.Writer.VTable = .{...@@ -27,19 +27,27 @@ const vtable: std.io.Writer.VTable = .{
27};27};
2828
29/// Sets the `AllocatingWriter` to an empty state.29/// Sets the `AllocatingWriter` to an empty state.
30pub fn init(aw: *AllocatingWriter, allocator: std.mem.Allocator) *std.io.BufferedWriter {30pub fn init(aw: *AllocatingWriter, allocator: std.mem.Allocator) void {
31 initOwnedSlice(aw, allocator, &.{});
32}
33
34pub fn initCapacity(aw: *AllocatingWriter, allocator: std.mem.Allocator, capacity: usize) error{OutOfMemory}!void {
35 const initial_buffer = try allocator.alloc(u8, capacity);
36 initOwnedSlice(aw, allocator, initial_buffer);
37}
38
39pub fn initOwnedSlice(aw: *AllocatingWriter, allocator: std.mem.Allocator, slice: []u8) void {
31 aw.* = .{40 aw.* = .{
32 .written = &.{},41 .written = slice[0..0],
33 .allocator = allocator,42 .allocator = allocator,
34 .buffered_writer = .{43 .buffered_writer = .{
35 .unbuffered_writer = .{44 .unbuffered_writer = .{
36 .context = aw,45 .context = aw,
37 .vtable = &vtable,46 .vtable = &vtable,
38 },47 },
39 .buffer = &.{},48 .buffer = slice,
40 },49 },
41 };50 };
42 return &aw.buffered_writer;
43}51}
4452
45pub fn deinit(aw: *AllocatingWriter) void {53pub fn deinit(aw: *AllocatingWriter) void {
...@@ -118,7 +126,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {...@@ -118,7 +126,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
118 aw.written.len = 0;126 aw.written.len = 0;
119}127}
120128
121fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {129fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
122 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));130 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
123 const start_len = aw.written.len;131 const start_len = aw.written.len;
124 const bw = &aw.buffered_writer;132 const bw = &aw.buffered_writer;
...@@ -150,9 +158,9 @@ fn appendPatternAssumeCapacity(list: *std.ArrayListUnmanaged(u8), pattern: []con...@@ -150,9 +158,9 @@ fn appendPatternAssumeCapacity(list: *std.ArrayListUnmanaged(u8), pattern: []con
150}158}
151159
152fn writeFile(160fn writeFile(
153 context: *anyopaque,161 context: ?*anyopaque,
154 file: std.fs.File,162 file: std.fs.File,
155 offset: u64,163 offset: std.io.Writer.Offset,
156 len: std.io.Writer.FileLen,164 len: std.io.Writer.FileLen,
157 headers_and_trailers_full: []const []const u8,165 headers_and_trailers_full: []const []const u8,
158 headers_len_full: usize,166 headers_len_full: usize,
...@@ -168,13 +176,14 @@ fn writeFile(...@@ -168,13 +176,14 @@ fn writeFile(
168 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };176 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
169 } else .{ headers_and_trailers_full, headers_len_full };177 } else .{ headers_and_trailers_full, headers_len_full };
170 const trailers = headers_and_trailers[headers_len..];178 const trailers = headers_and_trailers[headers_len..];
179 const pos = offset.toInt() orelse @panic("TODO treat file as stream");
171 if (len == .entire_file) {180 if (len == .entire_file) {
172 var new_capacity: usize = list.capacity + std.atomic.cache_line;181 var new_capacity: usize = list.capacity + std.atomic.cache_line;
173 for (headers_and_trailers) |bytes| new_capacity += bytes.len;182 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
174 try list.ensureTotalCapacity(gpa, new_capacity);183 try list.ensureTotalCapacity(gpa, new_capacity);
175 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);184 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
176 const dest = list.items.ptr[list.items.len..list.capacity];185 const dest = list.items.ptr[list.items.len..list.capacity];
177 const n = try file.pread(dest, offset);186 const n = try file.pread(dest, pos);
178 if (n == 0) {187 if (n == 0) {
179 new_capacity = list.capacity;188 new_capacity = list.capacity;
180 for (trailers) |bytes| new_capacity += bytes.len;189 for (trailers) |bytes| new_capacity += bytes.len;
...@@ -190,7 +199,7 @@ fn writeFile(...@@ -190,7 +199,7 @@ fn writeFile(
190 try list.ensureTotalCapacity(gpa, new_capacity);199 try list.ensureTotalCapacity(gpa, new_capacity);
191 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);200 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
192 const dest = list.items.ptr[list.items.len..][0..len.int()];201 const dest = list.items.ptr[list.items.len..][0..len.int()];
193 const n = try file.pread(dest, offset);202 const n = try file.pread(dest, pos);
194 list.items.len += n;203 list.items.len += n;
195 if (n < dest.len) {204 if (n < dest.len) {
196 return list.items.len - start_len;205 return list.items.len - start_len;
...@@ -201,8 +210,9 @@ fn writeFile(...@@ -201,8 +210,9 @@ fn writeFile(
201210
202test AllocatingWriter {211test AllocatingWriter {
203 var aw: AllocatingWriter = undefined;212 var aw: AllocatingWriter = undefined;
204 const bw = aw.init(std.testing.allocator);213 aw.init(std.testing.allocator);
205 defer aw.deinit();214 defer aw.deinit();
215 const bw = &aw.buffered_writer;
206216
207 const x: i32 = 42;217 const x: i32 = 42;
208 const y: i32 = 1234;218 const y: i32 = 1234;
lib/std/io/BufferedReader.zig+54-51
...@@ -74,7 +74,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {...@@ -74,7 +74,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
74 br.* = .{74 br.* = .{
75 .seek = 0,75 .seek = 0,
76 .storage = .{76 .storage = .{
77 .buffer = .fromOwnedSlice(@constCast(buffer)),77 .buffer = @constCast(buffer),
78 .unbuffered_writer = .{78 .unbuffered_writer = .{
79 .context = undefined,79 .context = undefined,
80 .vtable = &eof_writer,80 .vtable = &eof_writer,
...@@ -88,9 +88,10 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {...@@ -88,9 +88,10 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
88}88}
8989
90pub fn storageBuffer(br: *BufferedReader) []u8 {90pub fn storageBuffer(br: *BufferedReader) []u8 {
91 assert(br.storage.unbuffered_writer.vtable == &eof_writer);91 const storage = &br.storage;
92 assert(storage.unbuffered_writer.vtable == &eof_writer);
92 assert(br.unbuffered_reader.vtable == &eof_reader);93 assert(br.unbuffered_reader.vtable == &eof_reader);
93 return br.storage.buffer.allocatedSlice();94 return storage.buffer;
94}95}
9596
96/// Although `BufferedReader` can easily satisfy the `Reader` interface, it's97/// Although `BufferedReader` can easily satisfy the `Reader` interface, it's
...@@ -108,7 +109,8 @@ pub fn reader(br: *BufferedReader) Reader {...@@ -108,7 +109,8 @@ pub fn reader(br: *BufferedReader) Reader {
108109
109fn passthru_read(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) anyerror!Reader.RwResult {110fn passthru_read(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) anyerror!Reader.RwResult {
110 const br: *BufferedReader = @alignCast(@ptrCast(ctx));111 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
111 const buffer = br.storage.buffer.items;112 const storage = &br.storage;
113 const buffer = storage.buffer[0..storage.end];
112 const buffered = buffer[br.seek..];114 const buffered = buffer[br.seek..];
113 const limited = buffered[0..limit.min(buffered.len)];115 const limited = buffered[0..limit.min(buffered.len)];
114 if (limited.len > 0) {116 if (limited.len > 0) {
...@@ -135,7 +137,7 @@ pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void {...@@ -135,7 +137,7 @@ pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void {
135}137}
136138
137pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {139pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
138 if (seek_by > br.storage.buffer.items.len - br.seek) return error.Unseekable; // TODO140 if (seek_by > br.storage.end - br.seek) return error.Unseekable; // TODO
139 br.seek += @abs(seek_by);141 br.seek += @abs(seek_by);
140}142}
141143
...@@ -178,10 +180,10 @@ pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {...@@ -178,10 +180,10 @@ pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {
178/// * `peek`180/// * `peek`
179/// * `toss`181/// * `toss`
180pub fn peekAll(br: *BufferedReader, n: usize) anyerror![]u8 {182pub fn peekAll(br: *BufferedReader, n: usize) anyerror![]u8 {
181 const list = &br.storage.buffer;183 const storage = &br.storage;
182 assert(n <= list.capacity);184 assert(n <= storage.buffer.len);
183 try br.fill(n);185 try br.fill(n);
184 return list.items[br.seek..];186 return storage.buffer[br.seek..storage.end];
185}187}
186188
187/// Skips the next `n` bytes from the stream, advancing the seek position. This189/// Skips the next `n` bytes from the stream, advancing the seek position. This
...@@ -194,7 +196,7 @@ pub fn peekAll(br: *BufferedReader, n: usize) anyerror![]u8 {...@@ -194,7 +196,7 @@ pub fn peekAll(br: *BufferedReader, n: usize) anyerror![]u8 {
194/// * `discard`.196/// * `discard`.
195pub fn toss(br: *BufferedReader, n: usize) void {197pub fn toss(br: *BufferedReader, n: usize) void {
196 br.seek += n;198 br.seek += n;
197 assert(br.seek <= br.storage.buffer.items.len);199 assert(br.seek <= br.storage.end);
198}200}
199201
200/// Equivalent to `peek` + `toss`.202/// Equivalent to `peek` + `toss`.
...@@ -245,22 +247,22 @@ pub fn discard(br: *BufferedReader, n: usize) anyerror!void {...@@ -245,22 +247,22 @@ pub fn discard(br: *BufferedReader, n: usize) anyerror!void {
245/// * `toss`247/// * `toss`
246/// * `discardUntilEnd`248/// * `discardUntilEnd`
247pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {249pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {
248 const list = &br.storage.buffer;250 const storage = &br.storage;
249 var remaining = n;251 var remaining = n;
250 while (remaining > 0) {252 while (remaining > 0) {
251 const proposed_seek = br.seek + remaining;253 const proposed_seek = br.seek + remaining;
252 if (proposed_seek <= list.items.len) {254 if (proposed_seek <= storage.end) {
253 br.seek = proposed_seek;255 br.seek = proposed_seek;
254 return;256 return;
255 }257 }
256 remaining -= (list.items.len - br.seek);258 remaining -= (storage.end - br.seek);
257 list.items.len = 0;259 storage.end = 0;
258 br.seek = 0;260 br.seek = 0;
259 const result = try br.unbuffered_reader.read(&br.storage, .none);261 const result = try br.unbuffered_reader.read(&storage, .none);
260 result.write_err catch unreachable;262 result.write_err catch unreachable;
261 try result.read_err;263 try result.read_err;
262 assert(result.len == list.items.len);264 assert(result.len == storage.end);
263 if (remaining <= list.items.len) continue;265 if (remaining <= storage.end) continue;
264 if (result.end) return n - remaining;266 if (result.end) return n - remaining;
265 }267 }
266}268}
...@@ -268,9 +270,9 @@ pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {...@@ -268,9 +270,9 @@ pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {
268/// Reads the stream until the end, ignoring all the data.270/// Reads the stream until the end, ignoring all the data.
269/// Returns the number of bytes discarded.271/// Returns the number of bytes discarded.
270pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {272pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
271 const list = &br.storage.buffer;273 const storage = &br.storage;
272 var total: usize = list.items.len;274 var total: usize = storage.end;
273 list.items.len = 0;275 storage.end = 0;
274 total += try br.unbuffered_reader.discardUntilEnd();276 total += try br.unbuffered_reader.discardUntilEnd();
275 return total;277 return total;
276}278}
...@@ -286,8 +288,8 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {...@@ -286,8 +288,8 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
286/// See also:288/// See also:
287/// * `peek`289/// * `peek`
288pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {290pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
289 const list = &br.storage.buffer;291 const storage = &br.storage;
290 const in_buffer = list.items;292 const in_buffer = storage.buffer[0..storage.end];
291 const seek = br.seek;293 const seek = br.seek;
292 const proposed_seek = seek + in_buffer.len;294 const proposed_seek = seek + in_buffer.len;
293 if (proposed_seek <= in_buffer.len) {295 if (proposed_seek <= in_buffer.len) {
...@@ -296,21 +298,21 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {...@@ -296,21 +298,21 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
296 return;298 return;
297 }299 }
298 @memcpy(buffer[0..in_buffer.len], in_buffer);300 @memcpy(buffer[0..in_buffer.len], in_buffer);
299 list.items.len = 0;301 storage.end = 0;
300 br.seek = 0;302 br.seek = 0;
301 var i: usize = in_buffer.len;303 var i: usize = in_buffer.len;
302 while (true) {304 while (true) {
303 const status = try br.unbuffered_reader.read(&br.storage, .none);305 const status = try br.unbuffered_reader.read(storage, .none);
304 const next_i = i + list.items.len;306 const next_i = i + storage.end;
305 if (next_i >= buffer.len) {307 if (next_i >= buffer.len) {
306 const remaining = buffer[i..];308 const remaining = buffer[i..];
307 @memcpy(remaining, list.items[0..remaining.len]);309 @memcpy(remaining, storage.buffer[0..remaining.len]);
308 br.seek = remaining.len;310 br.seek = remaining.len;
309 return;311 return;
310 }312 }
311 if (status.end) return error.EndOfStream;313 if (status.end) return error.EndOfStream;
312 @memcpy(buffer[i..next_i], list.items);314 @memcpy(buffer[i..next_i], storage.buffer[0..storage.end]);
313 list.items.len = 0;315 storage.end = 0;
314 i = next_i;316 i = next_i;
315 }317 }
316}318}
...@@ -347,8 +349,8 @@ pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8...@@ -347,8 +349,8 @@ pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
347}349}
348350
349pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {351pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
350 const list = &br.storage.buffer;352 const storage = &br.storage;
351 const buffer = list.items;353 const buffer = storage.buffer[0..storage.end];
352 const seek = br.seek;354 const seek = br.seek;
353 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {355 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
354 @branchHint(.likely);356 @branchHint(.likely);
...@@ -357,15 +359,15 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8...@@ -357,15 +359,15 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
357 const remainder = buffer[seek..];359 const remainder = buffer[seek..];
358 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);360 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
359 var i = remainder.len;361 var i = remainder.len;
360 list.items.len = i;362 storage.end = i;
361 br.seek = 0;363 br.seek = 0;
362 while (i < list.capacity) {364 while (i < storage.buffer.len) {
363 const status = try br.unbuffered_reader.read(&br.storage, .none);365 const status = try br.unbuffered_reader.read(storage, .none);
364 if (std.mem.indexOfScalarPos(u8, list.items, i, delimiter)) |end| {366 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| {
365 return list.items[0 .. end + 1];367 return storage.buffer[0 .. end + 1];
366 }368 }
367 if (status.end) return error.EndOfStream;369 if (status.end) return error.EndOfStream;
368 i = list.items.len;370 i = storage.end;
369 }371 }
370 return error.StreamTooLong;372 return error.StreamTooLong;
371}373}
...@@ -392,8 +394,8 @@ pub fn takeDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8...@@ -392,8 +394,8 @@ pub fn takeDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
392}394}
393395
394pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {396pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
395 const list = &br.storage.buffer;397 const storage = &br.storage;
396 const buffer = list.items;398 const buffer = storage.buffer[0..storage.end];
397 const seek = br.seek;399 const seek = br.seek;
398 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {400 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
399 @branchHint(.likely);401 @branchHint(.likely);
...@@ -402,15 +404,15 @@ pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8...@@ -402,15 +404,15 @@ pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
402 const remainder = buffer[seek..];404 const remainder = buffer[seek..];
403 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);405 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
404 var i = remainder.len;406 var i = remainder.len;
405 list.items.len = i;407 storage.end = i;
406 br.seek = 0;408 br.seek = 0;
407 while (i < list.capacity) {409 while (i < storage.buffer.len) {
408 const status = try br.unbuffered_reader.read(&br.storage, .none);410 const status = try br.unbuffered_reader.read(storage, .none);
409 if (std.mem.indexOfScalarPos(u8, list.items, i, delimiter)) |end| {411 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| {
410 return list.items[0 .. end + 1];412 return storage.buffer[0 .. end + 1];
411 }413 }
412 if (status.end) return list.items;414 if (status.end) return storage.buffer[0..storage.end];
413 i = list.items.len;415 i = storage.end;
414 }416 }
415 return error.StreamTooLong;417 return error.StreamTooLong;
416}418}
...@@ -490,9 +492,9 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo...@@ -490,9 +492,9 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo
490///492///
491/// Asserts buffer capacity is at least `n`.493/// Asserts buffer capacity is at least `n`.
492pub fn fill(br: *BufferedReader, n: usize) anyerror!void {494pub fn fill(br: *BufferedReader, n: usize) anyerror!void {
493 assert(n <= br.storage.buffer.capacity);495 const storage = &br.storage;
494 const list = &br.storage.buffer;496 assert(n <= storage.buffer.len);
495 const buffer = list.items;497 const buffer = storage.buffer[0..storage.end];
496 const seek = br.seek;498 const seek = br.seek;
497 if (seek + n <= buffer.len) {499 if (seek + n <= buffer.len) {
498 @branchHint(.likely);500 @branchHint(.likely);
...@@ -500,18 +502,19 @@ pub fn fill(br: *BufferedReader, n: usize) anyerror!void {...@@ -500,18 +502,19 @@ pub fn fill(br: *BufferedReader, n: usize) anyerror!void {
500 }502 }
501 const remainder = buffer[seek..];503 const remainder = buffer[seek..];
502 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);504 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
503 list.items.len = remainder.len;505 storage.end = remainder.len;
504 br.seek = 0;506 br.seek = 0;
505 while (true) {507 while (true) {
506 const status = try br.unbuffered_reader.read(&br.storage, .none);508 const status = try br.unbuffered_reader.read(storage, .none);
507 if (n <= list.items.len) return;509 if (n <= storage.end) return;
508 if (status.end) return error.EndOfStream;510 if (status.end) return error.EndOfStream;
509 }511 }
510}512}
511513
512/// Reads 1 byte from the stream or returns `error.EndOfStream`.514/// Reads 1 byte from the stream or returns `error.EndOfStream`.
513pub fn takeByte(br: *BufferedReader) anyerror!u8 {515pub fn takeByte(br: *BufferedReader) anyerror!u8 {
514 const buffer = br.storage.buffer.items;516 const storage = &br.storage;
517 const buffer = storage.buffer[0..storage.end];
515 const seek = br.seek;518 const seek = br.seek;
516 if (seek >= buffer.len) {519 if (seek >= buffer.len) {
517 @branchHint(.unlikely);520 @branchHint(.unlikely);
lib/std/io/BufferedWriter.zig+94-98
...@@ -6,10 +6,6 @@ const Writer = std.io.Writer;...@@ -6,10 +6,6 @@ const Writer = std.io.Writer;
6const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
7const testing = std.testing;7const testing = std.testing;
88
9/// User-provided storage that must outlive this `BufferedWriter`.
10///
11/// If this has capacity zero, the writer is unbuffered, and `flush` is a no-op.
12buffer: std.ArrayListUnmanaged(u8),
13/// Underlying stream to send bytes to.9/// Underlying stream to send bytes to.
14///10///
15/// A write will only be sent here if it could not fit into `buffer`, or if it11/// A write will only be sent here if it could not fit into `buffer`, or if it
...@@ -19,10 +15,14 @@ buffer: std.ArrayListUnmanaged(u8),...@@ -19,10 +15,14 @@ buffer: std.ArrayListUnmanaged(u8),
19/// equals number of bytes provided. This property is exploited by15/// equals number of bytes provided. This property is exploited by
20/// `std.io.AllocatingWriter` for example.16/// `std.io.AllocatingWriter` for example.
21unbuffered_writer: Writer,17unbuffered_writer: Writer,
18/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.
19buffer: []u8,
20/// Marks the end of `buffer` - before this are buffered bytes, after this is
21/// undefined.
22end: usize = 0,
22/// Tracks total number of bytes written to this `BufferedWriter`. This value23/// Tracks total number of bytes written to this `BufferedWriter`. This value
23/// only increases. In the case of fixed mode, this value always equals24/// only increases. In the case of fixed mode, this value always equals `end`.
24/// `buffer.items.len`.25count: usize = 0,
25bytes_written: usize = 0,
2626
27/// Number of slices to store on the stack, when trying to send as many byte27/// Number of slices to store on the stack, when trying to send as many byte
28/// vectors through the underlying write calls as possible.28/// vectors through the underlying write calls as possible.
...@@ -46,71 +46,72 @@ const fixed_vtable: Writer.VTable = .{...@@ -46,71 +46,72 @@ const fixed_vtable: Writer.VTable = .{
46 .writeFile = Writer.unimplemented_writeFile,46 .writeFile = Writer.unimplemented_writeFile,
47};47};
4848
49/// Replaces the `BufferedWriter` with a new one that writes to `buffer` and49/// Replaces the `BufferedWriter` with one that writes to `buffer` and returns
50/// returns `error.NoSpaceLeft` when it is full.50/// `error.NoSpaceLeft` when it is full. `end` and `count` will always be
51/// equal.
51pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {52pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
52 bw.* = .{53 bw.* = .{
53 .unbuffered_writer = .{54 .unbuffered_writer = .{
54 .context = bw,55 .context = bw,
55 .vtable = &fixed_vtable,56 .vtable = &fixed_vtable,
56 },57 },
57 .buffer = .initBuffer(buffer),58 .buffer = buffer,
58 };59 };
59}60}
6061
61/// This function is available when using `initFixed`.62/// This function is available when using `initFixed`.
62pub fn getWritten(bw: *const BufferedWriter) []u8 {63pub fn getWritten(bw: *const BufferedWriter) []u8 {
63 assert(bw.unbuffered_writer.vtable == &fixed_vtable);64 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
64 return bw.buffer.items;65 return bw.buffer[0..bw.end];
65}66}
6667
67/// This function is available when using `initFixed`.68/// This function is available when using `initFixed`.
68pub fn reset(bw: *BufferedWriter) void {69pub fn reset(bw: *BufferedWriter) void {
69 assert(bw.unbuffered_writer.vtable == &fixed_vtable);70 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
70 bw.buffer.items.len = 0;71 bw.end = 0;
72 bw.count = 0;
71}73}
7274
73pub fn flush(bw: *BufferedWriter) anyerror!void {75pub fn flush(bw: *BufferedWriter) anyerror!void {
74 const list = &bw.buffer;76 const send_buffer = bw.buffer[0..bw.end];
75 const send_buffer = list.items;
76 var index: usize = 0;77 var index: usize = 0;
77 while (index < send_buffer.len) index += try bw.unbuffered_writer.writev(&.{send_buffer[index..]});78 while (index < send_buffer.len) index += try bw.unbuffered_writer.writev(&.{send_buffer[index..]});
78 list.items.len = 0;79 bw.end = 0;
79}80}
8081
81pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {82pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
82 return bw.buffer.unusedCapacitySlice();83 return bw.buffer[bw.end..];
83}84}
8485
86/// Asserts the provided buffer has total capacity enough for `minimum_length`.
85pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {87pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {
86 const list = &bw.buffer;88 assert(bw.buffer.len >= minimum_length);
87 assert(list.capacity >= minimum_length);89 const cap_slice = bw.buffer[bw.end..];
88 const cap_slice = list.unusedCapacitySlice();
89 if (cap_slice.len >= minimum_length) {90 if (cap_slice.len >= minimum_length) {
90 @branchHint(.likely);91 @branchHint(.likely);
91 return cap_slice;92 return cap_slice;
92 }93 }
93 const buffer = list.items;94 const buffer = bw.buffer[0..bw.end];
94 const n = try bw.unbuffered_writer.write(buffer);95 const n = try bw.unbuffered_writer.write(buffer);
95 if (n == buffer.len) {96 if (n == buffer.len) {
96 @branchHint(.likely);97 @branchHint(.likely);
97 list.items.len = 0;98 bw.end = 0;
98 return list.unusedCapacitySlice();99 return bw.buffer;
99 }100 }
100 if (n > 0) {101 if (n > 0) {
101 const remainder = buffer[n..];102 const remainder = buffer[n..];
102 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);103 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
103 list.items.len = remainder.len;104 bw.end = remainder.len;
104 }105 }
105 return list.unusedCapacitySlice();106 return bw.buffer[bw.end..];
106}107}
107108
108/// After calling `writableSlice`, this function tracks how many bytes were written to it.109/// After calling `writableSlice`, this function tracks how many bytes were written to it.
109pub fn advance(bw: *BufferedWriter, n: usize) void {110pub fn advance(bw: *BufferedWriter, n: usize) void {
110 const list = &bw.buffer;111 const new_end = bw.end + n;
111 list.items.len += n;112 assert(new_end <= bw.buffer.len);
112 assert(list.items.len <= list.capacity);113 bw.end = new_end;
113 bw.bytes_written += n;114 bw.count += n;
114}115}
115116
116/// The `data` parameter is mutable because this function needs to mutate the117/// The `data` parameter is mutable because this function needs to mutate the
...@@ -139,9 +140,8 @@ pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {...@@ -139,9 +140,8 @@ pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
139140
140fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {141fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
141 const bw: *BufferedWriter = @alignCast(@ptrCast(context));142 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
142 const list = &bw.buffer;143 const buffer = bw.buffer;
143 const buffer = list.allocatedSlice();144 const start_end = bw.end;
144 const start_end = list.items.len;
145145
146 var buffers: [max_buffers_len][]const u8 = undefined;146 var buffers: [max_buffers_len][]const u8 = undefined;
147 var end = start_end;147 var end = start_end;
...@@ -153,7 +153,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us...@@ -153,7 +153,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
153 end = new_end;153 end = new_end;
154 continue;154 continue;
155 }155 }
156 if (end == 0) return track(&bw.bytes_written, try bw.unbuffered_writer.writeSplat(data, splat));156 if (end == 0) return track(&bw.count, try bw.unbuffered_writer.writeSplat(data, splat));
157 buffers[0] = buffer[0..end];157 buffers[0] = buffer[0..end];
158 const remaining_data = data[i..];158 const remaining_data = data[i..];
159 const remaining_buffers = buffers[1..];159 const remaining_buffers = buffers[1..];
...@@ -168,22 +168,22 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us...@@ -168,22 +168,22 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
168 @branchHint(.unlikely);168 @branchHint(.unlikely);
169 const remainder = buffer[n..end];169 const remainder = buffer[n..end];
170 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);170 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
171 list.items.len = remainder.len;171 bw.end = remainder.len;
172 return track(&bw.bytes_written, end - start_end);172 return track(&bw.count, end - start_end);
173 }173 }
174 list.items.len = 0;174 bw.end = 0;
175 return track(&bw.bytes_written, n - start_end);175 return track(&bw.count, n - start_end);
176 }176 }
177 const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1);177 const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1);
178 if (n < end) {178 if (n < end) {
179 @branchHint(.unlikely);179 @branchHint(.unlikely);
180 const remainder = buffer[n..end];180 const remainder = buffer[n..end];
181 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);181 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
182 list.items.len = remainder.len;182 bw.end = remainder.len;
183 return track(&bw.bytes_written, end - start_end);183 return track(&bw.count, end - start_end);
184 }184 }
185 list.items.len = 0;185 bw.end = 0;
186 return track(&bw.bytes_written, n - start_end);186 return track(&bw.count, n - start_end);
187 }187 }
188188
189 const pattern = data[data.len - 1];189 const pattern = data[data.len - 1];
...@@ -192,24 +192,24 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us...@@ -192,24 +192,24 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
192 @branchHint(.unlikely);192 @branchHint(.unlikely);
193 // It was added in the loop above; undo it here.193 // It was added in the loop above; undo it here.
194 end -= pattern.len;194 end -= pattern.len;
195 list.items.len = end;195 bw.end = end;
196 return track(&bw.bytes_written, end - start_end);196 return track(&bw.count, end - start_end);
197 }197 }
198198
199 const remaining_splat = splat - 1;199 const remaining_splat = splat - 1;
200200
201 switch (pattern.len) {201 switch (pattern.len) {
202 0 => {202 0 => {
203 list.items.len = end;203 bw.end = end;
204 return track(&bw.bytes_written, end - start_end);204 return track(&bw.count, end - start_end);
205 },205 },
206 1 => {206 1 => {
207 const new_end = end + remaining_splat;207 const new_end = end + remaining_splat;
208 if (new_end <= buffer.len) {208 if (new_end <= buffer.len) {
209 @branchHint(.likely);209 @branchHint(.likely);
210 @memset(buffer[end..new_end], pattern[0]);210 @memset(buffer[end..new_end], pattern[0]);
211 list.items.len = new_end;211 bw.end = new_end;
212 return track(&bw.bytes_written, new_end - start_end);212 return track(&bw.count, new_end - start_end);
213 }213 }
214 buffers[0] = buffer[0..end];214 buffers[0] = buffer[0..end];
215 buffers[1] = pattern;215 buffers[1] = pattern;
...@@ -218,11 +218,11 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us...@@ -218,11 +218,11 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
218 @branchHint(.unlikely);218 @branchHint(.unlikely);
219 const remainder = buffer[n..end];219 const remainder = buffer[n..end];
220 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);220 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
221 list.items.len = remainder.len;221 bw.end = remainder.len;
222 return track(&bw.bytes_written, end - start_end);222 return track(&bw.count, end - start_end);
223 }223 }
224 list.items.len = 0;224 bw.end = 0;
225 return track(&bw.bytes_written, n - start_end);225 return track(&bw.count, n - start_end);
226 },226 },
227 else => {227 else => {
228 const new_end = end + pattern.len * remaining_splat;228 const new_end = end + pattern.len * remaining_splat;
...@@ -231,8 +231,8 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us...@@ -231,8 +231,8 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
231 while (end < new_end) : (end += pattern.len) {231 while (end < new_end) : (end += pattern.len) {
232 @memcpy(buffer[end..][0..pattern.len], pattern);232 @memcpy(buffer[end..][0..pattern.len], pattern);
233 }233 }
234 list.items.len = new_end;234 bw.end = new_end;
235 return track(&bw.bytes_written, new_end - start_end);235 return track(&bw.count, new_end - start_end);
236 }236 }
237 buffers[0] = buffer[0..end];237 buffers[0] = buffer[0..end];
238 buffers[1] = pattern;238 buffers[1] = pattern;
...@@ -241,17 +241,17 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us...@@ -241,17 +241,17 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
241 @branchHint(.unlikely);241 @branchHint(.unlikely);
242 const remainder = buffer[n..end];242 const remainder = buffer[n..end];
243 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);243 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
244 list.items.len = remainder.len;244 bw.end = remainder.len;
245 return track(&bw.bytes_written, end - start_end);245 return track(&bw.count, end - start_end);
246 }246 }
247 list.items.len = 0;247 bw.end = 0;
248 return track(&bw.bytes_written, n - start_end);248 return track(&bw.count, n - start_end);
249 },249 },
250 }250 }
251}251}
252252
253fn track(bytes_written: *usize, n: usize) usize {253fn track(count: *usize, n: usize) usize {
254 bytes_written.* += n;254 count.* += n;
255 return n;255 return n;
256}256}
257257
...@@ -260,31 +260,29 @@ fn track(bytes_written: *usize, n: usize) usize {...@@ -260,31 +260,29 @@ fn track(bytes_written: *usize, n: usize) usize {
260/// available buffer has been filled.260/// available buffer has been filled.
261fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {261fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
262 const bw: *BufferedWriter = @alignCast(@ptrCast(context));262 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
263 const list = &bw.buffer;
264 for (data) |bytes| {263 for (data) |bytes| {
265 const dest = list.unusedCapacitySlice();264 const dest = bw.buffer[bw.end..];
266 if (dest.len == 0) return error.NoSpaceLeft;265 if (dest.len == 0) return error.NoSpaceLeft;
267 const len = @min(bytes.len, dest.len);266 const len = @min(bytes.len, dest.len);
268 @memcpy(dest[0..len], bytes[0..len]);267 @memcpy(dest[0..len], bytes[0..len]);
269 list.items.len += len;268 bw.end += len;
270 bw.bytes_written = list.items.len;269 bw.count = bw.end;
271 }270 }
272 const pattern = data[data.len - 1];271 const pattern = data[data.len - 1];
273 const dest = list.unusedCapacitySlice();272 const dest = bw.buffer[bw.end..];
274 switch (pattern.len) {273 switch (pattern.len) {
275 0 => unreachable,274 0 => unreachable,
276 1 => @memset(dest, pattern[0]),275 1 => @memset(dest, pattern[0]),
277 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),276 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),
278 }277 }
279 list.items.len = list.capacity;278 bw.end = bw.buffer.len;
280 bw.bytes_written = list.items.len;279 bw.count = bw.end;
281 return error.NoSpaceLeft;280 return error.NoSpaceLeft;
282}281}
283282
284pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {283pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
285 const list = &bw.buffer;284 const buffer = bw.buffer;
286 const buffer = list.allocatedSlice();285 const end = bw.end;
287 const end = list.items.len;
288 const new_end = end + bytes.len;286 const new_end = end + bytes.len;
289 if (new_end > buffer.len) {287 if (new_end > buffer.len) {
290 var data: [2][]const u8 = .{ buffer[0..end], bytes };288 var data: [2][]const u8 = .{ buffer[0..end], bytes };
...@@ -293,15 +291,15 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {...@@ -293,15 +291,15 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
293 @branchHint(.unlikely);291 @branchHint(.unlikely);
294 const remainder = buffer[n..end];292 const remainder = buffer[n..end];
295 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);293 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
296 list.items.len = remainder.len;294 bw.end = remainder.len;
297 return 0;295 return 0;
298 }296 }
299 list.items.len = 0;297 bw.end = 0;
300 return track(&bw.bytes_written, n - end);298 return track(&bw.count, n - end);
301 }299 }
302 @memcpy(buffer[end..new_end], bytes);300 @memcpy(buffer[end..new_end], bytes);
303 list.items.len = new_end;301 bw.end = new_end;
304 return track(&bw.bytes_written, bytes.len);302 return track(&bw.count, bytes.len);
305}303}
306304
307/// Calls `write` as many times as necessary such that all of `bytes` are305/// Calls `write` as many times as necessary such that all of `bytes` are
...@@ -316,13 +314,12 @@ pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) an...@@ -316,13 +314,12 @@ pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) an
316}314}
317315
318pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {316pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
319 const list = &bw.buffer;317 const buffer = bw.buffer[0..bw.end];
320 const buffer = list.items;318 if (buffer.len < bw.buffer.len) {
321 if (buffer.len < list.capacity) {
322 @branchHint(.likely);319 @branchHint(.likely);
323 buffer.ptr[buffer.len] = byte;320 buffer.ptr[buffer.len] = byte;
324 list.items.len = buffer.len + 1;321 bw.end = buffer.len + 1;
325 bw.bytes_written += 1;322 bw.count += 1;
326 return;323 return;
327 }324 }
328 var buffers: [2][]const u8 = .{ buffer, &.{byte} };325 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
...@@ -332,23 +329,23 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {...@@ -332,23 +329,23 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
332 @branchHint(.unlikely);329 @branchHint(.unlikely);
333 continue;330 continue;
334 }331 }
335 bw.bytes_written += 1;332 bw.count += 1;
336 if (n >= buffer.len) {333 if (n >= buffer.len) {
337 @branchHint(.likely);334 @branchHint(.likely);
338 if (n > buffer.len) {335 if (n > buffer.len) {
339 @branchHint(.likely);336 @branchHint(.likely);
340 list.items.len = 0;337 bw.end = 0;
341 return;338 return;
342 } else {339 } else {
343 buffer[0] = byte;340 buffer[0] = byte;
344 list.items.len = 1;341 bw.end = 1;
345 return;342 return;
346 }343 }
347 }344 }
348 const remainder = buffer[n..];345 const remainder = buffer[n..];
349 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);346 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
350 buffer[remainder.len] = byte;347 buffer[remainder.len] = byte;
351 list.items.len = remainder.len + 1;348 bw.end = remainder.len + 1;
352 return;349 return;
353 }350 }
354}351}
...@@ -430,13 +427,12 @@ fn passthru_writeFile(...@@ -430,13 +427,12 @@ fn passthru_writeFile(
430 headers_len: usize,427 headers_len: usize,
431) anyerror!usize {428) anyerror!usize {
432 const bw: *BufferedWriter = @alignCast(@ptrCast(context));429 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
433 const list = &bw.buffer;430 const buffer = bw.buffer;
434 const buffer = list.allocatedSlice();
435 if (buffer.len == 0) return track(431 if (buffer.len == 0) return track(
436 &bw.bytes_written,432 &bw.count,
437 try bw.unbuffered_writer.writeFile(file, offset, len, headers_and_trailers, headers_len),433 try bw.unbuffered_writer.writeFile(file, offset, len, headers_and_trailers, headers_len),
438 );434 );
439 const start_end = list.items.len;435 const start_end = bw.end;
440 const headers = headers_and_trailers[0..headers_len];436 const headers = headers_and_trailers[0..headers_len];
441 const trailers = headers_and_trailers[headers_len..];437 const trailers = headers_and_trailers[headers_len..];
442 var buffers: [max_buffers_len][]const u8 = undefined;438 var buffers: [max_buffers_len][]const u8 = undefined;
...@@ -466,11 +462,11 @@ fn passthru_writeFile(...@@ -466,11 +462,11 @@ fn passthru_writeFile(
466 @branchHint(.unlikely);462 @branchHint(.unlikely);
467 const remainder = buffer[n..end];463 const remainder = buffer[n..end];
468 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);464 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
469 list.items.len = remainder.len;465 bw.end = remainder.len;
470 return track(&bw.bytes_written, end - start_end);466 return track(&bw.count, end - start_end);
471 }467 }
472 list.items.len = 0;468 bw.end = 0;
473 return track(&bw.bytes_written, n - start_end);469 return track(&bw.count, n - start_end);
474 }470 }
475 // Have not made it past the headers yet; must call `writev`.471 // Have not made it past the headers yet; must call `writev`.
476 const n = try bw.unbuffered_writer.writev(buffers[0 .. buffers_len + 1]);472 const n = try bw.unbuffered_writer.writev(buffers[0 .. buffers_len + 1]);
...@@ -478,11 +474,11 @@ fn passthru_writeFile(...@@ -478,11 +474,11 @@ fn passthru_writeFile(
478 @branchHint(.unlikely);474 @branchHint(.unlikely);
479 const remainder = buffer[n..end];475 const remainder = buffer[n..end];
480 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);476 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
481 list.items.len = remainder.len;477 bw.end = remainder.len;
482 return track(&bw.bytes_written, end - start_end);478 return track(&bw.count, end - start_end);
483 }479 }
484 list.items.len = 0;480 bw.end = 0;
485 return track(&bw.bytes_written, n - start_end);481 return track(&bw.count, n - start_end);
486 }482 }
487 // All headers written to buffer.483 // All headers written to buffer.
488 buffers[0] = buffer[0..end];484 buffers[0] = buffer[0..end];
...@@ -496,11 +492,11 @@ fn passthru_writeFile(...@@ -496,11 +492,11 @@ fn passthru_writeFile(
496 @branchHint(.unlikely);492 @branchHint(.unlikely);
497 const remainder = buffer[n..end];493 const remainder = buffer[n..end];
498 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);494 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
499 list.items.len = remainder.len;495 bw.end = remainder.len;
500 return track(&bw.bytes_written, end - start_end);496 return track(&bw.count, end - start_end);
501 }497 }
502 list.items.len = 0;498 bw.end = 0;
503 return track(&bw.bytes_written, n - start_end);499 return track(&bw.count, n - start_end);
504}500}
505501
506pub const WriteFileOptions = struct {502pub const WriteFileOptions = struct {
lib/std/io/Writer.zig+1-1
...@@ -112,7 +112,7 @@ pub fn unimplemented_writeFile(...@@ -112,7 +112,7 @@ pub fn unimplemented_writeFile(
112112
113pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {113pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
114 return .{114 return .{
115 .buffer = .initBuffer(buffer),115 .buffer = buffer,
116 .unbuffered_writer = w,116 .unbuffered_writer = w,
117 };117 };
118}118}
lib/std/tar/Writer.zig+4-2
...@@ -420,7 +420,8 @@ test "write files" {...@@ -420,7 +420,8 @@ test "write files" {
420 const root = "root";420 const root = "root";
421421
422 var output: std.io.AllocatingWriter = undefined;422 var output: std.io.AllocatingWriter = undefined;
423 var wrt: Writer = .{ .underlying_writer = output.init(testing.allocator) };423 output.init(testing.allocator);
424 var wrt: Writer = .{ .underlying_writer = &output.buffered_writer };
424 defer output.deinit();425 defer output.deinit();
425 try wrt.setRoot(root);426 try wrt.setRoot(root);
426 for (files) |file|427 for (files) |file|
...@@ -456,7 +457,8 @@ test "write files" {...@@ -456,7 +457,8 @@ test "write files" {
456 // without root457 // without root
457 {458 {
458 var output: std.io.AllocatingWriter = undefined;459 var output: std.io.AllocatingWriter = undefined;
459 var wrt: Writer = .{ .underlying_writer = output.init(testing.allocator) };460 output.init(testing.allocator);
461 var wrt: Writer = .{ .underlying_writer = &output.buffered_writer };
460 defer output.deinit();462 defer output.deinit();
461 for (files) |file| {463 for (files) |file| {
462 var content = std.io.fixedBufferStream(file.content);464 var content = std.io.fixedBufferStream(file.content);
lib/std/zon/parse.zig+14-12
...@@ -638,10 +638,12 @@ const Parser = struct {...@@ -638,10 +638,12 @@ const Parser = struct {
638 const pointer = @typeInfo(T).pointer;638 const pointer = @typeInfo(T).pointer;
639 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);639 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);
640 if (pointer.sentinel() != null) size_hint += 1;640 if (pointer.sentinel() != null) size_hint += 1;
641 const gpa = self.gpa;
641642
642 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, size_hint);643 var aw: std.io.AllocatingWriter = undefined;
643 defer buf.deinit(self.gpa);644 try aw.initCapacity(gpa, size_hint);
644 switch (try ZonGen.parseStrLit(self.ast, ast_node, buf.writer(self.gpa))) {645 defer aw.deinit();
646 switch (try ZonGen.parseStrLit(self.ast, ast_node, &aw.buffered_writer)) {
645 .success => {},647 .success => {},
646 .failure => |err| {648 .failure => |err| {
647 const token = self.ast.nodeMainToken(ast_node);649 const token = self.ast.nodeMainToken(ast_node);
...@@ -660,9 +662,9 @@ const Parser = struct {...@@ -660,9 +662,9 @@ const Parser = struct {
660 }662 }
661663
662 if (pointer.sentinel() != null) {664 if (pointer.sentinel() != null) {
663 return buf.toOwnedSliceSentinel(self.gpa, 0);665 return aw.toOwnedSliceSentinel(gpa, 0);
664 } else {666 } else {
665 return buf.toOwnedSlice(self.gpa);667 return aw.toOwnedSlice(gpa);
666 }668 }
667 }669 }
668670
...@@ -1064,6 +1066,7 @@ const Parser = struct {...@@ -1064,6 +1066,7 @@ const Parser = struct {
1064 name: []const u8,1066 name: []const u8,
1065 ) error{ OutOfMemory, ParseZon } {1067 ) error{ OutOfMemory, ParseZon } {
1066 @branchHint(.cold);1068 @branchHint(.cold);
1069 const gpa = self.gpa;
1067 const token = if (field) |f| b: {1070 const token = if (field) |f| b: {
1068 var buf: [2]Ast.Node.Index = undefined;1071 var buf: [2]Ast.Node.Index = undefined;
1069 const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?;1072 const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?;
...@@ -1081,18 +1084,17 @@ const Parser = struct {...@@ -1081,18 +1084,17 @@ const Parser = struct {
1081 };1084 };
1082 } else b: {1085 } else b: {
1083 const msg = "supported: ";1086 const msg = "supported: ";
1084 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, 64);1087 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(gpa, 64);
1085 defer buf.deinit(self.gpa);1088 defer buf.deinit(gpa);
1086 const writer = buf.writer(self.gpa);1089 try buf.appendSlice(gpa, msg);
1087 try writer.writeAll(msg);
1088 inline for (info.fields, 0..) |field_info, i| {1090 inline for (info.fields, 0..) |field_info, i| {
1089 if (i != 0) try writer.writeAll(", ");1091 if (i != 0) try buf.appendSlice(gpa, ", ");
1090 try writer.print("'{p_}'", .{std.zig.fmtId(field_info.name)});1092 try buf.print(gpa, "'{p_}'", .{std.zig.fmtId(field_info.name)});
1091 }1093 }
1092 break :b .{1094 break :b .{
1093 .token = token,1095 .token = token,
1094 .offset = 0,1096 .offset = 0,
1095 .msg = try buf.toOwnedSlice(self.gpa),1097 .msg = try buf.toOwnedSlice(gpa),
1096 .owned = true,1098 .owned = true,
1097 };1099 };
1098 };1100 };