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 {
856856 var buffer: [std.atomic.cache_line]u8 = undefined;
857857 var bw = std.io.Writer.null.buffered(&buffer);
858858 bw.print(fmt, args) catch unreachable;
859 return bw.bytes_written;
859 return bw.count;
860860}
861861
862862pub const AllocPrintError = error{OutOfMemory};
lib/std/io/AllocatingWriter.zig+20-10
......@@ -27,19 +27,27 @@ const vtable: std.io.Writer.VTable = .{
2727};
2828
2929/// 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 {
3140 aw.* = .{
32 .written = &.{},
41 .written = slice[0..0],
3342 .allocator = allocator,
3443 .buffered_writer = .{
3544 .unbuffered_writer = .{
3645 .context = aw,
3746 .vtable = &vtable,
3847 },
39 .buffer = &.{},
48 .buffer = slice,
4049 },
4150 };
42 return &aw.buffered_writer;
4351}
4452
4553pub fn deinit(aw: *AllocatingWriter) void {
......@@ -118,7 +126,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
118126 aw.written.len = 0;
119127}
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 {
122130 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
123131 const start_len = aw.written.len;
124132 const bw = &aw.buffered_writer;
......@@ -150,9 +158,9 @@ fn appendPatternAssumeCapacity(list: *std.ArrayListUnmanaged(u8), pattern: []con
150158}
151159
152160fn writeFile(
153 context: *anyopaque,
161 context: ?*anyopaque,
154162 file: std.fs.File,
155 offset: u64,
163 offset: std.io.Writer.Offset,
156164 len: std.io.Writer.FileLen,
157165 headers_and_trailers_full: []const []const u8,
158166 headers_len_full: usize,
......@@ -168,13 +176,14 @@ fn writeFile(
168176 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
169177 } else .{ headers_and_trailers_full, headers_len_full };
170178 const trailers = headers_and_trailers[headers_len..];
179 const pos = offset.toInt() orelse @panic("TODO treat file as stream");
171180 if (len == .entire_file) {
172181 var new_capacity: usize = list.capacity + std.atomic.cache_line;
173182 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
174183 try list.ensureTotalCapacity(gpa, new_capacity);
175184 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
176185 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);
178187 if (n == 0) {
179188 new_capacity = list.capacity;
180189 for (trailers) |bytes| new_capacity += bytes.len;
......@@ -190,7 +199,7 @@ fn writeFile(
190199 try list.ensureTotalCapacity(gpa, new_capacity);
191200 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
192201 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);
194203 list.items.len += n;
195204 if (n < dest.len) {
196205 return list.items.len - start_len;
......@@ -201,8 +210,9 @@ fn writeFile(
201210
202211test AllocatingWriter {
203212 var aw: AllocatingWriter = undefined;
204 const bw = aw.init(std.testing.allocator);
213 aw.init(std.testing.allocator);
205214 defer aw.deinit();
215 const bw = &aw.buffered_writer;
206216
207217 const x: i32 = 42;
208218 const y: i32 = 1234;
lib/std/io/BufferedReader.zig+54-51
......@@ -74,7 +74,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
7474 br.* = .{
7575 .seek = 0,
7676 .storage = .{
77 .buffer = .fromOwnedSlice(@constCast(buffer)),
77 .buffer = @constCast(buffer),
7878 .unbuffered_writer = .{
7979 .context = undefined,
8080 .vtable = &eof_writer,
......@@ -88,9 +88,10 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
8888}
8989
9090pub 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);
9293 assert(br.unbuffered_reader.vtable == &eof_reader);
93 return br.storage.buffer.allocatedSlice();
94 return storage.buffer;
9495}
9596
9697/// Although `BufferedReader` can easily satisfy the `Reader` interface, it's
......@@ -108,7 +109,8 @@ pub fn reader(br: *BufferedReader) Reader {
108109
109110fn passthru_read(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) anyerror!Reader.RwResult {
110111 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];
112114 const buffered = buffer[br.seek..];
113115 const limited = buffered[0..limit.min(buffered.len)];
114116 if (limited.len > 0) {
......@@ -135,7 +137,7 @@ pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void {
135137}
136138
137139pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
138 if (seek_by > br.storage.buffer.items.len - br.seek) return error.Unseekable; // TODO
140 if (seek_by > br.storage.end - br.seek) return error.Unseekable; // TODO
139141 br.seek += @abs(seek_by);
140142}
141143
......@@ -178,10 +180,10 @@ pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {
178180/// * `peek`
179181/// * `toss`
180182pub fn peekAll(br: *BufferedReader, n: usize) anyerror![]u8 {
181 const list = &br.storage.buffer;
182 assert(n <= list.capacity);
183 const storage = &br.storage;
184 assert(n <= storage.buffer.len);
183185 try br.fill(n);
184 return list.items[br.seek..];
186 return storage.buffer[br.seek..storage.end];
185187}
186188
187189/// 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 {
194196/// * `discard`.
195197pub fn toss(br: *BufferedReader, n: usize) void {
196198 br.seek += n;
197 assert(br.seek <= br.storage.buffer.items.len);
199 assert(br.seek <= br.storage.end);
198200}
199201
200202/// Equivalent to `peek` + `toss`.
......@@ -245,22 +247,22 @@ pub fn discard(br: *BufferedReader, n: usize) anyerror!void {
245247/// * `toss`
246248/// * `discardUntilEnd`
247249pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {
248 const list = &br.storage.buffer;
250 const storage = &br.storage;
249251 var remaining = n;
250252 while (remaining > 0) {
251253 const proposed_seek = br.seek + remaining;
252 if (proposed_seek <= list.items.len) {
254 if (proposed_seek <= storage.end) {
253255 br.seek = proposed_seek;
254256 return;
255257 }
256 remaining -= (list.items.len - br.seek);
257 list.items.len = 0;
258 remaining -= (storage.end - br.seek);
259 storage.end = 0;
258260 br.seek = 0;
259 const result = try br.unbuffered_reader.read(&br.storage, .none);
261 const result = try br.unbuffered_reader.read(&storage, .none);
260262 result.write_err catch unreachable;
261263 try result.read_err;
262 assert(result.len == list.items.len);
263 if (remaining <= list.items.len) continue;
264 assert(result.len == storage.end);
265 if (remaining <= storage.end) continue;
264266 if (result.end) return n - remaining;
265267 }
266268}
......@@ -268,9 +270,9 @@ pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {
268270/// Reads the stream until the end, ignoring all the data.
269271/// Returns the number of bytes discarded.
270272pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
271 const list = &br.storage.buffer;
272 var total: usize = list.items.len;
273 list.items.len = 0;
273 const storage = &br.storage;
274 var total: usize = storage.end;
275 storage.end = 0;
274276 total += try br.unbuffered_reader.discardUntilEnd();
275277 return total;
276278}
......@@ -286,8 +288,8 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
286288/// See also:
287289/// * `peek`
288290pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
289 const list = &br.storage.buffer;
290 const in_buffer = list.items;
291 const storage = &br.storage;
292 const in_buffer = storage.buffer[0..storage.end];
291293 const seek = br.seek;
292294 const proposed_seek = seek + in_buffer.len;
293295 if (proposed_seek <= in_buffer.len) {
......@@ -296,21 +298,21 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
296298 return;
297299 }
298300 @memcpy(buffer[0..in_buffer.len], in_buffer);
299 list.items.len = 0;
301 storage.end = 0;
300302 br.seek = 0;
301303 var i: usize = in_buffer.len;
302304 while (true) {
303 const status = try br.unbuffered_reader.read(&br.storage, .none);
304 const next_i = i + list.items.len;
305 const status = try br.unbuffered_reader.read(storage, .none);
306 const next_i = i + storage.end;
305307 if (next_i >= buffer.len) {
306308 const remaining = buffer[i..];
307 @memcpy(remaining, list.items[0..remaining.len]);
309 @memcpy(remaining, storage.buffer[0..remaining.len]);
308310 br.seek = remaining.len;
309311 return;
310312 }
311313 if (status.end) return error.EndOfStream;
312 @memcpy(buffer[i..next_i], list.items);
313 list.items.len = 0;
314 @memcpy(buffer[i..next_i], storage.buffer[0..storage.end]);
315 storage.end = 0;
314316 i = next_i;
315317 }
316318}
......@@ -347,8 +349,8 @@ pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
347349}
348350
349351pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
350 const list = &br.storage.buffer;
351 const buffer = list.items;
352 const storage = &br.storage;
353 const buffer = storage.buffer[0..storage.end];
352354 const seek = br.seek;
353355 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
354356 @branchHint(.likely);
......@@ -357,15 +359,15 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
357359 const remainder = buffer[seek..];
358360 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
359361 var i = remainder.len;
360 list.items.len = i;
362 storage.end = i;
361363 br.seek = 0;
362 while (i < list.capacity) {
363 const status = try br.unbuffered_reader.read(&br.storage, .none);
364 if (std.mem.indexOfScalarPos(u8, list.items, i, delimiter)) |end| {
365 return list.items[0 .. end + 1];
364 while (i < storage.buffer.len) {
365 const status = try br.unbuffered_reader.read(storage, .none);
366 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| {
367 return storage.buffer[0 .. end + 1];
366368 }
367369 if (status.end) return error.EndOfStream;
368 i = list.items.len;
370 i = storage.end;
369371 }
370372 return error.StreamTooLong;
371373}
......@@ -392,8 +394,8 @@ pub fn takeDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
392394}
393395
394396pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
395 const list = &br.storage.buffer;
396 const buffer = list.items;
397 const storage = &br.storage;
398 const buffer = storage.buffer[0..storage.end];
397399 const seek = br.seek;
398400 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
399401 @branchHint(.likely);
......@@ -402,15 +404,15 @@ pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
402404 const remainder = buffer[seek..];
403405 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
404406 var i = remainder.len;
405 list.items.len = i;
407 storage.end = i;
406408 br.seek = 0;
407 while (i < list.capacity) {
408 const status = try br.unbuffered_reader.read(&br.storage, .none);
409 if (std.mem.indexOfScalarPos(u8, list.items, i, delimiter)) |end| {
410 return list.items[0 .. end + 1];
409 while (i < storage.buffer.len) {
410 const status = try br.unbuffered_reader.read(storage, .none);
411 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| {
412 return storage.buffer[0 .. end + 1];
411413 }
412 if (status.end) return list.items;
413 i = list.items.len;
414 if (status.end) return storage.buffer[0..storage.end];
415 i = storage.end;
414416 }
415417 return error.StreamTooLong;
416418}
......@@ -490,9 +492,9 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo
490492///
491493/// Asserts buffer capacity is at least `n`.
492494pub fn fill(br: *BufferedReader, n: usize) anyerror!void {
493 assert(n <= br.storage.buffer.capacity);
494 const list = &br.storage.buffer;
495 const buffer = list.items;
495 const storage = &br.storage;
496 assert(n <= storage.buffer.len);
497 const buffer = storage.buffer[0..storage.end];
496498 const seek = br.seek;
497499 if (seek + n <= buffer.len) {
498500 @branchHint(.likely);
......@@ -500,18 +502,19 @@ pub fn fill(br: *BufferedReader, n: usize) anyerror!void {
500502 }
501503 const remainder = buffer[seek..];
502504 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
503 list.items.len = remainder.len;
505 storage.end = remainder.len;
504506 br.seek = 0;
505507 while (true) {
506 const status = try br.unbuffered_reader.read(&br.storage, .none);
507 if (n <= list.items.len) return;
508 const status = try br.unbuffered_reader.read(storage, .none);
509 if (n <= storage.end) return;
508510 if (status.end) return error.EndOfStream;
509511 }
510512}
511513
512514/// Reads 1 byte from the stream or returns `error.EndOfStream`.
513515pub 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];
515518 const seek = br.seek;
516519 if (seek >= buffer.len) {
517520 @branchHint(.unlikely);
lib/std/io/BufferedWriter.zig+94-98
......@@ -6,10 +6,6 @@ const Writer = std.io.Writer;
66const Allocator = std.mem.Allocator;
77const 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),
139/// Underlying stream to send bytes to.
1410///
1511/// 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),
1915/// equals number of bytes provided. This property is exploited by
2016/// `std.io.AllocatingWriter` for example.
2117unbuffered_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,
2223/// Tracks total number of bytes written to this `BufferedWriter`. This value
23/// only increases. In the case of fixed mode, this value always equals
24/// `buffer.items.len`.
25bytes_written: usize = 0,
24/// only increases. In the case of fixed mode, this value always equals `end`.
25count: usize = 0,
2626
2727/// Number of slices to store on the stack, when trying to send as many byte
2828/// vectors through the underlying write calls as possible.
......@@ -46,71 +46,72 @@ const fixed_vtable: Writer.VTable = .{
4646 .writeFile = Writer.unimplemented_writeFile,
4747};
4848
49/// Replaces the `BufferedWriter` with a new one that writes to `buffer` and
50/// returns `error.NoSpaceLeft` when it is full.
49/// Replaces the `BufferedWriter` with one that writes to `buffer` and returns
50/// `error.NoSpaceLeft` when it is full. `end` and `count` will always be
51/// equal.
5152pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
5253 bw.* = .{
5354 .unbuffered_writer = .{
5455 .context = bw,
5556 .vtable = &fixed_vtable,
5657 },
57 .buffer = .initBuffer(buffer),
58 .buffer = buffer,
5859 };
5960}
6061
6162/// This function is available when using `initFixed`.
6263pub fn getWritten(bw: *const BufferedWriter) []u8 {
6364 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
64 return bw.buffer.items;
65 return bw.buffer[0..bw.end];
6566}
6667
6768/// This function is available when using `initFixed`.
6869pub fn reset(bw: *BufferedWriter) void {
6970 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
70 bw.buffer.items.len = 0;
71 bw.end = 0;
72 bw.count = 0;
7173}
7274
7375pub fn flush(bw: *BufferedWriter) anyerror!void {
74 const list = &bw.buffer;
75 const send_buffer = list.items;
76 const send_buffer = bw.buffer[0..bw.end];
7677 var index: usize = 0;
7778 while (index < send_buffer.len) index += try bw.unbuffered_writer.writev(&.{send_buffer[index..]});
78 list.items.len = 0;
79 bw.end = 0;
7980}
8081
8182pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
82 return bw.buffer.unusedCapacitySlice();
83 return bw.buffer[bw.end..];
8384}
8485
86/// Asserts the provided buffer has total capacity enough for `minimum_length`.
8587pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {
86 const list = &bw.buffer;
87 assert(list.capacity >= minimum_length);
88 const cap_slice = list.unusedCapacitySlice();
88 assert(bw.buffer.len >= minimum_length);
89 const cap_slice = bw.buffer[bw.end..];
8990 if (cap_slice.len >= minimum_length) {
9091 @branchHint(.likely);
9192 return cap_slice;
9293 }
93 const buffer = list.items;
94 const buffer = bw.buffer[0..bw.end];
9495 const n = try bw.unbuffered_writer.write(buffer);
9596 if (n == buffer.len) {
9697 @branchHint(.likely);
97 list.items.len = 0;
98 return list.unusedCapacitySlice();
98 bw.end = 0;
99 return bw.buffer;
99100 }
100101 if (n > 0) {
101102 const remainder = buffer[n..];
102103 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
103 list.items.len = remainder.len;
104 bw.end = remainder.len;
104105 }
105 return list.unusedCapacitySlice();
106 return bw.buffer[bw.end..];
106107}
107108
108109/// After calling `writableSlice`, this function tracks how many bytes were written to it.
109110pub fn advance(bw: *BufferedWriter, n: usize) void {
110 const list = &bw.buffer;
111 list.items.len += n;
112 assert(list.items.len <= list.capacity);
113 bw.bytes_written += n;
111 const new_end = bw.end + n;
112 assert(new_end <= bw.buffer.len);
113 bw.end = new_end;
114 bw.count += n;
114115}
115116
116117/// 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 {
139140
140141fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
141142 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
142 const list = &bw.buffer;
143 const buffer = list.allocatedSlice();
144 const start_end = list.items.len;
143 const buffer = bw.buffer;
144 const start_end = bw.end;
145145
146146 var buffers: [max_buffers_len][]const u8 = undefined;
147147 var end = start_end;
......@@ -153,7 +153,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
153153 end = new_end;
154154 continue;
155155 }
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));
157157 buffers[0] = buffer[0..end];
158158 const remaining_data = data[i..];
159159 const remaining_buffers = buffers[1..];
......@@ -168,22 +168,22 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
168168 @branchHint(.unlikely);
169169 const remainder = buffer[n..end];
170170 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
171 list.items.len = remainder.len;
172 return track(&bw.bytes_written, end - start_end);
171 bw.end = remainder.len;
172 return track(&bw.count, end - start_end);
173173 }
174 list.items.len = 0;
175 return track(&bw.bytes_written, n - start_end);
174 bw.end = 0;
175 return track(&bw.count, n - start_end);
176176 }
177177 const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1);
178178 if (n < end) {
179179 @branchHint(.unlikely);
180180 const remainder = buffer[n..end];
181181 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
182 list.items.len = remainder.len;
183 return track(&bw.bytes_written, end - start_end);
182 bw.end = remainder.len;
183 return track(&bw.count, end - start_end);
184184 }
185 list.items.len = 0;
186 return track(&bw.bytes_written, n - start_end);
185 bw.end = 0;
186 return track(&bw.count, n - start_end);
187187 }
188188
189189 const pattern = data[data.len - 1];
......@@ -192,24 +192,24 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
192192 @branchHint(.unlikely);
193193 // It was added in the loop above; undo it here.
194194 end -= pattern.len;
195 list.items.len = end;
196 return track(&bw.bytes_written, end - start_end);
195 bw.end = end;
196 return track(&bw.count, end - start_end);
197197 }
198198
199199 const remaining_splat = splat - 1;
200200
201201 switch (pattern.len) {
202202 0 => {
203 list.items.len = end;
204 return track(&bw.bytes_written, end - start_end);
203 bw.end = end;
204 return track(&bw.count, end - start_end);
205205 },
206206 1 => {
207207 const new_end = end + remaining_splat;
208208 if (new_end <= buffer.len) {
209209 @branchHint(.likely);
210210 @memset(buffer[end..new_end], pattern[0]);
211 list.items.len = new_end;
212 return track(&bw.bytes_written, new_end - start_end);
211 bw.end = new_end;
212 return track(&bw.count, new_end - start_end);
213213 }
214214 buffers[0] = buffer[0..end];
215215 buffers[1] = pattern;
......@@ -218,11 +218,11 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
218218 @branchHint(.unlikely);
219219 const remainder = buffer[n..end];
220220 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
221 list.items.len = remainder.len;
222 return track(&bw.bytes_written, end - start_end);
221 bw.end = remainder.len;
222 return track(&bw.count, end - start_end);
223223 }
224 list.items.len = 0;
225 return track(&bw.bytes_written, n - start_end);
224 bw.end = 0;
225 return track(&bw.count, n - start_end);
226226 },
227227 else => {
228228 const new_end = end + pattern.len * remaining_splat;
......@@ -231,8 +231,8 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
231231 while (end < new_end) : (end += pattern.len) {
232232 @memcpy(buffer[end..][0..pattern.len], pattern);
233233 }
234 list.items.len = new_end;
235 return track(&bw.bytes_written, new_end - start_end);
234 bw.end = new_end;
235 return track(&bw.count, new_end - start_end);
236236 }
237237 buffers[0] = buffer[0..end];
238238 buffers[1] = pattern;
......@@ -241,17 +241,17 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
241241 @branchHint(.unlikely);
242242 const remainder = buffer[n..end];
243243 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
244 list.items.len = remainder.len;
245 return track(&bw.bytes_written, end - start_end);
244 bw.end = remainder.len;
245 return track(&bw.count, end - start_end);
246246 }
247 list.items.len = 0;
248 return track(&bw.bytes_written, n - start_end);
247 bw.end = 0;
248 return track(&bw.count, n - start_end);
249249 },
250250 }
251251}
252252
253fn track(bytes_written: *usize, n: usize) usize {
254 bytes_written.* += n;
253fn track(count: *usize, n: usize) usize {
254 count.* += n;
255255 return n;
256256}
257257
......@@ -260,31 +260,29 @@ fn track(bytes_written: *usize, n: usize) usize {
260260/// available buffer has been filled.
261261fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
262262 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
263 const list = &bw.buffer;
264263 for (data) |bytes| {
265 const dest = list.unusedCapacitySlice();
264 const dest = bw.buffer[bw.end..];
266265 if (dest.len == 0) return error.NoSpaceLeft;
267266 const len = @min(bytes.len, dest.len);
268267 @memcpy(dest[0..len], bytes[0..len]);
269 list.items.len += len;
270 bw.bytes_written = list.items.len;
268 bw.end += len;
269 bw.count = bw.end;
271270 }
272271 const pattern = data[data.len - 1];
273 const dest = list.unusedCapacitySlice();
272 const dest = bw.buffer[bw.end..];
274273 switch (pattern.len) {
275274 0 => unreachable,
276275 1 => @memset(dest, pattern[0]),
277276 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),
278277 }
279 list.items.len = list.capacity;
280 bw.bytes_written = list.items.len;
278 bw.end = bw.buffer.len;
279 bw.count = bw.end;
281280 return error.NoSpaceLeft;
282281}
283282
284283pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
285 const list = &bw.buffer;
286 const buffer = list.allocatedSlice();
287 const end = list.items.len;
284 const buffer = bw.buffer;
285 const end = bw.end;
288286 const new_end = end + bytes.len;
289287 if (new_end > buffer.len) {
290288 var data: [2][]const u8 = .{ buffer[0..end], bytes };
......@@ -293,15 +291,15 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
293291 @branchHint(.unlikely);
294292 const remainder = buffer[n..end];
295293 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
296 list.items.len = remainder.len;
294 bw.end = remainder.len;
297295 return 0;
298296 }
299 list.items.len = 0;
300 return track(&bw.bytes_written, n - end);
297 bw.end = 0;
298 return track(&bw.count, n - end);
301299 }
302300 @memcpy(buffer[end..new_end], bytes);
303 list.items.len = new_end;
304 return track(&bw.bytes_written, bytes.len);
301 bw.end = new_end;
302 return track(&bw.count, bytes.len);
305303}
306304
307305/// 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
316314}
317315
318316pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
319 const list = &bw.buffer;
320 const buffer = list.items;
321 if (buffer.len < list.capacity) {
317 const buffer = bw.buffer[0..bw.end];
318 if (buffer.len < bw.buffer.len) {
322319 @branchHint(.likely);
323320 buffer.ptr[buffer.len] = byte;
324 list.items.len = buffer.len + 1;
325 bw.bytes_written += 1;
321 bw.end = buffer.len + 1;
322 bw.count += 1;
326323 return;
327324 }
328325 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
......@@ -332,23 +329,23 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
332329 @branchHint(.unlikely);
333330 continue;
334331 }
335 bw.bytes_written += 1;
332 bw.count += 1;
336333 if (n >= buffer.len) {
337334 @branchHint(.likely);
338335 if (n > buffer.len) {
339336 @branchHint(.likely);
340 list.items.len = 0;
337 bw.end = 0;
341338 return;
342339 } else {
343340 buffer[0] = byte;
344 list.items.len = 1;
341 bw.end = 1;
345342 return;
346343 }
347344 }
348345 const remainder = buffer[n..];
349346 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
350347 buffer[remainder.len] = byte;
351 list.items.len = remainder.len + 1;
348 bw.end = remainder.len + 1;
352349 return;
353350 }
354351}
......@@ -430,13 +427,12 @@ fn passthru_writeFile(
430427 headers_len: usize,
431428) anyerror!usize {
432429 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
433 const list = &bw.buffer;
434 const buffer = list.allocatedSlice();
430 const buffer = bw.buffer;
435431 if (buffer.len == 0) return track(
436 &bw.bytes_written,
432 &bw.count,
437433 try bw.unbuffered_writer.writeFile(file, offset, len, headers_and_trailers, headers_len),
438434 );
439 const start_end = list.items.len;
435 const start_end = bw.end;
440436 const headers = headers_and_trailers[0..headers_len];
441437 const trailers = headers_and_trailers[headers_len..];
442438 var buffers: [max_buffers_len][]const u8 = undefined;
......@@ -466,11 +462,11 @@ fn passthru_writeFile(
466462 @branchHint(.unlikely);
467463 const remainder = buffer[n..end];
468464 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
469 list.items.len = remainder.len;
470 return track(&bw.bytes_written, end - start_end);
465 bw.end = remainder.len;
466 return track(&bw.count, end - start_end);
471467 }
472 list.items.len = 0;
473 return track(&bw.bytes_written, n - start_end);
468 bw.end = 0;
469 return track(&bw.count, n - start_end);
474470 }
475471 // Have not made it past the headers yet; must call `writev`.
476472 const n = try bw.unbuffered_writer.writev(buffers[0 .. buffers_len + 1]);
......@@ -478,11 +474,11 @@ fn passthru_writeFile(
478474 @branchHint(.unlikely);
479475 const remainder = buffer[n..end];
480476 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
481 list.items.len = remainder.len;
482 return track(&bw.bytes_written, end - start_end);
477 bw.end = remainder.len;
478 return track(&bw.count, end - start_end);
483479 }
484 list.items.len = 0;
485 return track(&bw.bytes_written, n - start_end);
480 bw.end = 0;
481 return track(&bw.count, n - start_end);
486482 }
487483 // All headers written to buffer.
488484 buffers[0] = buffer[0..end];
......@@ -496,11 +492,11 @@ fn passthru_writeFile(
496492 @branchHint(.unlikely);
497493 const remainder = buffer[n..end];
498494 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
499 list.items.len = remainder.len;
500 return track(&bw.bytes_written, end - start_end);
495 bw.end = remainder.len;
496 return track(&bw.count, end - start_end);
501497 }
502 list.items.len = 0;
503 return track(&bw.bytes_written, n - start_end);
498 bw.end = 0;
499 return track(&bw.count, n - start_end);
504500}
505501
506502pub const WriteFileOptions = struct {
lib/std/io/Writer.zig+1-1
......@@ -112,7 +112,7 @@ pub fn unimplemented_writeFile(
112112
113113pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
114114 return .{
115 .buffer = .initBuffer(buffer),
115 .buffer = buffer,
116116 .unbuffered_writer = w,
117117 };
118118}
lib/std/tar/Writer.zig+4-2
......@@ -420,7 +420,8 @@ test "write files" {
420420 const root = "root";
421421
422422 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 };
424425 defer output.deinit();
425426 try wrt.setRoot(root);
426427 for (files) |file|
......@@ -456,7 +457,8 @@ test "write files" {
456457 // without root
457458 {
458459 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 };
460462 defer output.deinit();
461463 for (files) |file| {
462464 var content = std.io.fixedBufferStream(file.content);
lib/std/zon/parse.zig+14-12
......@@ -638,10 +638,12 @@ const Parser = struct {
638638 const pointer = @typeInfo(T).pointer;
639639 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);
640640 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 defer buf.deinit(self.gpa);
644 switch (try ZonGen.parseStrLit(self.ast, ast_node, buf.writer(self.gpa))) {
643 var aw: std.io.AllocatingWriter = undefined;
644 try aw.initCapacity(gpa, size_hint);
645 defer aw.deinit();
646 switch (try ZonGen.parseStrLit(self.ast, ast_node, &aw.buffered_writer)) {
645647 .success => {},
646648 .failure => |err| {
647649 const token = self.ast.nodeMainToken(ast_node);
......@@ -660,9 +662,9 @@ const Parser = struct {
660662 }
661663
662664 if (pointer.sentinel() != null) {
663 return buf.toOwnedSliceSentinel(self.gpa, 0);
665 return aw.toOwnedSliceSentinel(gpa, 0);
664666 } else {
665 return buf.toOwnedSlice(self.gpa);
667 return aw.toOwnedSlice(gpa);
666668 }
667669 }
668670
......@@ -1064,6 +1066,7 @@ const Parser = struct {
10641066 name: []const u8,
10651067 ) error{ OutOfMemory, ParseZon } {
10661068 @branchHint(.cold);
1069 const gpa = self.gpa;
10671070 const token = if (field) |f| b: {
10681071 var buf: [2]Ast.Node.Index = undefined;
10691072 const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?;
......@@ -1081,18 +1084,17 @@ const Parser = struct {
10811084 };
10821085 } else b: {
10831086 const msg = "supported: ";
1084 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, 64);
1085 defer buf.deinit(self.gpa);
1086 const writer = buf.writer(self.gpa);
1087 try writer.writeAll(msg);
1087 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(gpa, 64);
1088 defer buf.deinit(gpa);
1089 try buf.appendSlice(gpa, msg);
10881090 inline for (info.fields, 0..) |field_info, i| {
1089 if (i != 0) try writer.writeAll(", ");
1090 try writer.print("'{p_}'", .{std.zig.fmtId(field_info.name)});
1091 if (i != 0) try buf.appendSlice(gpa, ", ");
1092 try buf.print(gpa, "'{p_}'", .{std.zig.fmtId(field_info.name)});
10911093 }
10921094 break :b .{
10931095 .token = token,
10941096 .offset = 0,
1095 .msg = try buf.toOwnedSlice(self.gpa),
1097 .msg = try buf.toOwnedSlice(gpa),
10961098 .owned = true,
10971099 };
10981100 };