authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-24 23:30:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-25 14:15:33-07:00
log7f1c04423e5cfd628c58824aee543b4e79525c5f
tree353c58790bd4781f591902060038729cd67fc645
parent3687eada6a4c7a2f9444b090f49094c9755cb364

std.Io: add "preserve" variants to Reader/Writer


2 files changed, 96 insertions(+), 23 deletions(-)

lib/std/Io/Reader.zig+26
...@@ -185,6 +185,32 @@ pub fn streamExact64(r: *Reader, w: *Writer, n: u64) StreamError!void {...@@ -185,6 +185,32 @@ pub fn streamExact64(r: *Reader, w: *Writer, n: u64) StreamError!void {
185 while (remaining != 0) remaining -= try r.stream(w, .limited64(remaining));185 while (remaining != 0) remaining -= try r.stream(w, .limited64(remaining));
186}186}
187187
188/// "Pump" exactly `n` bytes from the reader to the writer.
189///
190/// When draining `w`, ensures that at least `preserve_len` bytes remain
191/// buffered.
192///
193/// Asserts `Writer.buffer` capacity exceeds `preserve_len`.
194pub fn streamExactPreserve(r: *Reader, w: *Writer, preserve_len: usize, n: usize) StreamError!void {
195 if (w.end + n <= w.buffer.len) {
196 @branchHint(.likely);
197 return streamExact(r, w, n);
198 }
199 // If `n` is large, we can ignore `preserve_len` up to a point.
200 var remaining = n;
201 while (remaining > preserve_len) {
202 assert(remaining != 0);
203 remaining -= try r.stream(w, .limited(remaining - preserve_len));
204 if (w.end + remaining <= w.buffer.len) return streamExact(r, w, remaining);
205 }
206 // All the next bytes received must be preserved.
207 if (preserve_len < w.end) {
208 @memmove(w.buffer[0..preserve_len], w.buffer[w.end - preserve_len ..][0..preserve_len]);
209 w.end = preserve_len;
210 }
211 return streamExact(r, w, remaining);
212}
213
188/// "Pump" data from the reader to the writer, handling `error.EndOfStream` as214/// "Pump" data from the reader to the writer, handling `error.EndOfStream` as
189/// a success case.215/// a success case.
190///216///
lib/std/Io/Writer.zig+70-23
...@@ -256,10 +256,10 @@ test "fixed buffer flush" {...@@ -256,10 +256,10 @@ test "fixed buffer flush" {
256 try testing.expectEqual(10, buffer[0]);256 try testing.expectEqual(10, buffer[0]);
257}257}
258258
259/// Calls `VTable.drain` but hides the last `preserve_length` bytes from the259/// Calls `VTable.drain` but hides the last `preserve_len` bytes from the
260/// implementation, keeping them buffered.260/// implementation, keeping them buffered.
261pub fn drainPreserve(w: *Writer, preserve_length: usize) Error!void {261pub fn drainPreserve(w: *Writer, preserve_len: usize) Error!void {
262 const temp_end = w.end -| preserve_length;262 const temp_end = w.end -| preserve_len;
263 const preserved = w.buffer[temp_end..w.end];263 const preserved = w.buffer[temp_end..w.end];
264 w.end = temp_end;264 w.end = temp_end;
265 defer w.end += preserved.len;265 defer w.end += preserved.len;
...@@ -310,24 +310,38 @@ pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {...@@ -310,24 +310,38 @@ pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {
310}310}
311311
312/// Asserts the provided buffer has total capacity enough for `minimum_length`312/// Asserts the provided buffer has total capacity enough for `minimum_length`
313/// and `preserve_length` combined.313/// and `preserve_len` combined.
314///314///
315/// Does not `advance` the buffer end position.315/// Does not `advance` the buffer end position.
316///316///
317/// When draining the buffer, ensures that at least `preserve_length` bytes317/// When draining the buffer, ensures that at least `preserve_len` bytes
318/// remain buffered.318/// remain buffered.
319///319///
320/// If `preserve_length` is zero, this is equivalent to `writableSliceGreedy`.320/// If `preserve_len` is zero, this is equivalent to `writableSliceGreedy`.
321pub fn writableSliceGreedyPreserve(w: *Writer, preserve_length: usize, minimum_length: usize) Error![]u8 {321pub fn writableSliceGreedyPreserve(w: *Writer, preserve_len: usize, minimum_length: usize) Error![]u8 {
322 assert(w.buffer.len >= preserve_length + minimum_length);322 assert(w.buffer.len >= preserve_len + minimum_length);
323 while (w.buffer.len - w.end < minimum_length) {323 while (w.buffer.len - w.end < minimum_length) {
324 try drainPreserve(w, preserve_length);324 try drainPreserve(w, preserve_len);
325 } else {325 } else {
326 @branchHint(.likely);326 @branchHint(.likely);
327 return w.buffer[w.end..];327 return w.buffer[w.end..];
328 }328 }
329}329}
330330
331/// Asserts the provided buffer has total capacity enough for `len`.
332///
333/// Advances the buffer end position by `len`.
334///
335/// When draining the buffer, ensures that at least `preserve_len` bytes
336/// remain buffered.
337///
338/// If `preserve_len` is zero, this is equivalent to `writableSlice`.
339pub fn writableSlicePreserve(w: *Writer, preserve_len: usize, len: usize) Error![]u8 {
340 const big_slice = try w.writableSliceGreedyPreserve(preserve_len, len);
341 advance(w, len);
342 return big_slice[0..len];
343}
344
331pub const WritableVectorIterator = struct {345pub const WritableVectorIterator = struct {
332 first: []u8,346 first: []u8,
333 middle: []const []u8 = &.{},347 middle: []const []u8 = &.{},
...@@ -523,16 +537,16 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {...@@ -523,16 +537,16 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {
523 return w.vtable.drain(w, &.{bytes}, 1);537 return w.vtable.drain(w, &.{bytes}, 1);
524}538}
525539
526/// Asserts `buffer` capacity exceeds `preserve_length`.540/// Asserts `buffer` capacity exceeds `preserve_len`.
527pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Error!usize {541pub fn writePreserve(w: *Writer, preserve_len: usize, bytes: []const u8) Error!usize {
528 assert(preserve_length <= w.buffer.len);542 assert(preserve_len <= w.buffer.len);
529 if (w.end + bytes.len <= w.buffer.len) {543 if (w.end + bytes.len <= w.buffer.len) {
530 @branchHint(.likely);544 @branchHint(.likely);
531 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);545 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
532 w.end += bytes.len;546 w.end += bytes.len;
533 return bytes.len;547 return bytes.len;
534 }548 }
535 const temp_end = w.end -| preserve_length;549 const temp_end = w.end -| preserve_len;
536 const preserved = w.buffer[temp_end..w.end];550 const preserved = w.buffer[temp_end..w.end];
537 w.end = temp_end;551 w.end = temp_end;
538 defer w.end += preserved.len;552 defer w.end += preserved.len;
...@@ -552,13 +566,13 @@ pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {...@@ -552,13 +566,13 @@ pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {
552/// Calls `drain` as many times as necessary such that all of `bytes` are566/// Calls `drain` as many times as necessary such that all of `bytes` are
553/// transferred.567/// transferred.
554///568///
555/// When draining the buffer, ensures that at least `preserve_length` bytes569/// When draining the buffer, ensures that at least `preserve_len` bytes
556/// remain buffered.570/// remain buffered.
557///571///
558/// Asserts `buffer` capacity exceeds `preserve_length`.572/// Asserts `buffer` capacity exceeds `preserve_len`.
559pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Error!void {573pub fn writeAllPreserve(w: *Writer, preserve_len: usize, bytes: []const u8) Error!void {
560 var index: usize = 0;574 var index: usize = 0;
561 while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]);575 while (index < bytes.len) index += try w.writePreserve(preserve_len, bytes[index..]);
562}576}
563577
564/// Renders fmt string with args, calling `writer` with slices of bytes.578/// Renders fmt string with args, calling `writer` with slices of bytes.
...@@ -761,11 +775,11 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void {...@@ -761,11 +775,11 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void {
761 }775 }
762}776}
763777
764/// When draining the buffer, ensures that at least `preserve_length` bytes778/// When draining the buffer, ensures that at least `preserve_len` bytes
765/// remain buffered.779/// remain buffered.
766pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!void {780pub fn writeBytePreserve(w: *Writer, preserve_len: usize, byte: u8) Error!void {
767 while (w.buffer.len - w.end == 0) {781 while (w.buffer.len - w.end == 0) {
768 try drainPreserve(w, preserve_length);782 try drainPreserve(w, preserve_len);
769 } else {783 } else {
770 @branchHint(.likely);784 @branchHint(.likely);
771 w.buffer[w.end] = byte;785 w.buffer[w.end] = byte;
...@@ -788,10 +802,42 @@ test splatByteAll {...@@ -788,10 +802,42 @@ test splatByteAll {
788 try testing.expectEqualStrings("7" ** 45, aw.writer.buffered());802 try testing.expectEqualStrings("7" ** 45, aw.writer.buffered());
789}803}
790804
805pub fn splatBytePreserve(w: *Writer, preserve_len: usize, byte: u8, n: usize) Error!void {
806 const new_end = w.end + n;
807 if (new_end <= w.buffer.len) {
808 @memset(w.buffer[w.end..][0..n], byte);
809 w.end = new_end;
810 return;
811 }
812 // If `n` is large, we can ignore `preserve_len` up to a point.
813 var remaining = n;
814 while (remaining > preserve_len) {
815 assert(remaining != 0);
816 remaining -= try splatByte(w, byte, remaining - preserve_len);
817 if (w.end + remaining <= w.buffer.len) {
818 @memset(w.buffer[w.end..][0..remaining], byte);
819 w.end += remaining;
820 return;
821 }
822 }
823 // All the next bytes received must be preserved.
824 if (preserve_len < w.end) {
825 @memmove(w.buffer[0..preserve_len], w.buffer[w.end - preserve_len ..][0..preserve_len]);
826 w.end = preserve_len;
827 }
828 while (remaining > 0) remaining -= try w.splatByte(byte, remaining);
829}
830
791/// Writes the same byte many times, allowing short writes.831/// Writes the same byte many times, allowing short writes.
792///832///
793/// Does maximum of one underlying `VTable.drain`.833/// Does maximum of one underlying `VTable.drain`.
794pub fn splatByte(w: *Writer, byte: u8, n: usize) Error!usize {834pub fn splatByte(w: *Writer, byte: u8, n: usize) Error!usize {
835 if (w.end + n <= w.buffer.len) {
836 @branchHint(.likely);
837 @memset(w.buffer[w.end..][0..n], byte);
838 w.end += n;
839 return n;
840 }
795 return writeSplat(w, &.{&.{byte}}, n);841 return writeSplat(w, &.{&.{byte}}, n);
796}842}
797843
...@@ -801,9 +847,10 @@ pub fn splatBytesAll(w: *Writer, bytes: []const u8, splat: usize) Error!void {...@@ -801,9 +847,10 @@ pub fn splatBytesAll(w: *Writer, bytes: []const u8, splat: usize) Error!void {
801 var remaining_bytes: usize = bytes.len * splat;847 var remaining_bytes: usize = bytes.len * splat;
802 remaining_bytes -= try w.splatBytes(bytes, splat);848 remaining_bytes -= try w.splatBytes(bytes, splat);
803 while (remaining_bytes > 0) {849 while (remaining_bytes > 0) {
804 const leftover = remaining_bytes % bytes.len;850 const leftover_splat = remaining_bytes / bytes.len;
805 const buffers: [2][]const u8 = .{ bytes[bytes.len - leftover ..], bytes };851 const leftover_bytes = remaining_bytes % bytes.len;
806 remaining_bytes -= try w.writeSplat(&buffers, splat);852 const buffers: [2][]const u8 = .{ bytes[bytes.len - leftover_bytes ..], bytes };
853 remaining_bytes -= try w.writeSplat(&buffers, leftover_splat);
807 }854 }
808}855}
809856