authorgravatar for silver@squirl.devSilver <silver@squirl.dev> 2025-07-13 11:37:46+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 20:37:38+02:00
logd772c062720c96cc4e1c601814f422dfa1efb5a5
tree335314acaad4cfac20931c1d0aabac579df220c4
parentc7bcdb4802c2a492abb40f292a7694f0511e7691

fix splatBytesAll and writeSplatAll


1 files changed, 53 insertions(+), 17 deletions(-)

lib/std/Io/Writer.zig+53-17
...@@ -464,34 +464,54 @@ pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {...@@ -464,34 +464,54 @@ pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {
464464
465/// The `data` parameter is mutable because this function needs to mutate the465/// The `data` parameter is mutable because this function needs to mutate the
466/// fields in order to handle partial writes from `VTable.writeSplat`.466/// fields in order to handle partial writes from `VTable.writeSplat`.
467/// `data` will be restored to its original state before returning.
467pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void {468pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void {
468 var index: usize = 0;469 var index: usize = 0;
469 var truncate: usize = 0;470 var truncate: usize = 0;
470 var remaining_splat = splat;
471 while (index + 1 < data.len) {471 while (index + 1 < data.len) {
472 {472 {
473 const untruncated = data[index];473 const untruncated = data[index];
474 data[index] = untruncated[truncate..];474 data[index] = untruncated[truncate..];
475 defer data[index] = untruncated;475 defer data[index] = untruncated;
476 truncate += try w.writeSplat(data[index..], remaining_splat);476 truncate += try w.writeSplat(data[index..], splat);
477 }477 }
478 while (truncate >= data[index].len) {478 while (truncate >= data[index].len and index + 1 < data.len) {
479 if (index + 1 < data.len) {479 truncate -= data[index].len;
480 truncate -= data[index].len;480 index += 1;
481 index += 1;481 }
482 } else {482 }
483 const last = data[data.len - 1];483
484 remaining_splat -= @divExact(truncate, last.len);484 // Deal with any left over splats
485 while (remaining_splat > 0) {485 if (data.len != 0 and truncate < data[index].len * splat) {
486 const n = try w.writeSplat(data[data.len - 1 ..][0..1], remaining_splat);486 std.debug.assert(index == data.len - 1);
487 remaining_splat -= @divExact(n, last.len);487 var remaining_splat = splat;
488 }488 while (true) {
489 return;489 remaining_splat -= truncate / data[index].len;
490 }490 truncate %= data[index].len;
491 if (remaining_splat == 0) break;
492 truncate += try w.writeSplat(&.{ data[index][truncate..], data[index] }, remaining_splat - 1);
491 }493 }
492 }494 }
493}495}
494496
497test writeSplatAll {
498 var aw: Writer.Allocating = .init(testing.allocator);
499 defer aw.deinit();
500
501 var buffers = [_][]const u8{ "ba", "na" };
502 try aw.writer.writeSplatAll(&buffers, 2);
503 try testing.expectEqualStrings("banana", aw.writer.buffered());
504}
505
506test "writeSplatAll works with a single buffer" {
507 var aw: Writer.Allocating = .init(testing.allocator);
508 defer aw.deinit();
509
510 var message: [1][]const u8 = .{"hello"};
511 try aw.writer.writeSplatAll(&message, 3);
512 try testing.expectEqualStrings("hellohellohello", aw.writer.buffered());
513}
514
495pub fn write(w: *Writer, bytes: []const u8) Error!usize {515pub fn write(w: *Writer, bytes: []const u8) Error!usize {
496 if (w.end + bytes.len <= w.buffer.len) {516 if (w.end + bytes.len <= w.buffer.len) {
497 @branchHint(.likely);517 @branchHint(.likely);
...@@ -763,6 +783,14 @@ pub fn splatByteAll(w: *Writer, byte: u8, n: usize) Error!void {...@@ -763,6 +783,14 @@ pub fn splatByteAll(w: *Writer, byte: u8, n: usize) Error!void {
763 while (remaining > 0) remaining -= try w.splatByte(byte, remaining);783 while (remaining > 0) remaining -= try w.splatByte(byte, remaining);
764}784}
765785
786test splatByteAll {
787 var aw: Writer.Allocating = .init(testing.allocator);
788 defer aw.deinit();
789
790 try aw.writer.splatByteAll('7', 45);
791 try testing.expectEqualStrings("7" ** 45, aw.writer.buffered());
792}
793
766/// Writes the same byte many times, allowing short writes.794/// Writes the same byte many times, allowing short writes.
767///795///
768/// Does maximum of one underlying `VTable.drain`.796/// Does maximum of one underlying `VTable.drain`.
...@@ -778,13 +806,21 @@ pub fn splatBytesAll(w: *Writer, bytes: []const u8, splat: usize) Error!void {...@@ -778,13 +806,21 @@ pub fn splatBytesAll(w: *Writer, bytes: []const u8, splat: usize) Error!void {
778 while (remaining_bytes > 0) {806 while (remaining_bytes > 0) {
779 const leftover = remaining_bytes % bytes.len;807 const leftover = remaining_bytes % bytes.len;
780 const buffers: [2][]const u8 = .{ bytes[bytes.len - leftover ..], bytes };808 const buffers: [2][]const u8 = .{ bytes[bytes.len - leftover ..], bytes };
781 remaining_bytes -= try w.splatBytes(&buffers, splat);809 remaining_bytes -= try w.writeSplat(&buffers, splat);
782 }810 }
783}811}
784812
813test splatBytesAll {
814 var aw: Writer.Allocating = .init(testing.allocator);
815 defer aw.deinit();
816
817 try aw.writer.splatBytesAll("hello", 3);
818 try testing.expectEqualStrings("hellohellohello", aw.writer.buffered());
819}
820
785/// Writes the same slice many times, allowing short writes.821/// Writes the same slice many times, allowing short writes.
786///822///
787/// Does maximum of one underlying `VTable.writeSplat`.823/// Does maximum of one underlying `VTable.drain`.
788pub fn splatBytes(w: *Writer, bytes: []const u8, n: usize) Error!usize {824pub fn splatBytes(w: *Writer, bytes: []const u8, n: usize) Error!usize {
789 return writeSplat(w, &.{bytes}, n);825 return writeSplat(w, &.{bytes}, n);
790}826}