authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-07-30 21:10:50-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-08-01 11:12:03+02:00
logf212e3716b7f687d552fdad5f15cd1f736039910
tree8d8751ea43c51c178af7c9df972ae18febc12a5a
parent91c6d8a092498b5d168cc8aaffdf72e0b825e340

Writer.Allocating.drain: avoid overallocating in certain situations

In scenarios where splat=1, `drain` would ensure 2x more unused capacity than necessary for the "pattern" bytes since `bytes.len` and `splat_len` would both be counting the same bytes for the `data[data.len - 1]` element. Now, instead of ensuring `bytes.len + splat_len + 1` unused capacity within the loop, the total amount is calculated upfront and that much unused capacity (+ 1, see 8f4229158be69685b49f4e1ac446cd3677a2e63f) is ensured all at once.

1 files changed, 9 insertions(+), 12 deletions(-)

lib/std/Io/Writer.zig+9-12
......@@ -2742,29 +2742,26 @@ pub const Allocating = struct {
27422742
27432743 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
27442744 const a: *Allocating = @fieldParentPtr("writer", w);
2745 const pattern = data[data.len - 1];
2746 const splat_len = pattern.len * splat;
2747 const start_len = a.writer.end;
27482745 assert(data.len != 0);
2749 for (data) |bytes| {
2750 a.ensureUnusedCapacity(bytes.len + splat_len + 1) catch return error.WriteFailed;
2746 const count = countSplat(data, splat);
2747 a.ensureUnusedCapacity(count + 1) catch return error.WriteFailed;
2748 for (data[0 .. data.len - 1]) |bytes| {
27512749 @memcpy(a.writer.buffer[a.writer.end..][0..bytes.len], bytes);
27522750 a.writer.end += bytes.len;
27532751 }
2754 if (splat == 0) {
2755 a.writer.end -= pattern.len;
2756 } else switch (pattern.len) {
2752 const pattern = data[data.len - 1];
2753 switch (pattern.len) {
27572754 0 => {},
27582755 1 => {
2759 @memset(a.writer.buffer[a.writer.end..][0 .. splat - 1], pattern[0]);
2760 a.writer.end += splat - 1;
2756 @memset(a.writer.buffer[a.writer.end..][0..splat], pattern[0]);
2757 a.writer.end += splat;
27612758 },
2762 else => for (0..splat - 1) |_| {
2759 else => for (0..splat) |_| {
27632760 @memcpy(a.writer.buffer[a.writer.end..][0..pattern.len], pattern);
27642761 a.writer.end += pattern.len;
27652762 },
27662763 }
2767 return a.writer.end - start_len;
2764 return count;
27682765 }
27692766
27702767 fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {