authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-27 19:33:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-07 22:43:51-07:00
log9f27d770a1832cf3017a8b2f7281b6faf0347a51
treeef5207f926478533af2e4f84ff7fdc62e6f61a6f
parentfc2c1883b36a6ba8c7303d12b57147656dc7dd70

std.io: deprecated Reader/Writer; introduce new API


56 files changed, 4271 insertions(+), 473 deletions(-)

lib/compiler/resinator/compile.zig+2-2
...@@ -2949,7 +2949,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype)...@@ -2949,7 +2949,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype)
2949 slurped_header: [size]u8 = [_]u8{0x00} ** size,2949 slurped_header: [size]u8 = [_]u8{0x00} ** size,
29502950
2951 pub const Error = ReaderType.Error;2951 pub const Error = ReaderType.Error;
2952 pub const Reader = std.io.Reader(*@This(), Error, read);2952 pub const Reader = std.io.GenericReader(*@This(), Error, read);
29532953
2954 pub fn read(self: *@This(), buf: []u8) Error!usize {2954 pub fn read(self: *@This(), buf: []u8) Error!usize {
2955 const amt = try self.child_reader.read(buf);2955 const amt = try self.child_reader.read(buf);
...@@ -2983,7 +2983,7 @@ pub fn LimitedWriter(comptime WriterType: type) type {...@@ -2983,7 +2983,7 @@ pub fn LimitedWriter(comptime WriterType: type) type {
2983 bytes_left: u64,2983 bytes_left: u64,
29842984
2985 pub const Error = error{NoSpaceLeft} || WriterType.Error;2985 pub const Error = error{NoSpaceLeft} || WriterType.Error;
2986 pub const Writer = std.io.Writer(*Self, Error, write);2986 pub const Writer = std.io.GenericWriter(*Self, Error, write);
29872987
2988 const Self = @This();2988 const Self = @This();
29892989
lib/compiler/resinator/main.zig+1-1
...@@ -471,7 +471,7 @@ const IoStream = struct {...@@ -471,7 +471,7 @@ const IoStream = struct {
471 allocator: std.mem.Allocator,471 allocator: std.mem.Allocator,
472 };472 };
473 pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError;473 pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError;
474 pub const Writer = std.io.Writer(WriterContext, WriteError, write);474 pub const Writer = std.io.GenericWriter(WriterContext, WriteError, write);
475475
476 pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize {476 pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize {
477 switch (ctx.self.*) {477 switch (ctx.self.*) {
lib/std/array_list.zig+37-18
...@@ -338,11 +338,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -338,11 +338,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
338 @memcpy(self.items[old_len..][0..items.len], items);338 @memcpy(self.items[old_len..][0..items.len], items);
339 }339 }
340340
341 pub const Writer = if (T != u8)341 pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
342 @compileError("The Writer interface is only defined for ArrayList(u8) " ++342 const gpa = self.allocator;
343 "but the given type is ArrayList(" ++ @typeName(T) ++ ")")343 var unmanaged = self.moveToUnmanaged();
344 else344 defer self.* = unmanaged.toManaged(gpa);
345 std.io.Writer(*Self, Allocator.Error, appendWrite);345 try unmanaged.print(gpa, fmt, args);
346 }
347
348 pub const Writer = if (T != u8) void else std.io.GenericWriter(*Self, Allocator.Error, appendWrite);
346349
347 /// Initializes a Writer which will append to the list.350 /// Initializes a Writer which will append to the list.
348 pub fn writer(self: *Self) Writer {351 pub fn writer(self: *Self) Writer {
...@@ -350,14 +353,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -350,14 +353,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
350 }353 }
351354
352 /// Same as `append` except it returns the number of bytes written, which is always the same355 /// Same as `append` except it returns the number of bytes written, which is always the same
353 /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.356 /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API.
354 /// Invalidates element pointers if additional memory is needed.357 /// Invalidates element pointers if additional memory is needed.
355 fn appendWrite(self: *Self, m: []const u8) Allocator.Error!usize {358 fn appendWrite(self: *Self, m: []const u8) Allocator.Error!usize {
356 try self.appendSlice(m);359 try self.appendSlice(m);
357 return m.len;360 return m.len;
358 }361 }
359362
360 pub const FixedWriter = std.io.Writer(*Self, Allocator.Error, appendWriteFixed);363 pub const FixedWriter = std.io.GenericWriter(*Self, Allocator.Error, appendWriteFixed);
361364
362 /// Initializes a Writer which will append to the list but will return365 /// Initializes a Writer which will append to the list but will return
363 /// `error.OutOfMemory` rather than increasing capacity.366 /// `error.OutOfMemory` rather than increasing capacity.
...@@ -365,7 +368,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -365,7 +368,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
365 return .{ .context = self };368 return .{ .context = self };
366 }369 }
367370
368 /// The purpose of this function existing is to match `std.io.Writer` API.371 /// The purpose of this function existing is to match `std.io.GenericWriter` API.
369 fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize {372 fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize {
370 const available_capacity = self.capacity - self.items.len;373 const available_capacity = self.capacity - self.items.len;
371 if (m.len > available_capacity)374 if (m.len > available_capacity)
...@@ -933,40 +936,56 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -933,40 +936,56 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
933 @memcpy(self.items[old_len..][0..items.len], items);936 @memcpy(self.items[old_len..][0..items.len], items);
934 }937 }
935938
939 pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
940 comptime assert(T == u8);
941 try self.ensureUnusedCapacity(gpa, fmt.len);
942 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self);
943 defer self.* = aw.toArrayList();
944 return aw.interface.print(fmt, args) catch |err| switch (err) {
945 error.WriteFailed => return error.OutOfMemory,
946 };
947 }
948
949 pub fn printAssumeCapacity(self: *Self, comptime fmt: []const u8, args: anytype) void {
950 comptime assert(T == u8);
951 var w: std.io.Writer = .fixed(self.unusedCapacitySlice());
952 w.print(fmt, args) catch unreachable;
953 self.items.len += w.end;
954 }
955
956 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
936 pub const WriterContext = struct {957 pub const WriterContext = struct {
937 self: *Self,958 self: *Self,
938 allocator: Allocator,959 allocator: Allocator,
939 };960 };
940961
962 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
941 pub const Writer = if (T != u8)963 pub const Writer = if (T != u8)
942 @compileError("The Writer interface is only defined for ArrayList(u8) " ++964 @compileError("The Writer interface is only defined for ArrayList(u8) " ++
943 "but the given type is ArrayList(" ++ @typeName(T) ++ ")")965 "but the given type is ArrayList(" ++ @typeName(T) ++ ")")
944 else966 else
945 std.io.Writer(WriterContext, Allocator.Error, appendWrite);967 std.io.GenericWriter(WriterContext, Allocator.Error, appendWrite);
946968
947 /// Initializes a Writer which will append to the list.969 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
948 pub fn writer(self: *Self, gpa: Allocator) Writer {970 pub fn writer(self: *Self, gpa: Allocator) Writer {
949 return .{ .context = .{ .self = self, .allocator = gpa } };971 return .{ .context = .{ .self = self, .allocator = gpa } };
950 }972 }
951973
952 /// Same as `append` except it returns the number of bytes written,974 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
953 /// which is always the same as `m.len`. The purpose of this function
954 /// existing is to match `std.io.Writer` API.
955 /// Invalidates element pointers if additional memory is needed.
956 fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize {975 fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize {
957 try context.self.appendSlice(context.allocator, m);976 try context.self.appendSlice(context.allocator, m);
958 return m.len;977 return m.len;
959 }978 }
960979
961 pub const FixedWriter = std.io.Writer(*Self, Allocator.Error, appendWriteFixed);980 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
981 pub const FixedWriter = std.io.GenericWriter(*Self, Allocator.Error, appendWriteFixed);
962982
963 /// Initializes a Writer which will append to the list but will return983 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
964 /// `error.OutOfMemory` rather than increasing capacity.
965 pub fn fixedWriter(self: *Self) FixedWriter {984 pub fn fixedWriter(self: *Self) FixedWriter {
966 return .{ .context = self };985 return .{ .context = self };
967 }986 }
968987
969 /// The purpose of this function existing is to match `std.io.Writer` API.988 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
970 fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize {989 fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize {
971 const available_capacity = self.capacity - self.items.len;990 const available_capacity = self.capacity - self.items.len;
972 if (m.len > available_capacity)991 if (m.len > available_capacity)
lib/std/base64.zig+3-3
...@@ -108,7 +108,7 @@ pub const Base64Encoder = struct {...@@ -108,7 +108,7 @@ pub const Base64Encoder = struct {
108 }108 }
109 }109 }
110110
111 // dest must be compatible with std.io.Writer's writeAll interface111 // dest must be compatible with std.io.GenericWriter's writeAll interface
112 pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void {112 pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void {
113 var chunker = window(u8, source, 3, 3);113 var chunker = window(u8, source, 3, 3);
114 while (chunker.next()) |chunk| {114 while (chunker.next()) |chunk| {
...@@ -118,8 +118,8 @@ pub const Base64Encoder = struct {...@@ -118,8 +118,8 @@ pub const Base64Encoder = struct {
118 }118 }
119 }119 }
120120
121 // destWriter must be compatible with std.io.Writer's writeAll interface121 // destWriter must be compatible with std.io.GenericWriter's writeAll interface
122 // sourceReader must be compatible with std.io.Reader's read interface122 // sourceReader must be compatible with `std.io.GenericReader` read interface
123 pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void {123 pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void {
124 while (true) {124 while (true) {
125 var tempSource: [3]u8 = undefined;125 var tempSource: [3]u8 = undefined;
lib/std/bounded_array.zig+2-2
...@@ -277,7 +277,7 @@ pub fn BoundedArrayAligned(...@@ -277,7 +277,7 @@ pub fn BoundedArrayAligned(
277 @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++277 @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++
278 "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)")278 "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)")
279 else279 else
280 std.io.Writer(*Self, error{Overflow}, appendWrite);280 std.io.GenericWriter(*Self, error{Overflow}, appendWrite);
281281
282 /// Initializes a writer which will write into the array.282 /// Initializes a writer which will write into the array.
283 pub fn writer(self: *Self) Writer {283 pub fn writer(self: *Self) Writer {
...@@ -285,7 +285,7 @@ pub fn BoundedArrayAligned(...@@ -285,7 +285,7 @@ pub fn BoundedArrayAligned(
285 }285 }
286286
287 /// Same as `appendSlice` except it returns the number of bytes written, which is always the same287 /// Same as `appendSlice` except it returns the number of bytes written, which is always the same
288 /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.288 /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API.
289 fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize {289 fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize {
290 try self.appendSlice(m);290 try self.appendSlice(m);
291 return m.len;291 return m.len;
lib/std/compress.zig+2-2
...@@ -16,7 +16,7 @@ pub fn HashedReader(ReaderType: type, HasherType: type) type {...@@ -16,7 +16,7 @@ pub fn HashedReader(ReaderType: type, HasherType: type) type {
16 hasher: HasherType,16 hasher: HasherType,
1717
18 pub const Error = ReaderType.Error;18 pub const Error = ReaderType.Error;
19 pub const Reader = std.io.Reader(*@This(), Error, read);19 pub const Reader = std.io.GenericReader(*@This(), Error, read);
2020
21 pub fn read(self: *@This(), buf: []u8) Error!usize {21 pub fn read(self: *@This(), buf: []u8) Error!usize {
22 const amt = try self.child_reader.read(buf);22 const amt = try self.child_reader.read(buf);
...@@ -43,7 +43,7 @@ pub fn HashedWriter(WriterType: type, HasherType: type) type {...@@ -43,7 +43,7 @@ pub fn HashedWriter(WriterType: type, HasherType: type) type {
43 hasher: HasherType,43 hasher: HasherType,
4444
45 pub const Error = WriterType.Error;45 pub const Error = WriterType.Error;
46 pub const Writer = std.io.Writer(*@This(), Error, write);46 pub const Writer = std.io.GenericWriter(*@This(), Error, write);
4747
48 pub fn write(self: *@This(), buf: []const u8) Error!usize {48 pub fn write(self: *@This(), buf: []const u8) Error!usize {
49 const amt = try self.child_writer.write(buf);49 const amt = try self.child_writer.write(buf);
lib/std/compress/flate/deflate.zig+2-2
...@@ -355,7 +355,7 @@ fn Deflate(comptime container: Container, comptime WriterType: type, comptime Bl...@@ -355,7 +355,7 @@ fn Deflate(comptime container: Container, comptime WriterType: type, comptime Bl
355355
356 // Writer interface356 // Writer interface
357357
358 pub const Writer = io.Writer(*Self, Error, write);358 pub const Writer = io.GenericWriter(*Self, Error, write);
359 pub const Error = BlockWriterType.Error;359 pub const Error = BlockWriterType.Error;
360360
361 /// Write `input` of uncompressed data.361 /// Write `input` of uncompressed data.
...@@ -512,7 +512,7 @@ fn SimpleCompressor(...@@ -512,7 +512,7 @@ fn SimpleCompressor(
512512
513 // Writer interface513 // Writer interface
514514
515 pub const Writer = io.Writer(*Self, Error, write);515 pub const Writer = io.GenericWriter(*Self, Error, write);
516 pub const Error = BlockWriterType.Error;516 pub const Error = BlockWriterType.Error;
517517
518 // Write `input` of uncompressed data.518 // Write `input` of uncompressed data.
lib/std/compress/flate/inflate.zig+1-1
...@@ -341,7 +341,7 @@ pub fn Inflate(comptime container: Container, comptime LookaheadType: type, comp...@@ -341,7 +341,7 @@ pub fn Inflate(comptime container: Container, comptime LookaheadType: type, comp
341341
342 // Reader interface342 // Reader interface
343343
344 pub const Reader = std.io.Reader(*Self, Error, read);344 pub const Reader = std.io.GenericReader(*Self, Error, read);
345345
346 /// Returns the number of bytes read. It may be less than buffer.len.346 /// Returns the number of bytes read. It may be less than buffer.len.
347 /// If the number of bytes read is 0, it means end of stream.347 /// If the number of bytes read is 0, it means end of stream.
lib/std/compress/lzma.zig+1-1
...@@ -30,7 +30,7 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -30,7 +30,7 @@ pub fn Decompress(comptime ReaderType: type) type {
30 Allocator.Error ||30 Allocator.Error ||
31 error{ CorruptInput, EndOfStream, Overflow };31 error{ CorruptInput, EndOfStream, Overflow };
3232
33 pub const Reader = std.io.Reader(*Self, Error, read);33 pub const Reader = std.io.GenericReader(*Self, Error, read);
3434
35 allocator: Allocator,35 allocator: Allocator,
36 in_reader: ReaderType,36 in_reader: ReaderType,
lib/std/compress/xz.zig+1-1
...@@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type {
34 const Self = @This();34 const Self = @This();
3535
36 pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error;36 pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error;
37 pub const Reader = std.io.Reader(*Self, Error, read);37 pub const Reader = std.io.GenericReader(*Self, Error, read);
3838
39 allocator: Allocator,39 allocator: Allocator,
40 block_decoder: block.Decoder(ReaderType),40 block_decoder: block.Decoder(ReaderType),
lib/std/compress/xz/block.zig+1-1
...@@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type {
27 ReaderType.Error ||27 ReaderType.Error ||
28 DecodeError ||28 DecodeError ||
29 Allocator.Error;29 Allocator.Error;
30 pub const Reader = std.io.Reader(*Self, Error, read);30 pub const Reader = std.io.GenericReader(*Self, Error, read);
3131
32 allocator: Allocator,32 allocator: Allocator,
33 inner_reader: ReaderType,33 inner_reader: ReaderType,
lib/std/compress/zstandard.zig+1-1
...@@ -50,7 +50,7 @@ pub fn Decompressor(comptime ReaderType: type) type {...@@ -50,7 +50,7 @@ pub fn Decompressor(comptime ReaderType: type) type {
50 OutOfMemory,50 OutOfMemory,
51 };51 };
5252
53 pub const Reader = std.io.Reader(*Self, Error, read);53 pub const Reader = std.io.GenericReader(*Self, Error, read);
5454
55 pub fn init(source: ReaderType, options: DecompressorOptions) Self {55 pub fn init(source: ReaderType, options: DecompressorOptions) Self {
56 return .{56 return .{
lib/std/compress/zstandard/readers.zig+1-1
...@@ -4,7 +4,7 @@ pub const ReversedByteReader = struct {...@@ -4,7 +4,7 @@ pub const ReversedByteReader = struct {
4 remaining_bytes: usize,4 remaining_bytes: usize,
5 bytes: []const u8,5 bytes: []const u8,
66
7 const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn);7 const Reader = std.io.GenericReader(*ReversedByteReader, error{}, readFn);
88
9 pub fn init(bytes: []const u8) ReversedByteReader {9 pub fn init(bytes: []const u8) ReversedByteReader {
10 return .{10 return .{
lib/std/crypto/aegis.zig+1-1
...@@ -803,7 +803,7 @@ fn AegisMac(comptime T: type) type {...@@ -803,7 +803,7 @@ fn AegisMac(comptime T: type) type {
803 }803 }
804804
805 pub const Error = error{};805 pub const Error = error{};
806 pub const Writer = std.io.Writer(*Mac, Error, write);806 pub const Writer = std.io.GenericWriter(*Mac, Error, write);
807807
808 fn write(self: *Mac, bytes: []const u8) Error!usize {808 fn write(self: *Mac, bytes: []const u8) Error!usize {
809 self.update(bytes);809 self.update(bytes);
lib/std/crypto/blake2.zig+1-1
...@@ -187,7 +187,7 @@ pub fn Blake2s(comptime out_bits: usize) type {...@@ -187,7 +187,7 @@ pub fn Blake2s(comptime out_bits: usize) type {
187 }187 }
188188
189 pub const Error = error{};189 pub const Error = error{};
190 pub const Writer = std.io.Writer(*Self, Error, write);190 pub const Writer = std.io.GenericWriter(*Self, Error, write);
191191
192 fn write(self: *Self, bytes: []const u8) Error!usize {192 fn write(self: *Self, bytes: []const u8) Error!usize {
193 self.update(bytes);193 self.update(bytes);
lib/std/crypto/blake3.zig+1-1
...@@ -476,7 +476,7 @@ pub const Blake3 = struct {...@@ -476,7 +476,7 @@ pub const Blake3 = struct {
476 }476 }
477477
478 pub const Error = error{};478 pub const Error = error{};
479 pub const Writer = std.io.Writer(*Blake3, Error, write);479 pub const Writer = std.io.GenericWriter(*Blake3, Error, write);
480480
481 fn write(self: *Blake3, bytes: []const u8) Error!usize {481 fn write(self: *Blake3, bytes: []const u8) Error!usize {
482 self.update(bytes);482 self.update(bytes);
lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig+1-1
...@@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void {...@@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void {
45 self.data.ptr = begin;45 self.data.ptr = begin;
46}46}
4747
48pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize);48pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize);
49/// Warning: This writer writes backwards. `fn print` will NOT work as expected.49/// Warning: This writer writes backwards. `fn print` will NOT work as expected.
50pub fn writer(self: *ArrayListReverse) Writer {50pub fn writer(self: *ArrayListReverse) Writer {
51 return .{ .context = self };51 return .{ .context = self };
lib/std/crypto/sha1.zig+1-1
...@@ -269,7 +269,7 @@ pub const Sha1 = struct {...@@ -269,7 +269,7 @@ pub const Sha1 = struct {
269 }269 }
270270
271 pub const Error = error{};271 pub const Error = error{};
272 pub const Writer = std.io.Writer(*Self, Error, write);272 pub const Writer = std.io.GenericWriter(*Self, Error, write);
273273
274 fn write(self: *Self, bytes: []const u8) Error!usize {274 fn write(self: *Self, bytes: []const u8) Error!usize {
275 self.update(bytes);275 self.update(bytes);
lib/std/crypto/sha2.zig+1-1
...@@ -376,7 +376,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {...@@ -376,7 +376,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {
376 }376 }
377377
378 pub const Error = error{};378 pub const Error = error{};
379 pub const Writer = std.io.Writer(*Self, Error, write);379 pub const Writer = std.io.GenericWriter(*Self, Error, write);
380380
381 fn write(self: *Self, bytes: []const u8) Error!usize {381 fn write(self: *Self, bytes: []const u8) Error!usize {
382 self.update(bytes);382 self.update(bytes);
lib/std/crypto/sha3.zig+5-5
...@@ -82,7 +82,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim...@@ -82,7 +82,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim
82 }82 }
8383
84 pub const Error = error{};84 pub const Error = error{};
85 pub const Writer = std.io.Writer(*Self, Error, write);85 pub const Writer = std.io.GenericWriter(*Self, Error, write);
8686
87 fn write(self: *Self, bytes: []const u8) Error!usize {87 fn write(self: *Self, bytes: []const u8) Error!usize {
88 self.update(bytes);88 self.update(bytes);
...@@ -193,7 +193,7 @@ fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime...@@ -193,7 +193,7 @@ fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime
193 }193 }
194194
195 pub const Error = error{};195 pub const Error = error{};
196 pub const Writer = std.io.Writer(*Self, Error, write);196 pub const Writer = std.io.GenericWriter(*Self, Error, write);
197197
198 fn write(self: *Self, bytes: []const u8) Error!usize {198 fn write(self: *Self, bytes: []const u8) Error!usize {
199 self.update(bytes);199 self.update(bytes);
...@@ -286,7 +286,7 @@ fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime...@@ -286,7 +286,7 @@ fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime
286 }286 }
287287
288 pub const Error = error{};288 pub const Error = error{};
289 pub const Writer = std.io.Writer(*Self, Error, write);289 pub const Writer = std.io.GenericWriter(*Self, Error, write);
290290
291 fn write(self: *Self, bytes: []const u8) Error!usize {291 fn write(self: *Self, bytes: []const u8) Error!usize {
292 self.update(bytes);292 self.update(bytes);
...@@ -392,7 +392,7 @@ fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime r...@@ -392,7 +392,7 @@ fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime r
392 }392 }
393393
394 pub const Error = error{};394 pub const Error = error{};
395 pub const Writer = std.io.Writer(*Self, Error, write);395 pub const Writer = std.io.GenericWriter(*Self, Error, write);
396396
397 fn write(self: *Self, bytes: []const u8) Error!usize {397 fn write(self: *Self, bytes: []const u8) Error!usize {
398 self.update(bytes);398 self.update(bytes);
...@@ -484,7 +484,7 @@ fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, compt...@@ -484,7 +484,7 @@ fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, compt
484 }484 }
485485
486 pub const Error = error{};486 pub const Error = error{};
487 pub const Writer = std.io.Writer(*Self, Error, write);487 pub const Writer = std.io.GenericWriter(*Self, Error, write);
488488
489 fn write(self: *Self, bytes: []const u8) Error!usize {489 fn write(self: *Self, bytes: []const u8) Error!usize {
490 self.update(bytes);490 self.update(bytes);
lib/std/crypto/siphash.zig+1-1
...@@ -240,7 +240,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize)...@@ -240,7 +240,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize)
240 }240 }
241241
242 pub const Error = error{};242 pub const Error = error{};
243 pub const Writer = std.io.Writer(*Self, Error, write);243 pub const Writer = std.io.GenericWriter(*Self, Error, write);
244244
245 fn write(self: *Self, bytes: []const u8) Error!usize {245 fn write(self: *Self, bytes: []const u8) Error!usize {
246 self.update(bytes);246 self.update(bytes);
lib/std/debug/Pdb.zig+1-1
...@@ -562,7 +562,7 @@ const MsfStream = struct {...@@ -562,7 +562,7 @@ const MsfStream = struct {
562 return block * self.block_size + offset;562 return block * self.block_size + offset;
563 }563 }
564564
565 pub fn reader(self: *MsfStream) std.io.Reader(*MsfStream, Error, read) {565 pub fn reader(self: *MsfStream) std.io.GenericReader(*MsfStream, Error, read) {
566 return .{ .context = self };566 return .{ .context = self };
567 }567 }
568};568};
lib/std/fifo.zig+4-4
...@@ -38,8 +38,8 @@ pub fn LinearFifo(...@@ -38,8 +38,8 @@ pub fn LinearFifo(
38 count: usize,38 count: usize,
3939
40 const Self = @This();40 const Self = @This();
41 pub const Reader = std.io.Reader(*Self, error{}, readFn);41 pub const Reader = std.io.GenericReader(*Self, error{}, readFn);
42 pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);42 pub const Writer = std.io.GenericWriter(*Self, error{OutOfMemory}, appendWrite);
4343
44 // Type of Self argument for slice operations.44 // Type of Self argument for slice operations.
45 // If buffer is inline (Static) then we need to ensure we haven't45 // If buffer is inline (Static) then we need to ensure we haven't
...@@ -231,7 +231,7 @@ pub fn LinearFifo(...@@ -231,7 +231,7 @@ pub fn LinearFifo(
231 }231 }
232232
233 /// Same as `read` except it returns an error union233 /// Same as `read` except it returns an error union
234 /// The purpose of this function existing is to match `std.io.Reader` API.234 /// The purpose of this function existing is to match `std.io.GenericReader` API.
235 fn readFn(self: *Self, dest: []u8) error{}!usize {235 fn readFn(self: *Self, dest: []u8) error{}!usize {
236 return self.read(dest);236 return self.read(dest);
237 }237 }
...@@ -320,7 +320,7 @@ pub fn LinearFifo(...@@ -320,7 +320,7 @@ pub fn LinearFifo(
320 }320 }
321321
322 /// Same as `write` except it returns the number of bytes written, which is always the same322 /// Same as `write` except it returns the number of bytes written, which is always the same
323 /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API.323 /// as `bytes.len`. The purpose of this function existing is to match `std.io.GenericWriter` API.
324 fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {324 fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {
325 try self.write(bytes);325 try self.write(bytes);
326 return bytes.len;326 return bytes.len;
lib/std/fs/File.zig+2-2
...@@ -1581,13 +1581,13 @@ fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix...@@ -1581,13 +1581,13 @@ fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix
1581 }1581 }
1582}1582}
15831583
1584pub const Reader = io.Reader(File, ReadError, read);1584pub const Reader = io.GenericReader(File, ReadError, read);
15851585
1586pub fn reader(file: File) Reader {1586pub fn reader(file: File) Reader {
1587 return .{ .context = file };1587 return .{ .context = file };
1588}1588}
15891589
1590pub const Writer = io.Writer(File, WriteError, write);1590pub const Writer = io.GenericWriter(File, WriteError, write);
15911591
1592pub fn writer(file: File) Writer {1592pub fn writer(file: File) Writer {
1593 return .{ .context = file };1593 return .{ .context = file };
lib/std/http/Client.zig+5-5
...@@ -311,7 +311,7 @@ pub const Connection = struct {...@@ -311,7 +311,7 @@ pub const Connection = struct {
311 EndOfStream,311 EndOfStream,
312 };312 };
313313
314 pub const Reader = std.io.Reader(*Connection, ReadError, read);314 pub const Reader = std.io.GenericReader(*Connection, ReadError, read);
315315
316 pub fn reader(conn: *Connection) Reader {316 pub fn reader(conn: *Connection) Reader {
317 return Reader{ .context = conn };317 return Reader{ .context = conn };
...@@ -374,7 +374,7 @@ pub const Connection = struct {...@@ -374,7 +374,7 @@ pub const Connection = struct {
374 UnexpectedWriteFailure,374 UnexpectedWriteFailure,
375 };375 };
376376
377 pub const Writer = std.io.Writer(*Connection, WriteError, write);377 pub const Writer = std.io.GenericWriter(*Connection, WriteError, write);
378378
379 pub fn writer(conn: *Connection) Writer {379 pub fn writer(conn: *Connection) Writer {
380 return Writer{ .context = conn };380 return Writer{ .context = conn };
...@@ -934,7 +934,7 @@ pub const Request = struct {...@@ -934,7 +934,7 @@ pub const Request = struct {
934934
935 const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;935 const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;
936936
937 const TransferReader = std.io.Reader(*Request, TransferReadError, transferRead);937 const TransferReader = std.io.GenericReader(*Request, TransferReadError, transferRead);
938938
939 fn transferReader(req: *Request) TransferReader {939 fn transferReader(req: *Request) TransferReader {
940 return .{ .context = req };940 return .{ .context = req };
...@@ -1094,7 +1094,7 @@ pub const Request = struct {...@@ -1094,7 +1094,7 @@ pub const Request = struct {
1094 pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError ||1094 pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError ||
1095 error{ DecompressionFailure, InvalidTrailers };1095 error{ DecompressionFailure, InvalidTrailers };
10961096
1097 pub const Reader = std.io.Reader(*Request, ReadError, read);1097 pub const Reader = std.io.GenericReader(*Request, ReadError, read);
10981098
1099 pub fn reader(req: *Request) Reader {1099 pub fn reader(req: *Request) Reader {
1100 return .{ .context = req };1100 return .{ .context = req };
...@@ -1134,7 +1134,7 @@ pub const Request = struct {...@@ -1134,7 +1134,7 @@ pub const Request = struct {
11341134
1135 pub const WriteError = Connection.WriteError || error{ NotWriteable, MessageTooLong };1135 pub const WriteError = Connection.WriteError || error{ NotWriteable, MessageTooLong };
11361136
1137 pub const Writer = std.io.Writer(*Request, WriteError, write);1137 pub const Writer = std.io.GenericWriter(*Request, WriteError, write);
11381138
1139 pub fn writer(req: *Request) Writer {1139 pub fn writer(req: *Request) Writer {
1140 return .{ .context = req };1140 return .{ .context = req };
lib/std/http/protocol.zig+2-2
...@@ -344,7 +344,7 @@ const MockBufferedConnection = struct {...@@ -344,7 +344,7 @@ const MockBufferedConnection = struct {
344 }344 }
345345
346 pub const ReadError = std.io.FixedBufferStream([]const u8).ReadError || error{EndOfStream};346 pub const ReadError = std.io.FixedBufferStream([]const u8).ReadError || error{EndOfStream};
347 pub const Reader = std.io.Reader(*MockBufferedConnection, ReadError, read);347 pub const Reader = std.io.GenericReader(*MockBufferedConnection, ReadError, read);
348348
349 pub fn reader(conn: *MockBufferedConnection) Reader {349 pub fn reader(conn: *MockBufferedConnection) Reader {
350 return Reader{ .context = conn };350 return Reader{ .context = conn };
...@@ -359,7 +359,7 @@ const MockBufferedConnection = struct {...@@ -359,7 +359,7 @@ const MockBufferedConnection = struct {
359 }359 }
360360
361 pub const WriteError = std.io.FixedBufferStream([]const u8).WriteError;361 pub const WriteError = std.io.FixedBufferStream([]const u8).WriteError;
362 pub const Writer = std.io.Writer(*MockBufferedConnection, WriteError, write);362 pub const Writer = std.io.GenericWriter(*MockBufferedConnection, WriteError, write);
363363
364 pub fn writer(conn: *MockBufferedConnection) Writer {364 pub fn writer(conn: *MockBufferedConnection) Writer {
365 return Writer{ .context = conn };365 return Writer{ .context = conn };
lib/std/io.zig+71-11
...@@ -14,6 +14,69 @@ const File = std.fs.File;...@@ -14,6 +14,69 @@ const File = std.fs.File;
14const Allocator = std.mem.Allocator;14const Allocator = std.mem.Allocator;
15const Alignment = std.mem.Alignment;15const Alignment = std.mem.Alignment;
1616
17pub const Limit = enum(usize) {
18 nothing = 0,
19 unlimited = std.math.maxInt(usize),
20 _,
21
22 /// `std.math.maxInt(usize)` is interpreted to mean `.unlimited`.
23 pub fn limited(n: usize) Limit {
24 return @enumFromInt(n);
25 }
26
27 pub fn countVec(data: []const []const u8) Limit {
28 var total: usize = 0;
29 for (data) |d| total += d.len;
30 return .limited(total);
31 }
32
33 pub fn min(a: Limit, b: Limit) Limit {
34 return @enumFromInt(@min(@intFromEnum(a), @intFromEnum(b)));
35 }
36
37 pub fn minInt(l: Limit, n: usize) usize {
38 return @min(n, @intFromEnum(l));
39 }
40
41 pub fn slice(l: Limit, s: []u8) []u8 {
42 return s[0..l.minInt(s.len)];
43 }
44
45 pub fn sliceConst(l: Limit, s: []const u8) []const u8 {
46 return s[0..l.minInt(s.len)];
47 }
48
49 pub fn toInt(l: Limit) ?usize {
50 return switch (l) {
51 else => @intFromEnum(l),
52 .unlimited => null,
53 };
54 }
55
56 /// Reduces a slice to account for the limit, leaving room for one extra
57 /// byte above the limit, allowing for the use case of differentiating
58 /// between end-of-stream and reaching the limit.
59 pub fn slice1(l: Limit, non_empty_buffer: []u8) []u8 {
60 assert(non_empty_buffer.len >= 1);
61 return non_empty_buffer[0..@min(@intFromEnum(l) +| 1, non_empty_buffer.len)];
62 }
63
64 pub fn nonzero(l: Limit) bool {
65 return @intFromEnum(l) > 0;
66 }
67
68 /// Return a new limit reduced by `amount` or return `null` indicating
69 /// limit would be exceeded.
70 pub fn subtract(l: Limit, amount: usize) ?Limit {
71 if (l == .unlimited) return .unlimited;
72 if (amount > @intFromEnum(l)) return null;
73 return @enumFromInt(@intFromEnum(l) - amount);
74 }
75};
76
77pub const Reader = @import("io/Reader.zig");
78pub const Writer = @import("io/Writer.zig");
79
17fn getStdOutHandle() posix.fd_t {80fn getStdOutHandle() posix.fd_t {
18 if (is_windows) {81 if (is_windows) {
19 return windows.peb().ProcessParameters.hStdOutput;82 return windows.peb().ProcessParameters.hStdOutput;
...@@ -62,6 +125,7 @@ pub fn getStdIn() File {...@@ -62,6 +125,7 @@ pub fn getStdIn() File {
62 return .{ .handle = getStdInHandle() };125 return .{ .handle = getStdInHandle() };
63}126}
64127
128/// Deprecated in favor of `Reader`.
65pub fn GenericReader(129pub fn GenericReader(
66 comptime Context: type,130 comptime Context: type,
67 comptime ReadError: type,131 comptime ReadError: type,
...@@ -289,6 +353,7 @@ pub fn GenericReader(...@@ -289,6 +353,7 @@ pub fn GenericReader(
289 };353 };
290}354}
291355
356/// Deprecated in favor of `Writer`.
292pub fn GenericWriter(357pub fn GenericWriter(
293 comptime Context: type,358 comptime Context: type,
294 comptime WriteError: type,359 comptime WriteError: type,
...@@ -350,15 +415,10 @@ pub fn GenericWriter(...@@ -350,15 +415,10 @@ pub fn GenericWriter(
350 };415 };
351}416}
352417
353/// Deprecated; consider switching to `AnyReader` or use `GenericReader`418/// Deprecated in favor of `Reader`.
354/// to use previous API.419pub const AnyReader = @import("io/DeprecatedReader.zig");
355pub const Reader = GenericReader;420/// Deprecated in favor of `Writer`.
356/// Deprecated; consider switching to `AnyWriter` or use `GenericWriter`421pub const AnyWriter = @import("io/DeprecatedWriter.zig");
357/// to use previous API.
358pub const Writer = GenericWriter;
359
360pub const AnyReader = @import("io/Reader.zig");
361pub const AnyWriter = @import("io/Writer.zig");
362422
363pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;423pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
364424
...@@ -819,8 +879,8 @@ pub fn PollFiles(comptime StreamEnum: type) type {...@@ -819,8 +879,8 @@ pub fn PollFiles(comptime StreamEnum: type) type {
819}879}
820880
821test {881test {
822 _ = AnyReader;882 _ = Reader;
823 _ = AnyWriter;883 _ = Writer;
824 _ = @import("io/bit_reader.zig");884 _ = @import("io/bit_reader.zig");
825 _ = @import("io/bit_writer.zig");885 _ = @import("io/bit_writer.zig");
826 _ = @import("io/buffered_atomic_file.zig");886 _ = @import("io/buffered_atomic_file.zig");
lib/std/io/DeprecatedReader.zig created+386
...@@ -0,0 +1,386 @@
1context: *const anyopaque,
2readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize,
3
4pub const Error = anyerror;
5
6/// Returns the number of bytes read. It may be less than buffer.len.
7/// If the number of bytes read is 0, it means end of stream.
8/// End of stream is not an error condition.
9pub fn read(self: Self, buffer: []u8) anyerror!usize {
10 return self.readFn(self.context, buffer);
11}
12
13/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
14/// means the stream reached the end. Reaching the end of a stream is not an error
15/// condition.
16pub fn readAll(self: Self, buffer: []u8) anyerror!usize {
17 return readAtLeast(self, buffer, buffer.len);
18}
19
20/// Returns the number of bytes read, calling the underlying read
21/// function the minimal number of times until the buffer has at least
22/// `len` bytes filled. If the number read is less than `len` it means
23/// the stream reached the end. Reaching the end of the stream is not
24/// an error condition.
25pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize {
26 assert(len <= buffer.len);
27 var index: usize = 0;
28 while (index < len) {
29 const amt = try self.read(buffer[index..]);
30 if (amt == 0) break;
31 index += amt;
32 }
33 return index;
34}
35
36/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
37pub fn readNoEof(self: Self, buf: []u8) anyerror!void {
38 const amt_read = try self.readAll(buf);
39 if (amt_read < buf.len) return error.EndOfStream;
40}
41
42/// Appends to the `std.ArrayList` contents by reading from the stream
43/// until end of stream is found.
44/// If the number of bytes appended would exceed `max_append_size`,
45/// `error.StreamTooLong` is returned
46/// and the `std.ArrayList` has exactly `max_append_size` bytes appended.
47pub fn readAllArrayList(
48 self: Self,
49 array_list: *std.ArrayList(u8),
50 max_append_size: usize,
51) anyerror!void {
52 return self.readAllArrayListAligned(null, array_list, max_append_size);
53}
54
55pub fn readAllArrayListAligned(
56 self: Self,
57 comptime alignment: ?Alignment,
58 array_list: *std.ArrayListAligned(u8, alignment),
59 max_append_size: usize,
60) anyerror!void {
61 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
62 const original_len = array_list.items.len;
63 var start_index: usize = original_len;
64 while (true) {
65 array_list.expandToCapacity();
66 const dest_slice = array_list.items[start_index..];
67 const bytes_read = try self.readAll(dest_slice);
68 start_index += bytes_read;
69
70 if (start_index - original_len > max_append_size) {
71 array_list.shrinkAndFree(original_len + max_append_size);
72 return error.StreamTooLong;
73 }
74
75 if (bytes_read != dest_slice.len) {
76 array_list.shrinkAndFree(start_index);
77 return;
78 }
79
80 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.
81 try array_list.ensureTotalCapacity(start_index + 1);
82 }
83}
84
85/// Allocates enough memory to hold all the contents of the stream. If the allocated
86/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
87/// Caller owns returned memory.
88/// If this function returns an error, the contents from the stream read so far are lost.
89pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 {
90 var array_list = std.ArrayList(u8).init(allocator);
91 defer array_list.deinit();
92 try self.readAllArrayList(&array_list, max_size);
93 return try array_list.toOwnedSlice();
94}
95
96/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
97/// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.
98/// Does not include the delimiter in the result.
99/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
100/// `std.ArrayList` is populated with `max_size` bytes from the stream.
101pub fn readUntilDelimiterArrayList(
102 self: Self,
103 array_list: *std.ArrayList(u8),
104 delimiter: u8,
105 max_size: usize,
106) anyerror!void {
107 array_list.shrinkRetainingCapacity(0);
108 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
109}
110
111/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
112/// Allocates enough memory to read until `delimiter`. If the allocated
113/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
114/// Caller owns returned memory.
115/// If this function returns an error, the contents from the stream read so far are lost.
116pub fn readUntilDelimiterAlloc(
117 self: Self,
118 allocator: mem.Allocator,
119 delimiter: u8,
120 max_size: usize,
121) anyerror![]u8 {
122 var array_list = std.ArrayList(u8).init(allocator);
123 defer array_list.deinit();
124 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
125 return try array_list.toOwnedSlice();
126}
127
128/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
129/// Reads from the stream until specified byte is found. If the buffer is not
130/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
131/// If end-of-stream is found, `error.EndOfStream` is returned.
132/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
133/// delimiter byte is written to the output buffer but is not included
134/// in the returned slice.
135pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 {
136 var fbs = std.io.fixedBufferStream(buf);
137 try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len);
138 const output = fbs.getWritten();
139 buf[output.len] = delimiter; // emulating old behaviour
140 return output;
141}
142
143/// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead.
144/// Allocates enough memory to read until `delimiter` or end-of-stream.
145/// If the allocated memory would be greater than `max_size`, returns
146/// `error.StreamTooLong`. If end-of-stream is found, returns the rest
147/// of the stream. If this function is called again after that, returns
148/// null.
149/// Caller owns returned memory.
150/// If this function returns an error, the contents from the stream read so far are lost.
151pub fn readUntilDelimiterOrEofAlloc(
152 self: Self,
153 allocator: mem.Allocator,
154 delimiter: u8,
155 max_size: usize,
156) anyerror!?[]u8 {
157 var array_list = std.ArrayList(u8).init(allocator);
158 defer array_list.deinit();
159 self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) {
160 error.EndOfStream => if (array_list.items.len == 0) {
161 return null;
162 },
163 else => |e| return e,
164 };
165 return try array_list.toOwnedSlice();
166}
167
168/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
169/// Reads from the stream until specified byte is found. If the buffer is not
170/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
171/// If end-of-stream is found, returns the rest of the stream. If this
172/// function is called again after that, returns null.
173/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
174/// delimiter byte is written to the output buffer but is not included
175/// in the returned slice.
176pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) anyerror!?[]u8 {
177 var fbs = std.io.fixedBufferStream(buf);
178 self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) {
179 error.EndOfStream => if (fbs.getWritten().len == 0) {
180 return null;
181 },
182
183 else => |e| return e,
184 };
185 const output = fbs.getWritten();
186 buf[output.len] = delimiter; // emulating old behaviour
187 return output;
188}
189
190/// Appends to the `writer` contents by reading from the stream until `delimiter` is found.
191/// Does not write the delimiter itself.
192/// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
193/// returns `error.StreamTooLong` and finishes appending.
194/// If `optional_max_size` is null, appending is unbounded.
195pub fn streamUntilDelimiter(
196 self: Self,
197 writer: anytype,
198 delimiter: u8,
199 optional_max_size: ?usize,
200) anyerror!void {
201 if (optional_max_size) |max_size| {
202 for (0..max_size) |_| {
203 const byte: u8 = try self.readByte();
204 if (byte == delimiter) return;
205 try writer.writeByte(byte);
206 }
207 return error.StreamTooLong;
208 } else {
209 while (true) {
210 const byte: u8 = try self.readByte();
211 if (byte == delimiter) return;
212 try writer.writeByte(byte);
213 }
214 // Can not throw `error.StreamTooLong` since there are no boundary.
215 }
216}
217
218/// Reads from the stream until specified byte is found, discarding all data,
219/// including the delimiter.
220/// If end-of-stream is found, this function succeeds.
221pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void {
222 while (true) {
223 const byte = self.readByte() catch |err| switch (err) {
224 error.EndOfStream => return,
225 else => |e| return e,
226 };
227 if (byte == delimiter) return;
228 }
229}
230
231/// Reads 1 byte from the stream or returns `error.EndOfStream`.
232pub fn readByte(self: Self) anyerror!u8 {
233 var result: [1]u8 = undefined;
234 const amt_read = try self.read(result[0..]);
235 if (amt_read < 1) return error.EndOfStream;
236 return result[0];
237}
238
239/// Same as `readByte` except the returned byte is signed.
240pub fn readByteSigned(self: Self) anyerror!i8 {
241 return @as(i8, @bitCast(try self.readByte()));
242}
243
244/// Reads exactly `num_bytes` bytes and returns as an array.
245/// `num_bytes` must be comptime-known
246pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 {
247 var bytes: [num_bytes]u8 = undefined;
248 try self.readNoEof(&bytes);
249 return bytes;
250}
251
252/// Reads bytes until `bounded.len` is equal to `num_bytes`,
253/// or the stream ends.
254///
255/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`
256pub fn readIntoBoundedBytes(
257 self: Self,
258 comptime num_bytes: usize,
259 bounded: *std.BoundedArray(u8, num_bytes),
260) anyerror!void {
261 while (bounded.len < num_bytes) {
262 // get at most the number of bytes free in the bounded array
263 const bytes_read = try self.read(bounded.unusedCapacitySlice());
264 if (bytes_read == 0) return;
265
266 // bytes_read will never be larger than @TypeOf(bounded.len)
267 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
268 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
269 }
270}
271
272/// Reads at most `num_bytes` and returns as a bounded array.
273pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) {
274 var result = std.BoundedArray(u8, num_bytes){};
275 try self.readIntoBoundedBytes(num_bytes, &result);
276 return result;
277}
278
279pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
280 const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8));
281 return mem.readInt(T, &bytes, endian);
282}
283
284pub fn readVarInt(
285 self: Self,
286 comptime ReturnType: type,
287 endian: std.builtin.Endian,
288 size: usize,
289) anyerror!ReturnType {
290 assert(size <= @sizeOf(ReturnType));
291 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
292 const bytes = bytes_buf[0..size];
293 try self.readNoEof(bytes);
294 return mem.readVarInt(ReturnType, bytes, endian);
295}
296
297/// Optional parameters for `skipBytes`
298pub const SkipBytesOptions = struct {
299 buf_size: usize = 512,
300};
301
302// `num_bytes` is a `u64` to match `off_t`
303/// Reads `num_bytes` bytes from the stream and discards them
304pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void {
305 var buf: [options.buf_size]u8 = undefined;
306 var remaining = num_bytes;
307
308 while (remaining > 0) {
309 const amt = @min(remaining, options.buf_size);
310 try self.readNoEof(buf[0..amt]);
311 remaining -= amt;
312 }
313}
314
315/// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice
316pub fn isBytes(self: Self, slice: []const u8) anyerror!bool {
317 var i: usize = 0;
318 var matches = true;
319 while (i < slice.len) : (i += 1) {
320 if (slice[i] != try self.readByte()) {
321 matches = false;
322 }
323 }
324 return matches;
325}
326
327pub fn readStruct(self: Self, comptime T: type) anyerror!T {
328 // Only extern and packed structs have defined in-memory layout.
329 comptime assert(@typeInfo(T).@"struct".layout != .auto);
330 var res: [1]T = undefined;
331 try self.readNoEof(mem.sliceAsBytes(res[0..]));
332 return res[0];
333}
334
335pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
336 var res = try self.readStruct(T);
337 if (native_endian != endian) {
338 mem.byteSwapAllFields(T, &res);
339 }
340 return res;
341}
342
343/// Reads an integer with the same size as the given enum's tag type. If the integer matches
344/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.
345/// TODO optimization taking advantage of most fields being in order
346pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {
347 const E = error{
348 /// An integer was read, but it did not match any of the tags in the supplied enum.
349 InvalidValue,
350 };
351 const type_info = @typeInfo(Enum).@"enum";
352 const tag = try self.readInt(type_info.tag_type, endian);
353
354 inline for (std.meta.fields(Enum)) |field| {
355 if (tag == field.value) {
356 return @field(Enum, field.name);
357 }
358 }
359
360 return E.InvalidValue;
361}
362
363/// Reads the stream until the end, ignoring all the data.
364/// Returns the number of bytes discarded.
365pub fn discard(self: Self) anyerror!u64 {
366 var trash: [4096]u8 = undefined;
367 var index: u64 = 0;
368 while (true) {
369 const n = try self.read(&trash);
370 if (n == 0) return index;
371 index += n;
372 }
373}
374
375const std = @import("../std.zig");
376const Self = @This();
377const math = std.math;
378const assert = std.debug.assert;
379const mem = std.mem;
380const testing = std.testing;
381const native_endian = @import("builtin").target.cpu.arch.endian();
382const Alignment = std.mem.Alignment;
383
384test {
385 _ = @import("Reader/test.zig");
386}
lib/std/io/DeprecatedWriter.zig created+83
...@@ -0,0 +1,83 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const native_endian = @import("builtin").target.cpu.arch.endian();
5
6context: *const anyopaque,
7writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize,
8
9const Self = @This();
10pub const Error = anyerror;
11
12pub fn write(self: Self, bytes: []const u8) anyerror!usize {
13 return self.writeFn(self.context, bytes);
14}
15
16pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {
17 var index: usize = 0;
18 while (index != bytes.len) {
19 index += try self.write(bytes[index..]);
20 }
21}
22
23pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void {
24 return std.fmt.format(self, format, args);
25}
26
27pub fn writeByte(self: Self, byte: u8) anyerror!void {
28 const array = [1]u8{byte};
29 return self.writeAll(&array);
30}
31
32pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void {
33 var bytes: [256]u8 = undefined;
34 @memset(bytes[0..], byte);
35
36 var remaining: usize = n;
37 while (remaining > 0) {
38 const to_write = @min(remaining, bytes.len);
39 try self.writeAll(bytes[0..to_write]);
40 remaining -= to_write;
41 }
42}
43
44pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) anyerror!void {
45 var i: usize = 0;
46 while (i < n) : (i += 1) {
47 try self.writeAll(bytes);
48 }
49}
50
51pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {
52 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;
53 mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
54 return self.writeAll(&bytes);
55}
56
57pub fn writeStruct(self: Self, value: anytype) anyerror!void {
58 // Only extern and packed structs have defined in-memory layout.
59 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
60 return self.writeAll(mem.asBytes(&value));
61}
62
63pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void {
64 // TODO: make sure this value is not a reference type
65 if (native_endian == endian) {
66 return self.writeStruct(value);
67 } else {
68 var copy = value;
69 mem.byteSwapAllFields(@TypeOf(value), &copy);
70 return self.writeStruct(copy);
71 }
72}
73
74pub fn writeFile(self: Self, file: std.fs.File) anyerror!void {
75 // TODO: figure out how to adjust std lib abstractions so that this ends up
76 // doing sendfile or maybe even copy_file_range under the right conditions.
77 var buf: [4000]u8 = undefined;
78 while (true) {
79 const n = try file.readAll(&buf);
80 try self.writeAll(buf[0..n]);
81 if (n < buf.len) return;
82 }
83}
lib/std/io/Reader.zig+1403-318
...@@ -1,386 +1,1471 @@...@@ -1,386 +1,1471 @@
1context: *const anyopaque,1const Reader = @This();
2readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize,
32
4pub const Error = anyerror;3const builtin = @import("builtin");
4const native_endian = builtin.target.cpu.arch.endian();
55
6/// Returns the number of bytes read. It may be less than buffer.len.6const std = @import("../std.zig");
7/// If the number of bytes read is 0, it means end of stream.7const Writer = std.io.Writer;
8/// End of stream is not an error condition.8const assert = std.debug.assert;
9pub fn read(self: Self, buffer: []u8) anyerror!usize {9const testing = std.testing;
10 return self.readFn(self.context, buffer);10const Allocator = std.mem.Allocator;
11const ArrayList = std.ArrayListUnmanaged;
12const Limit = std.io.Limit;
13
14pub const Limited = @import("Reader/Limited.zig");
15
16vtable: *const VTable,
17buffer: []u8,
18/// Number of bytes which have been consumed from `buffer`.
19seek: usize,
20/// In `buffer` before this are buffered bytes, after this is `undefined`.
21end: usize,
22
23pub const VTable = struct {
24 /// Writes bytes from the internally tracked logical position to `w`.
25 ///
26 /// Returns the number of bytes written, which will be at minimum `0` and
27 /// at most `limit`. The number returned, including zero, does not indicate
28 /// end of stream. `limit` is guaranteed to be at least as large as the
29 /// buffer capacity of `w`.
30 ///
31 /// The reader's internal logical seek position moves forward in accordance
32 /// with the number of bytes returned from this function.
33 ///
34 /// Implementations are encouraged to utilize mandatory minimum buffer
35 /// sizes combined with short reads (returning a value less than `limit`)
36 /// in order to minimize complexity.
37 ///
38 /// This function is always called when `buffer` is empty.
39 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
40
41 /// Consumes bytes from the internally tracked stream position without
42 /// providing access to them.
43 ///
44 /// Returns the number of bytes discarded, which will be at minimum `0` and
45 /// at most `limit`. The number of bytes returned, including zero, does not
46 /// indicate end of stream.
47 ///
48 /// The reader's internal logical seek position moves forward in accordance
49 /// with the number of bytes returned from this function.
50 ///
51 /// Implementations are encouraged to utilize mandatory minimum buffer
52 /// sizes combined with short reads (returning a value less than `limit`)
53 /// in order to minimize complexity.
54 ///
55 /// The default implementation is is based on calling `stream`, borrowing
56 /// `buffer` to construct a temporary `Writer` and ignoring the written
57 /// data.
58 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
59};
60
61pub const StreamError = error{
62 /// See the `Reader` implementation for detailed diagnostics.
63 ReadFailed,
64 /// See the `Writer` implementation for detailed diagnostics.
65 WriteFailed,
66 /// End of stream indicated from the `Reader`. This error cannot originate
67 /// from the `Writer`.
68 EndOfStream,
69};
70
71pub const Error = error{
72 /// See the `Reader` implementation for detailed diagnostics.
73 ReadFailed,
74 EndOfStream,
75};
76
77pub const StreamRemainingError = error{
78 /// See the `Reader` implementation for detailed diagnostics.
79 ReadFailed,
80 /// See the `Writer` implementation for detailed diagnostics.
81 WriteFailed,
82};
83
84pub const ShortError = error{
85 /// See the `Reader` implementation for detailed diagnostics.
86 ReadFailed,
87};
88
89pub const failing: Reader = .{
90 .vtable = &.{
91 .read = failingStream,
92 .discard = failingDiscard,
93 },
94 .buffer = &.{},
95 .seek = 0,
96 .end = 0,
97};
98
99/// This is generally safe to `@constCast` because it has an empty buffer, so
100/// there is not really a way to accidentally attempt mutation of these fields.
101const ending_state: Reader = .fixed(&.{});
102pub const ending: *Reader = @constCast(&ending_state);
103
104pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {
105 return Limited.init(r, limit, buffer);
11}106}
12107
13/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it108/// Constructs a `Reader` such that it will read from `buffer` and then end.
14/// means the stream reached the end. Reaching the end of a stream is not an error109pub fn fixed(buffer: []const u8) Reader {
15/// condition.110 return .{
16pub fn readAll(self: Self, buffer: []u8) anyerror!usize {111 .vtable = &.{
17 return readAtLeast(self, buffer, buffer.len);112 .stream = endingStream,
113 .discard = endingDiscard,
114 },
115 // This cast is safe because all potential writes to it will instead
116 // return `error.EndOfStream`.
117 .buffer = @constCast(buffer),
118 .end = buffer.len,
119 .seek = 0,
120 };
18}121}
19122
20/// Returns the number of bytes read, calling the underlying read123pub fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
21/// function the minimal number of times until the buffer has at least124 const buffer = limit.slice(r.buffer[r.seek..r.end]);
22/// `len` bytes filled. If the number read is less than `len` it means125 if (buffer.len > 0) {
23/// the stream reached the end. Reaching the end of the stream is not126 @branchHint(.likely);
24/// an error condition.127 const n = try w.write(buffer);
25pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize {128 r.seek += n;
26 assert(len <= buffer.len);129 return n;
27 var index: usize = 0;130 }
28 while (index < len) {131 const before = w.count;
29 const amt = try self.read(buffer[index..]);132 const n = try r.vtable.stream(r, w, limit);
30 if (amt == 0) break;133 assert(n <= @intFromEnum(limit));
31 index += amt;134 assert(w.count == before + n);
135 return n;
136}
137
138pub fn discard(r: *Reader, limit: Limit) Error!usize {
139 const buffered_len = r.end - r.seek;
140 const remaining: Limit = if (limit.toInt()) |n| l: {
141 if (buffered_len >= n) {
142 r.seek += n;
143 return n;
144 }
145 break :l .limited(n - buffered_len);
146 } else .unlimited;
147 r.seek = 0;
148 r.end = 0;
149 const n = try r.vtable.discard(r, remaining);
150 assert(n <= @intFromEnum(remaining));
151 return buffered_len + n;
152}
153
154pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
155 assert(r.seek == 0);
156 assert(r.end == 0);
157 var w: Writer = .discarding(r.buffer);
158 const n = r.stream(&w, limit) catch |err| switch (err) {
159 error.WriteFailed => unreachable,
160 error.ReadFailed => return error.ReadFailed,
161 error.EndOfStream => return error.EndOfStream,
162 };
163 if (n > @intFromEnum(limit)) {
164 const over_amt = n - @intFromEnum(limit);
165 r.seek = w.end - over_amt;
166 r.end = w.end;
167 assert(r.end <= w.buffer.len); // limit may be exceeded only by an amount within buffer capacity.
168 return @intFromEnum(limit);
32 }169 }
33 return index;170 return n;
34}171}
35172
36/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.173/// "Pump" exactly `n` bytes from the reader to the writer.
37pub fn readNoEof(self: Self, buf: []u8) anyerror!void {174pub fn streamExact(r: *Reader, w: *Writer, n: usize) StreamError!void {
38 const amt_read = try self.readAll(buf);175 var remaining = n;
39 if (amt_read < buf.len) return error.EndOfStream;176 while (remaining != 0) remaining -= try r.stream(w, .limited(remaining));
40}177}
41178
42/// Appends to the `std.ArrayList` contents by reading from the stream179/// "Pump" data from the reader to the writer, handling `error.EndOfStream` as
43/// until end of stream is found.180/// a success case.
44/// If the number of bytes appended would exceed `max_append_size`,181///
45/// `error.StreamTooLong` is returned182/// Returns total number of bytes written to `w`.
46/// and the `std.ArrayList` has exactly `max_append_size` bytes appended.183pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize {
47pub fn readAllArrayList(184 var offset: usize = 0;
48 self: Self,
49 array_list: *std.ArrayList(u8),
50 max_append_size: usize,
51) anyerror!void {
52 return self.readAllArrayListAligned(null, array_list, max_append_size);
53}
54
55pub fn readAllArrayListAligned(
56 self: Self,
57 comptime alignment: ?Alignment,
58 array_list: *std.ArrayListAligned(u8, alignment),
59 max_append_size: usize,
60) anyerror!void {
61 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
62 const original_len = array_list.items.len;
63 var start_index: usize = original_len;
64 while (true) {185 while (true) {
65 array_list.expandToCapacity();186 offset += r.stream(w, .unlimited) catch |err| switch (err) {
66 const dest_slice = array_list.items[start_index..];187 error.EndOfStream => return offset,
67 const bytes_read = try self.readAll(dest_slice);188 else => |e| return e,
68 start_index += bytes_read;189 };
190 }
191}
192
193/// Consumes the stream until the end, ignoring all the data, returning the
194/// number of bytes discarded.
195pub fn discardRemaining(r: *Reader) ShortError!usize {
196 var offset: usize = r.end;
197 r.seek = 0;
198 r.end = 0;
199 while (true) {
200 offset += r.vtable.discard(r, .unlimited) catch |err| switch (err) {
201 error.EndOfStream => return offset,
202 else => |e| return e,
203 };
204 }
205}
206
207pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLong};
208
209/// Transfers all bytes from the current position to the end of the stream, up
210/// to `limit`, returning them as a caller-owned allocated slice.
211///
212/// If `limit` would be exceeded, `error.StreamTooLong` is returned instead. In
213/// such case, the next byte that would be read will be the first one to exceed
214/// `limit`, and all preceeding bytes have been discarded.
215///
216/// Asserts `buffer` has nonzero capacity.
217///
218/// See also:
219/// * `appendRemaining`
220pub fn allocRemaining(r: *Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {
221 var buffer: ArrayList(u8) = .empty;
222 defer buffer.deinit(gpa);
223 try appendRemaining(r, gpa, null, &buffer, limit);
224 return buffer.toOwnedSlice(gpa);
225}
69226
70 if (start_index - original_len > max_append_size) {227/// Transfers all bytes from the current position to the end of the stream, up
71 array_list.shrinkAndFree(original_len + max_append_size);228/// to `limit`, appending them to `list`.
229///
230/// If `limit` would be exceeded, `error.StreamTooLong` is returned instead. In
231/// such case, the next byte that would be read will be the first one to exceed
232/// `limit`, and all preceeding bytes have been appended to `list`.
233///
234/// Asserts `buffer` has nonzero capacity.
235///
236/// See also:
237/// * `allocRemaining`
238pub fn appendRemaining(
239 r: *Reader,
240 gpa: Allocator,
241 comptime alignment: ?std.mem.Alignment,
242 list: *std.ArrayListAlignedUnmanaged(u8, alignment),
243 limit: Limit,
244) LimitedAllocError!void {
245 const buffer = r.buffer;
246 const buffer_contents = buffer[r.seek..r.end];
247 const copy_len = limit.minInt(buffer_contents.len);
248 try list.ensureUnusedCapacity(gpa, copy_len);
249 @memcpy(list.unusedCapacitySlice()[0..copy_len], buffer[0..copy_len]);
250 list.items.len += copy_len;
251 r.seek += copy_len;
252 if (copy_len == buffer_contents.len) {
253 r.seek = 0;
254 r.end = 0;
255 }
256 var remaining = limit.subtract(copy_len).?;
257 while (true) {
258 try list.ensureUnusedCapacity(gpa, 1);
259 const dest = remaining.slice(list.unusedCapacitySlice());
260 const additional_buffer: []u8 = if (@intFromEnum(remaining) == dest.len) buffer else &.{};
261 const n = readVec(r, &.{ dest, additional_buffer }) catch |err| switch (err) {
262 error.EndOfStream => break,
263 error.ReadFailed => return error.ReadFailed,
264 };
265 if (n >= dest.len) {
266 r.end = n - dest.len;
267 list.items.len += dest.len;
268 if (n == dest.len) return;
72 return error.StreamTooLong;269 return error.StreamTooLong;
73 }270 }
271 list.items.len += n;
272 remaining = remaining.subtract(n).?;
273 }
274}
275
276/// Writes bytes from the internally tracked stream position to `data`.
277///
278/// Returns the number of bytes written, which will be at minimum `0` and
279/// at most the sum of each data slice length. The number of bytes read,
280/// including zero, does not indicate end of stream.
281///
282/// The reader's internal logical seek position moves forward in accordance
283/// with the number of bytes returned from this function.
284pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
285 return readVecLimit(r, data, .unlimited);
286}
74287
75 if (bytes_read != dest_slice.len) {288/// Equivalent to `readVec` but reads at most `limit` bytes.
76 array_list.shrinkAndFree(start_index);289///
77 return;290/// This ultimately will lower to a call to `stream`, but it must ensure
291/// that the buffer used has at least as much capacity, in case that function
292/// depends on a minimum buffer capacity. It also ensures that if the `stream`
293/// implementation calls `Writer.writableVector`, it will get this data slice
294/// along with the buffer at the end.
295pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
296 comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));
297 var remaining = @intFromEnum(limit);
298 for (data, 0..) |buf, i| {
299 const buffer_contents = r.buffer[r.seek..r.end];
300 const copy_len = @min(buffer_contents.len, buf.len, remaining);
301 @memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);
302 r.seek += copy_len;
303 remaining -= copy_len;
304 if (remaining == 0) break;
305 if (buf.len - copy_len == 0) continue;
306
307 // All of `buffer` has been copied to `data`. We now set up a structure
308 // that enables the `Writer.writableVector` API, while also ensuring
309 // API that directly operates on the `Writable.buffer` has its minimum
310 // buffer capacity requirements met.
311 r.seek = 0;
312 r.end = 0;
313 const first = buf[copy_len..];
314 const middle = data[i + 1 ..];
315 var wrapper: Writer.VectorWrapper = .{
316 .it = .{
317 .first = first,
318 .middle = middle,
319 .last = r.buffer,
320 },
321 .writer = .{
322 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
323 .vtable = &Writer.VectorWrapper.vtable,
324 },
325 };
326 var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {
327 error.WriteFailed => {
328 if (wrapper.writer.buffer.ptr == first.ptr) {
329 remaining -= wrapper.writer.end;
330 } else {
331 r.end = wrapper.writer.end;
332 }
333 break;
334 },
335 else => |e| return e,
336 };
337 if (wrapper.writer.buffer.ptr != first.ptr) {
338 r.end = n;
339 break;
340 }
341 if (n < first.len) {
342 remaining -= n;
343 break;
78 }344 }
345 remaining -= first.len;
346 n -= first.len;
347 for (middle) |mid| {
348 if (n < mid.len) {
349 remaining -= n;
350 break;
351 }
352 remaining -= mid.len;
353 n -= mid.len;
354 }
355 r.end = n;
356 break;
357 }
358 return @intFromEnum(limit) - remaining;
359}
360
361pub fn buffered(r: *Reader) []u8 {
362 return r.buffer[r.seek..r.end];
363}
364
365pub fn bufferedLen(r: *const Reader) usize {
366 return r.end - r.seek;
367}
368
369pub fn hashed(r: *Reader, hasher: anytype) Hashed(@TypeOf(hasher)) {
370 return .{ .in = r, .hasher = hasher };
371}
79372
80 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.373pub fn readVecAll(r: *Reader, data: [][]u8) Error!void {
81 try array_list.ensureTotalCapacity(start_index + 1);374 var index: usize = 0;
375 var truncate: usize = 0;
376 while (index < data.len) {
377 {
378 const untruncated = data[index];
379 data[index] = untruncated[truncate..];
380 defer data[index] = untruncated;
381 truncate += try r.readVec(data[index..]);
382 }
383 while (index < data.len and truncate >= data[index].len) {
384 truncate -= data[index].len;
385 index += 1;
386 }
82 }387 }
83}388}
84389
85/// Allocates enough memory to hold all the contents of the stream. If the allocated390/// Returns the next `len` bytes from the stream, filling the buffer as
86/// memory would be greater than `max_size`, returns `error.StreamTooLong`.391/// necessary.
87/// Caller owns returned memory.392///
88/// If this function returns an error, the contents from the stream read so far are lost.393/// Invalidates previously returned values from `peek`.
89pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 {394///
90 var array_list = std.ArrayList(u8).init(allocator);395/// Asserts that the `Reader` was initialized with a buffer capacity at
91 defer array_list.deinit();396/// least as big as `len`.
92 try self.readAllArrayList(&array_list, max_size);397///
93 return try array_list.toOwnedSlice();398/// If there are fewer than `len` bytes left in the stream, `error.EndOfStream`
94}399/// is returned instead.
95400///
96/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.401/// See also:
97/// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.402/// * `peek`
98/// Does not include the delimiter in the result.403/// * `toss`
99/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the404pub fn peek(r: *Reader, n: usize) Error![]u8 {
100/// `std.ArrayList` is populated with `max_size` bytes from the stream.405 try r.fill(n);
101pub fn readUntilDelimiterArrayList(406 return r.buffer[r.seek..][0..n];
102 self: Self,407}
103 array_list: *std.ArrayList(u8),408
104 delimiter: u8,409/// Returns all the next buffered bytes, after filling the buffer to ensure it
105 max_size: usize,410/// contains at least `n` bytes.
106) anyerror!void {411///
107 array_list.shrinkRetainingCapacity(0);412/// Invalidates previously returned values from `peek` and `peekGreedy`.
108 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);413///
109}414/// Asserts that the `Reader` was initialized with a buffer capacity at
110415/// least as big as `n`.
111/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.416///
112/// Allocates enough memory to read until `delimiter`. If the allocated417/// If there are fewer than `n` bytes left in the stream, `error.EndOfStream`
113/// memory would be greater than `max_size`, returns `error.StreamTooLong`.418/// is returned instead.
114/// Caller owns returned memory.419///
115/// If this function returns an error, the contents from the stream read so far are lost.420/// See also:
116pub fn readUntilDelimiterAlloc(421/// * `peek`
117 self: Self,422/// * `toss`
118 allocator: mem.Allocator,423pub fn peekGreedy(r: *Reader, n: usize) Error![]u8 {
119 delimiter: u8,424 try r.fill(n);
120 max_size: usize,425 return r.buffer[r.seek..r.end];
121) anyerror![]u8 {426}
122 var array_list = std.ArrayList(u8).init(allocator);427
123 defer array_list.deinit();428/// Skips the next `n` bytes from the stream, advancing the seek position. This
124 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);429/// is typically and safely used after `peek`.
125 return try array_list.toOwnedSlice();430///
126}431/// Asserts that the number of bytes buffered is at least as many as `n`.
127432///
128/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.433/// The "tossed" memory remains alive until a "peek" operation occurs.
129/// Reads from the stream until specified byte is found. If the buffer is not434///
130/// large enough to hold the entire contents, `error.StreamTooLong` is returned.435/// See also:
131/// If end-of-stream is found, `error.EndOfStream` is returned.436/// * `peek`.
132/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The437/// * `discard`.
133/// delimiter byte is written to the output buffer but is not included438pub fn toss(r: *Reader, n: usize) void {
134/// in the returned slice.439 r.seek += n;
135pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 {440 assert(r.seek <= r.end);
136 var fbs = std.io.fixedBufferStream(buf);441}
137 try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len);442
138 const output = fbs.getWritten();443/// Equivalent to `toss(r.bufferedLen())`.
139 buf[output.len] = delimiter; // emulating old behaviour444pub fn tossAll(r: *Reader) void {
140 return output;445 r.seek = 0;
141}446 r.end = 0;
142447}
143/// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead.448
144/// Allocates enough memory to read until `delimiter` or end-of-stream.449/// Equivalent to `peek` followed by `toss`.
145/// If the allocated memory would be greater than `max_size`, returns450///
146/// `error.StreamTooLong`. If end-of-stream is found, returns the rest451/// The data returned is invalidated by the next call to `take`, `peek`,
147/// of the stream. If this function is called again after that, returns452/// `fill`, and functions with those prefixes.
148/// null.453pub fn take(r: *Reader, n: usize) Error![]u8 {
149/// Caller owns returned memory.454 const result = try r.peek(n);
150/// If this function returns an error, the contents from the stream read so far are lost.455 r.toss(n);
151pub fn readUntilDelimiterOrEofAlloc(456 return result;
152 self: Self,457}
153 allocator: mem.Allocator,458
154 delimiter: u8,459/// Returns the next `n` bytes from the stream as an array, filling the buffer
155 max_size: usize,460/// as necessary and advancing the seek position `n` bytes.
156) anyerror!?[]u8 {461///
157 var array_list = std.ArrayList(u8).init(allocator);462/// Asserts that the `Reader` was initialized with a buffer capacity at
158 defer array_list.deinit();463/// least as big as `n`.
159 self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) {464///
160 error.EndOfStream => if (array_list.items.len == 0) {465/// If there are fewer than `n` bytes left in the stream, `error.EndOfStream`
161 return null;466/// is returned instead.
467///
468/// See also:
469/// * `take`
470pub fn takeArray(r: *Reader, comptime n: usize) Error!*[n]u8 {
471 return (try r.take(n))[0..n];
472}
473
474/// Returns the next `n` bytes from the stream as an array, filling the buffer
475/// as necessary, without advancing the seek position.
476///
477/// Asserts that the `Reader` was initialized with a buffer capacity at
478/// least as big as `n`.
479///
480/// If there are fewer than `n` bytes left in the stream, `error.EndOfStream`
481/// is returned instead.
482///
483/// See also:
484/// * `peek`
485/// * `takeArray`
486pub fn peekArray(r: *Reader, comptime n: usize) Error!*[n]u8 {
487 return (try r.peek(n))[0..n];
488}
489
490/// Skips the next `n` bytes from the stream, advancing the seek position.
491///
492/// Unlike `toss` which is infallible, in this function `n` can be any amount.
493///
494/// Returns `error.EndOfStream` if fewer than `n` bytes could be discarded.
495///
496/// See also:
497/// * `toss`
498/// * `discardRemaining`
499/// * `discardShort`
500/// * `discard`
501pub fn discardAll(r: *Reader, n: usize) Error!void {
502 if ((try r.discardShort(n)) != n) return error.EndOfStream;
503}
504
505pub fn discardAll64(r: *Reader, n: u64) Error!void {
506 var remaining: u64 = n;
507 while (remaining > 0) {
508 const limited_remaining = std.math.cast(usize, remaining) orelse std.math.maxInt(usize);
509 try discardAll(r, limited_remaining);
510 remaining -= limited_remaining;
511 }
512}
513
514/// Skips the next `n` bytes from the stream, advancing the seek position.
515///
516/// Unlike `toss` which is infallible, in this function `n` can be any amount.
517///
518/// Returns the number of bytes discarded, which is less than `n` if and only
519/// if the stream reached the end.
520///
521/// See also:
522/// * `discardAll`
523/// * `discardRemaining`
524/// * `discard`
525pub fn discardShort(r: *Reader, n: usize) ShortError!usize {
526 const proposed_seek = r.seek + n;
527 if (proposed_seek <= r.end) {
528 @branchHint(.likely);
529 r.seek = proposed_seek;
530 return n;
531 }
532 var remaining = n - (r.end - r.seek);
533 r.end = 0;
534 r.seek = 0;
535 while (true) {
536 const discard_len = r.vtable.discard(r, .limited(remaining)) catch |err| switch (err) {
537 error.EndOfStream => return n - remaining,
538 error.ReadFailed => return error.ReadFailed,
539 };
540 remaining -= discard_len;
541 if (remaining == 0) return n;
542 }
543}
544
545/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
546/// the seek position.
547///
548/// Invalidates previously returned values from `peek`.
549///
550/// If the provided buffer cannot be filled completely, `error.EndOfStream` is
551/// returned instead.
552///
553/// See also:
554/// * `peek`
555/// * `readSliceShort`
556pub fn readSlice(r: *Reader, buffer: []u8) Error!void {
557 const n = try readSliceShort(r, buffer);
558 if (n != buffer.len) return error.EndOfStream;
559}
560
561/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
562/// the seek position.
563///
564/// Invalidates previously returned values from `peek`.
565///
566/// Returns the number of bytes read, which is less than `buffer.len` if and
567/// only if the stream reached the end.
568///
569/// See also:
570/// * `readSlice`
571pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
572 const in_buffer = r.buffer[r.seek..r.end];
573 const copy_len = @min(buffer.len, in_buffer.len);
574 @memcpy(buffer[0..copy_len], in_buffer[0..copy_len]);
575 if (buffer.len - copy_len == 0) {
576 r.seek += copy_len;
577 return buffer.len;
578 }
579 var i: usize = copy_len;
580 r.end = 0;
581 r.seek = 0;
582 while (true) {
583 const remaining = buffer[i..];
584 var wrapper: Writer.VectorWrapper = .{
585 .it = .{
586 .first = remaining,
587 .last = r.buffer,
588 },
589 .writer = .{
590 .buffer = if (remaining.len >= r.buffer.len) remaining else r.buffer,
591 .vtable = &Writer.VectorWrapper.vtable,
592 },
593 };
594 const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) {
595 error.WriteFailed => {
596 if (wrapper.writer.buffer.ptr != remaining.ptr) {
597 assert(r.seek == 0);
598 r.seek = remaining.len;
599 r.end = wrapper.writer.end;
600 @memcpy(remaining, r.buffer[0..remaining.len]);
601 return buffer.len;
602 }
603 return buffer.len;
604 },
605 error.EndOfStream => return i,
606 error.ReadFailed => return error.ReadFailed,
607 };
608 if (n < remaining.len) {
609 i += n;
610 continue;
611 }
612 r.end = n - remaining.len;
613 return buffer.len;
614 }
615}
616
617/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
618/// the seek position.
619///
620/// Invalidates previously returned values from `peek`.
621///
622/// If the provided buffer cannot be filled completely, `error.EndOfStream` is
623/// returned instead.
624///
625/// The function is inline to avoid the dead code in case `endian` is
626/// comptime-known and matches host endianness.
627///
628/// See also:
629/// * `readSlice`
630/// * `readSliceEndianAlloc`
631pub inline fn readSliceEndian(
632 r: *Reader,
633 comptime Elem: type,
634 buffer: []Elem,
635 endian: std.builtin.Endian,
636) Error!void {
637 try readSlice(r, @ptrCast(buffer));
638 if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem);
639}
640
641pub const ReadAllocError = Error || Allocator.Error;
642
643/// The function is inline to avoid the dead code in case `endian` is
644/// comptime-known and matches host endianness.
645pub inline fn readSliceEndianAlloc(
646 r: *Reader,
647 allocator: Allocator,
648 comptime Elem: type,
649 len: usize,
650 endian: std.builtin.Endian,
651) ReadAllocError![]Elem {
652 const dest = try allocator.alloc(Elem, len);
653 errdefer allocator.free(dest);
654 try readSlice(r, @ptrCast(dest));
655 if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem);
656 return dest;
657}
658
659pub fn readSliceAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 {
660 const dest = try allocator.alloc(u8, len);
661 errdefer allocator.free(dest);
662 try readSlice(r, dest);
663 return dest;
664}
665
666pub const DelimiterError = error{
667 /// See the `Reader` implementation for detailed diagnostics.
668 ReadFailed,
669 /// For "inclusive" functions, stream ended before the delimiter was found.
670 /// For "exclusive" functions, stream ended and there are no more bytes to
671 /// return.
672 EndOfStream,
673 /// The delimiter was not found within a number of bytes matching the
674 /// capacity of the `Reader`.
675 StreamTooLong,
676};
677
678/// Returns a slice of the next bytes of buffered data from the stream until
679/// `sentinel` is found, advancing the seek position.
680///
681/// Returned slice has a sentinel.
682///
683/// Invalidates previously returned values from `peek`.
684///
685/// See also:
686/// * `peekSentinel`
687/// * `takeDelimiterExclusive`
688/// * `takeDelimiterInclusive`
689pub fn takeSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {
690 const result = try r.peekSentinel(sentinel);
691 r.toss(result.len + 1);
692 return result;
693}
694
695pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {
696 const result = try r.peekDelimiterInclusive(sentinel);
697 return result[0 .. result.len - 1 :sentinel];
698}
699
700/// Returns a slice of the next bytes of buffered data from the stream until
701/// `delimiter` is found, advancing the seek position.
702///
703/// Returned slice includes the delimiter as the last byte.
704///
705/// Invalidates previously returned values from `peek`.
706///
707/// See also:
708/// * `takeSentinel`
709/// * `takeDelimiterExclusive`
710/// * `peekDelimiterInclusive`
711pub fn takeDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
712 const result = try r.peekDelimiterInclusive(delimiter);
713 r.toss(result.len);
714 return result;
715}
716
717/// Returns a slice of the next bytes of buffered data from the stream until
718/// `delimiter` is found, without advancing the seek position.
719///
720/// Returned slice includes the delimiter as the last byte.
721///
722/// Invalidates previously returned values from `peek`.
723///
724/// See also:
725/// * `peekSentinel`
726/// * `peekDelimiterExclusive`
727/// * `takeDelimiterInclusive`
728pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
729 const buffer = r.buffer[0..r.end];
730 const seek = r.seek;
731 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
732 @branchHint(.likely);
733 return buffer[seek .. end + 1];
734 }
735 if (seek > 0) {
736 const remainder = buffer[seek..];
737 @memmove(buffer[0..remainder.len], remainder);
738 r.end = remainder.len;
739 r.seek = 0;
740 }
741 var writer: Writer = .{
742 .buffer = r.buffer,
743 .vtable = &.{ .drain = Writer.fixedDrain },
744 };
745 while (r.end < r.buffer.len) {
746 writer.end = r.end;
747 const n = r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
748 error.WriteFailed => unreachable,
749 else => |e| return e,
750 };
751 const prev_end = r.end;
752 r.end = prev_end + n;
753 if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], prev_end, delimiter)) |end| {
754 return r.buffer[0 .. end + 1];
755 }
756 }
757 return error.StreamTooLong;
758}
759
760/// Returns a slice of the next bytes of buffered data from the stream until
761/// `delimiter` is found, advancing the seek position.
762///
763/// Returned slice excludes the delimiter. End-of-stream is treated equivalent
764/// to a delimiter, unless it would result in a length 0 return value, in which
765/// case `error.EndOfStream` is returned instead.
766///
767/// If the delimiter is not found within a number of bytes matching the
768/// capacity of this `Reader`, `error.StreamTooLong` is returned. In
769/// such case, the stream state is unmodified as if this function was never
770/// called.
771///
772/// Invalidates previously returned values from `peek`.
773///
774/// See also:
775/// * `takeDelimiterInclusive`
776/// * `peekDelimiterExclusive`
777pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
778 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
779 error.EndOfStream => {
780 if (r.end == 0) return error.EndOfStream;
781 r.toss(r.end);
782 return r.buffer[0..r.end];
162 },783 },
163 else => |e| return e,784 else => |e| return e,
164 };785 };
165 return try array_list.toOwnedSlice();786 r.toss(result.len);
166}787 return result[0 .. result.len - 1];
167788}
168/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
169/// Reads from the stream until specified byte is found. If the buffer is not
170/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
171/// If end-of-stream is found, returns the rest of the stream. If this
172/// function is called again after that, returns null.
173/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
174/// delimiter byte is written to the output buffer but is not included
175/// in the returned slice.
176pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) anyerror!?[]u8 {
177 var fbs = std.io.fixedBufferStream(buf);
178 self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) {
179 error.EndOfStream => if (fbs.getWritten().len == 0) {
180 return null;
181 },
182789
790/// Returns a slice of the next bytes of buffered data from the stream until
791/// `delimiter` is found, without advancing the seek position.
792///
793/// Returned slice excludes the delimiter. End-of-stream is treated equivalent
794/// to a delimiter, unless it would result in a length 0 return value, in which
795/// case `error.EndOfStream` is returned instead.
796///
797/// If the delimiter is not found within a number of bytes matching the
798/// capacity of this `Reader`, `error.StreamTooLong` is returned. In
799/// such case, the stream state is unmodified as if this function was never
800/// called.
801///
802/// Invalidates previously returned values from `peek`.
803///
804/// See also:
805/// * `peekDelimiterInclusive`
806/// * `takeDelimiterExclusive`
807pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
808 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
809 error.EndOfStream => {
810 if (r.end == 0) return error.EndOfStream;
811 return r.buffer[0..r.end];
812 },
183 else => |e| return e,813 else => |e| return e,
184 };814 };
185 const output = fbs.getWritten();815 return result[0 .. result.len - 1];
186 buf[output.len] = delimiter; // emulating old behaviour
187 return output;
188}816}
189817
190/// Appends to the `writer` contents by reading from the stream until `delimiter` is found.818/// Appends to `w` contents by reading from the stream until `delimiter` is
819/// found. Does not write the delimiter itself.
820///
821/// Returns number of bytes streamed.
822pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize {
823 const amount, const to = try r.readAny(w, delimiter, .unlimited);
824 return switch (to) {
825 .delimiter => amount,
826 .limit => unreachable,
827 .end => error.EndOfStream,
828 };
829}
830
831/// Appends to `w` contents by reading from the stream until `delimiter` is found.
191/// Does not write the delimiter itself.832/// Does not write the delimiter itself.
192/// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,833///
193/// returns `error.StreamTooLong` and finishes appending.834/// Succeeds if stream ends before delimiter found.
194/// If `optional_max_size` is null, appending is unbounded.835///
195pub fn streamUntilDelimiter(836/// Returns number of bytes streamed. The end is not signaled to the writer.
196 self: Self,837pub fn readDelimiterEnding(
197 writer: anytype,838 r: *Reader,
839 w: *Writer,
198 delimiter: u8,840 delimiter: u8,
199 optional_max_size: ?usize,841) StreamRemainingError!usize {
200) anyerror!void {842 const amount, const to = try r.readAny(w, delimiter, .unlimited);
201 if (optional_max_size) |max_size| {843 return switch (to) {
202 for (0..max_size) |_| {844 .delimiter, .end => amount,
203 const byte: u8 = try self.readByte();845 .limit => unreachable,
204 if (byte == delimiter) return;846 };
205 try writer.writeByte(byte);847}
206 }848
207 return error.StreamTooLong;849pub const StreamDelimiterLimitedError = StreamRemainingError || error{
208 } else {850 /// Stream ended before the delimiter was found.
209 while (true) {851 EndOfStream,
210 const byte: u8 = try self.readByte();852 /// The delimiter was not found within the limit.
211 if (byte == delimiter) return;853 StreamTooLong,
212 try writer.writeByte(byte);854};
213 }855
214 // Can not throw `error.StreamTooLong` since there are no boundary.856/// Appends to `w` contents by reading from the stream until `delimiter` is found.
857/// Does not write the delimiter itself.
858///
859/// Returns number of bytes streamed.
860pub fn readDelimiterLimit(
861 r: *Reader,
862 w: *Writer,
863 delimiter: u8,
864 limit: Limit,
865) StreamDelimiterLimitedError!usize {
866 const amount, const to = try r.readAny(w, delimiter, limit);
867 return switch (to) {
868 .delimiter => amount,
869 .limit => error.StreamTooLong,
870 .end => error.EndOfStream,
871 };
872}
873
874fn readAny(
875 r: *Reader,
876 w: *Writer,
877 delimiter: ?u8,
878 limit: Limit,
879) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } {
880 var amount: usize = 0;
881 var remaining = limit;
882 while (remaining.nonzero()) {
883 const available = remaining.slice(r.peekGreedy(1) catch |err| switch (err) {
884 error.ReadFailed => |e| return e,
885 error.EndOfStream => return .{ amount, .end },
886 });
887 if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| {
888 try w.writeAll(available[0..delimiter_index]);
889 r.toss(delimiter_index + 1);
890 return .{ amount + delimiter_index, .delimiter };
891 };
892 try w.writeAll(available);
893 r.toss(available.len);
894 amount += available.len;
895 remaining = remaining.subtract(available.len).?;
215 }896 }
897 return .{ amount, .limit };
216}898}
217899
218/// Reads from the stream until specified byte is found, discarding all data,900/// Reads from the stream until specified byte is found, discarding all data,
219/// including the delimiter.901/// including the delimiter.
220/// If end-of-stream is found, this function succeeds.902///
221pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void {903/// If end of stream is found, this function succeeds.
222 while (true) {904pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void {
223 const byte = self.readByte() catch |err| switch (err) {905 _ = r;
224 error.EndOfStream => return,906 _ = delimiter;
225 else => |e| return e,907 @panic("TODO");
908}
909
910/// Reads from the stream until specified byte is found, discarding all data,
911/// excluding the delimiter.
912///
913/// Succeeds if stream ends before delimiter found.
914pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!void {
915 _ = r;
916 _ = delimiter;
917 @panic("TODO");
918}
919
920/// Fills the buffer such that it contains at least `n` bytes, without
921/// advancing the seek position.
922///
923/// Returns `error.EndOfStream` if and only if there are fewer than `n` bytes
924/// remaining.
925///
926/// Asserts buffer capacity is at least `n`.
927pub fn fill(r: *Reader, n: usize) Error!void {
928 assert(n <= r.buffer.len);
929 if (r.seek + n <= r.end) {
930 @branchHint(.likely);
931 return;
932 }
933 rebaseCapacity(r, n);
934 var writer: Writer = .{
935 .buffer = r.buffer,
936 .vtable = &.{ .drain = Writer.fixedDrain },
937 };
938 while (r.end < r.seek + n) {
939 writer.end = r.end;
940 r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
941 error.WriteFailed => unreachable,
942 error.ReadFailed, error.EndOfStream => |e| return e,
226 };943 };
227 if (byte == delimiter) return;
228 }944 }
229}945}
230946
231/// Reads 1 byte from the stream or returns `error.EndOfStream`.947/// Without advancing the seek position, does exactly one underlying read, filling the buffer as
232pub fn readByte(self: Self) anyerror!u8 {948/// much as possible. This may result in zero bytes added to the buffer, which is not an end of
233 var result: [1]u8 = undefined;949/// stream condition. End of stream is communicated via returning `error.EndOfStream`.
234 const amt_read = try self.read(result[0..]);950///
235 if (amt_read < 1) return error.EndOfStream;951/// Asserts buffer capacity is at least 1.
236 return result[0];952pub fn fillMore(r: *Reader) Error!void {
237}953 rebaseCapacity(r, 1);
238954 var writer: Writer = .{
239/// Same as `readByte` except the returned byte is signed.955 .buffer = r.buffer,
240pub fn readByteSigned(self: Self) anyerror!i8 {956 .end = r.end,
241 return @as(i8, @bitCast(try self.readByte()));957 .vtable = &.{ .drain = Writer.fixedDrain },
242}958 };
243959 r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
244/// Reads exactly `num_bytes` bytes and returns as an array.960 error.WriteFailed => unreachable,
245/// `num_bytes` must be comptime-known961 else => |e| return e,
246pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 {962 };
247 var bytes: [num_bytes]u8 = undefined;963}
248 try self.readNoEof(&bytes);964
249 return bytes;965/// Returns the next byte from the stream or returns `error.EndOfStream`.
250}966///
251967/// Does not advance the seek position.
252/// Reads bytes until `bounded.len` is equal to `num_bytes`,968///
253/// or the stream ends.969/// Asserts the buffer capacity is nonzero.
254///970pub fn peekByte(r: *Reader) Error!u8 {
255/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`971 const buffer = r.buffer[0..r.end];
256pub fn readIntoBoundedBytes(972 const seek = r.seek;
257 self: Self,973 if (seek >= buffer.len) {
258 comptime num_bytes: usize,974 @branchHint(.unlikely);
259 bounded: *std.BoundedArray(u8, num_bytes),975 try fill(r, 1);
260) anyerror!void {
261 while (bounded.len < num_bytes) {
262 // get at most the number of bytes free in the bounded array
263 const bytes_read = try self.read(bounded.unusedCapacitySlice());
264 if (bytes_read == 0) return;
265
266 // bytes_read will never be larger than @TypeOf(bounded.len)
267 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
268 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
269 }976 }
977 return buffer[seek];
270}978}
271979
272/// Reads at most `num_bytes` and returns as a bounded array.980/// Reads 1 byte from the stream or returns `error.EndOfStream`.
273pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) {981///
274 var result = std.BoundedArray(u8, num_bytes){};982/// Asserts the buffer capacity is nonzero.
275 try self.readIntoBoundedBytes(num_bytes, &result);983pub fn takeByte(r: *Reader) Error!u8 {
984 const result = try peekByte(r);
985 r.seek += 1;
276 return result;986 return result;
277}987}
278988
279pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {989/// Same as `takeByte` except the returned byte is signed.
280 const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8));990pub fn takeByteSigned(r: *Reader) Error!i8 {
281 return mem.readInt(T, &bytes, endian);991 return @bitCast(try r.takeByte());
282}992}
283993
284pub fn readVarInt(994/// Asserts the buffer was initialized with a capacity at least `@bitSizeOf(T) / 8`.
285 self: Self,995pub inline fn takeInt(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
286 comptime ReturnType: type,996 const n = @divExact(@typeInfo(T).int.bits, 8);
287 endian: std.builtin.Endian,997 return std.mem.readInt(T, try r.takeArray(n), endian);
288 size: usize,998}
289) anyerror!ReturnType {
290 assert(size <= @sizeOf(ReturnType));
291 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
292 const bytes = bytes_buf[0..size];
293 try self.readNoEof(bytes);
294 return mem.readVarInt(ReturnType, bytes, endian);
295}
296
297/// Optional parameters for `skipBytes`
298pub const SkipBytesOptions = struct {
299 buf_size: usize = 512,
300};
301
302// `num_bytes` is a `u64` to match `off_t`
303/// Reads `num_bytes` bytes from the stream and discards them
304pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void {
305 var buf: [options.buf_size]u8 = undefined;
306 var remaining = num_bytes;
307999
308 while (remaining > 0) {1000/// Asserts the buffer was initialized with a capacity at least `n`.
309 const amt = @min(remaining, options.buf_size);1001pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n: usize) Error!Int {
310 try self.readNoEof(buf[0..amt]);1002 assert(n <= @sizeOf(Int));
311 remaining -= amt;1003 return std.mem.readVarInt(Int, try r.take(n), endian);
312 }
313}1004}
3141005
315/// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice1006/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
316pub fn isBytes(self: Self, slice: []const u8) anyerror!bool {1007///
317 var i: usize = 0;1008/// Advances the seek position.
318 var matches = true;1009///
319 while (i < slice.len) : (i += 1) {1010/// See also:
320 if (slice[i] != try self.readByte()) {1011/// * `peekStruct`
321 matches = false;1012pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
322 }1013 // Only extern and packed structs have defined in-memory layout.
323 }1014 comptime assert(@typeInfo(T).@"struct".layout != .auto);
324 return matches;1015 return @ptrCast(try r.takeArray(@sizeOf(T)));
325}1016}
3261017
327pub fn readStruct(self: Self, comptime T: type) anyerror!T {1018/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
1019///
1020/// Does not advance the seek position.
1021///
1022/// See also:
1023/// * `takeStruct`
1024pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
328 // Only extern and packed structs have defined in-memory layout.1025 // Only extern and packed structs have defined in-memory layout.
329 comptime assert(@typeInfo(T).@"struct".layout != .auto);1026 comptime assert(@typeInfo(T).@"struct".layout != .auto);
330 var res: [1]T = undefined;1027 return @ptrCast(try r.peekArray(@sizeOf(T)));
331 try self.readNoEof(mem.sliceAsBytes(res[0..]));
332 return res[0];
333}1028}
3341029
335pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {1030/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
336 var res = try self.readStruct(T);1031///
337 if (native_endian != endian) {1032/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
338 mem.byteSwapAllFields(T, &res);1033/// when `endian` is comptime-known and matches the host endianness.
339 }1034pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1035 var res = (try r.takeStruct(T)).*;
1036 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1037 return res;
1038}
1039
1040/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
1041///
1042/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
1043/// when `endian` is comptime-known and matches the host endianness.
1044pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1045 var res = (try r.peekStruct(T)).*;
1046 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
340 return res;1047 return res;
341}1048}
3421049
343/// Reads an integer with the same size as the given enum's tag type. If the integer matches1050pub const TakeEnumError = Error || error{InvalidEnumTag};
344/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.1051
345/// TODO optimization taking advantage of most fields being in order1052/// Reads an integer with the same size as the given enum's tag type. If the
346pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {1053/// integer matches an enum tag, casts the integer to the enum tag and returns
347 const E = error{1054/// it. Otherwise, returns `error.InvalidEnumTag`.
348 /// An integer was read, but it did not match any of the tags in the supplied enum.1055///
349 InvalidValue,1056/// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`.
1057pub fn takeEnum(r: *Reader, comptime Enum: type, endian: std.builtin.Endian) TakeEnumError!Enum {
1058 const Tag = @typeInfo(Enum).@"enum".tag_type;
1059 const int = try r.takeInt(Tag, endian);
1060 return std.meta.intToEnum(Enum, int);
1061}
1062
1063/// Reads an integer with the same size as the given nonexhaustive enum's tag type.
1064///
1065/// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`.
1066pub fn takeEnumNonexhaustive(r: *Reader, comptime Enum: type, endian: std.builtin.Endian) Error!Enum {
1067 const info = @typeInfo(Enum).@"enum";
1068 comptime assert(!info.is_exhaustive);
1069 comptime assert(@bitSizeOf(info.tag_type) == @sizeOf(info.tag_type) * 8);
1070 return takeEnum(r, Enum, endian) catch |err| switch (err) {
1071 error.InvalidEnumTag => unreachable,
1072 else => |e| return e,
350 };1073 };
351 const type_info = @typeInfo(Enum).@"enum";1074}
352 const tag = try self.readInt(type_info.tag_type, endian);
3531075
354 inline for (std.meta.fields(Enum)) |field| {1076pub const TakeLeb128Error = Error || error{Overflow};
355 if (tag == field.value) {1077
356 return @field(Enum, field.name);1078/// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit.
357 }1079pub fn takeLeb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result {
1080 const result_info = @typeInfo(Result).int;
1081 return std.math.cast(Result, try r.takeMultipleOf7Leb128(@Type(.{ .int = .{
1082 .signedness = result_info.signedness,
1083 .bits = std.mem.alignForwardAnyAlign(u16, result_info.bits, 7),
1084 } }))) orelse error.Overflow;
1085}
1086
1087pub fn expandTotalCapacity(r: *Reader, allocator: Allocator, n: usize) Allocator.Error!void {
1088 if (n <= r.buffer.len) return;
1089 if (r.seek > 0) rebase(r);
1090 var list: ArrayList(u8) = .{
1091 .items = r.buffer[0..r.end],
1092 .capacity = r.buffer.len,
1093 };
1094 defer r.buffer = list.allocatedSlice();
1095 try list.ensureTotalCapacity(allocator, n);
1096}
1097
1098pub const FillAllocError = Error || Allocator.Error;
1099
1100pub fn fillAlloc(r: *Reader, allocator: Allocator, n: usize) FillAllocError!void {
1101 try expandTotalCapacity(r, allocator, n);
1102 return fill(r, n);
1103}
1104
1105/// Returns a slice into the unused capacity of `buffer` with at least
1106/// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary.
1107///
1108/// After calling this function, typically the caller will follow up with a
1109/// call to `advanceBufferEnd` to report the actual number of bytes buffered.
1110pub fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 {
1111 {
1112 const unused = r.buffer[r.end..];
1113 if (unused.len >= min_len) return unused;
1114 }
1115 if (r.seek > 0) rebase(r);
1116 {
1117 var list: ArrayList(u8) = .{
1118 .items = r.buffer[0..r.end],
1119 .capacity = r.buffer.len,
1120 };
1121 defer r.buffer = list.allocatedSlice();
1122 try list.ensureUnusedCapacity(allocator, min_len);
358 }1123 }
1124 const unused = r.buffer[r.end..];
1125 assert(unused.len >= min_len);
1126 return unused;
1127}
3591128
360 return E.InvalidValue;1129/// After writing directly into the unused capacity of `buffer`, this function
1130/// updates `end` so that users of `Reader` can receive the data.
1131pub fn advanceBufferEnd(r: *Reader, n: usize) void {
1132 assert(n <= r.buffer.len - r.end);
1133 r.end += n;
361}1134}
3621135
363/// Reads the stream until the end, ignoring all the data.1136fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result {
364/// Returns the number of bytes discarded.1137 const result_info = @typeInfo(Result).int;
365pub fn discard(self: Self) anyerror!u64 {1138 comptime assert(result_info.bits % 7 == 0);
366 var trash: [4096]u8 = undefined;1139 var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits;
367 var index: u64 = 0;1140 const UnsignedResult = @Type(.{ .int = .{
1141 .signedness = .unsigned,
1142 .bits = result_info.bits,
1143 } });
1144 var result: UnsignedResult = 0;
1145 var fits = true;
368 while (true) {1146 while (true) {
369 const n = try self.read(&trash);1147 const buffer: []const packed struct(u8) { bits: u7, more: bool } = @ptrCast(try r.peekGreedy(1));
370 if (n == 0) return index;1148 for (buffer, 1..) |byte, len| {
371 index += n;1149 if (remaining_bits > 0) {
1150 result = @shlExact(@as(UnsignedResult, byte.bits), result_info.bits - 7) |
1151 if (result_info.bits > 7) @shrExact(result, 7) else 0;
1152 remaining_bits -= 7;
1153 } else if (fits) fits = switch (result_info.signedness) {
1154 .signed => @as(i7, @bitCast(byte.bits)) ==
1155 @as(i7, @truncate(@as(Result, @bitCast(result)) >> (result_info.bits - 1))),
1156 .unsigned => byte.bits == 0,
1157 };
1158 if (byte.more) continue;
1159 r.toss(len);
1160 return if (fits) @as(Result, @bitCast(result)) >> remaining_bits else error.Overflow;
1161 }
1162 r.toss(buffer.len);
372 }1163 }
373}1164}
3741165
375const std = @import("../std.zig");1166/// Left-aligns data such that `r.seek` becomes zero.
376const Self = @This();1167pub fn rebase(r: *Reader) void {
377const math = std.math;1168 if (r.seek == 0) return;
378const assert = std.debug.assert;1169 const data = r.buffer[r.seek..r.end];
379const mem = std.mem;1170 @memmove(r.buffer[0..data.len], data);
380const testing = std.testing;1171 r.seek = 0;
381const native_endian = @import("builtin").target.cpu.arch.endian();1172 r.end = data.len;
382const Alignment = std.mem.Alignment;1173}
1174
1175/// Ensures `capacity` more data can be buffered without rebasing, by rebasing
1176/// if necessary.
1177///
1178/// Asserts `capacity` is within the buffer capacity.
1179pub fn rebaseCapacity(r: *Reader, capacity: usize) void {
1180 if (r.end > r.buffer.len - capacity) rebase(r);
1181}
1182
1183/// Advances the stream and decreases the size of the storage buffer by `n`,
1184/// returning the range of bytes no longer accessible by `r`.
1185///
1186/// This action can be undone by `restitute`.
1187///
1188/// Asserts there are at least `n` buffered bytes already.
1189///
1190/// Asserts that `r.seek` is zero, i.e. the buffer is in a rebased state.
1191pub fn steal(r: *Reader, n: usize) []u8 {
1192 assert(r.seek == 0);
1193 assert(n <= r.end);
1194 const stolen = r.buffer[0..n];
1195 r.buffer = r.buffer[n..];
1196 r.end -= n;
1197 return stolen;
1198}
1199
1200/// Expands the storage buffer, undoing the effects of `steal`
1201/// Assumes that `n` does not exceed the total number of stolen bytes.
1202pub fn restitute(r: *Reader, n: usize) void {
1203 r.buffer = (r.buffer.ptr - n)[0 .. r.buffer.len + n];
1204 r.end += n;
1205 r.seek += n;
1206}
1207
1208test fixed {
1209 var r: Reader = .fixed("a\x02");
1210 try testing.expect((try r.takeByte()) == 'a');
1211 try testing.expect((try r.takeEnum(enum(u8) {
1212 a = 0,
1213 b = 99,
1214 c = 2,
1215 d = 3,
1216 }, builtin.cpu.arch.endian())) == .c);
1217 try testing.expectError(error.EndOfStream, r.takeByte());
1218}
1219
1220test peek {
1221 return error.Unimplemented;
1222}
1223
1224test peekGreedy {
1225 return error.Unimplemented;
1226}
1227
1228test toss {
1229 return error.Unimplemented;
1230}
1231
1232test take {
1233 return error.Unimplemented;
1234}
1235
1236test takeArray {
1237 return error.Unimplemented;
1238}
1239
1240test peekArray {
1241 return error.Unimplemented;
1242}
1243
1244test discardAll {
1245 var r: Reader = .fixed("foobar");
1246 try r.discard(3);
1247 try testing.expectEqualStrings("bar", try r.take(3));
1248 try r.discard(0);
1249 try testing.expectError(error.EndOfStream, r.discard(1));
1250}
1251
1252test discardRemaining {
1253 return error.Unimplemented;
1254}
1255
1256test stream {
1257 return error.Unimplemented;
1258}
3831259
384test {1260test takeSentinel {
385 _ = @import("Reader/test.zig");1261 return error.Unimplemented;
1262}
1263
1264test peekSentinel {
1265 return error.Unimplemented;
1266}
1267
1268test takeDelimiterInclusive {
1269 return error.Unimplemented;
1270}
1271
1272test peekDelimiterInclusive {
1273 return error.Unimplemented;
1274}
1275
1276test takeDelimiterExclusive {
1277 return error.Unimplemented;
1278}
1279
1280test peekDelimiterExclusive {
1281 return error.Unimplemented;
1282}
1283
1284test readDelimiter {
1285 return error.Unimplemented;
1286}
1287
1288test readDelimiterEnding {
1289 return error.Unimplemented;
1290}
1291
1292test readDelimiterLimit {
1293 return error.Unimplemented;
1294}
1295
1296test discardDelimiterExclusive {
1297 return error.Unimplemented;
1298}
1299
1300test discardDelimiterInclusive {
1301 return error.Unimplemented;
1302}
1303
1304test fill {
1305 return error.Unimplemented;
1306}
1307
1308test takeByte {
1309 return error.Unimplemented;
1310}
1311
1312test takeByteSigned {
1313 return error.Unimplemented;
1314}
1315
1316test takeInt {
1317 return error.Unimplemented;
1318}
1319
1320test takeVarInt {
1321 return error.Unimplemented;
1322}
1323
1324test takeStruct {
1325 return error.Unimplemented;
1326}
1327
1328test peekStruct {
1329 return error.Unimplemented;
1330}
1331
1332test takeStructEndian {
1333 return error.Unimplemented;
1334}
1335
1336test peekStructEndian {
1337 return error.Unimplemented;
1338}
1339
1340test takeEnum {
1341 return error.Unimplemented;
1342}
1343
1344test takeLeb128 {
1345 return error.Unimplemented;
1346}
1347
1348test readSliceShort {
1349 return error.Unimplemented;
1350}
1351
1352test readVec {
1353 return error.Unimplemented;
1354}
1355
1356test "expected error.EndOfStream" {
1357 // Unit test inspired by https://github.com/ziglang/zig/issues/17733
1358 var r: std.io.Reader = .fixed("");
1359 try std.testing.expectError(error.EndOfStream, r.readEnum(enum(u8) { a, b }, .little));
1360 try std.testing.expectError(error.EndOfStream, r.isBytes("foo"));
1361}
1362
1363fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1364 _ = r;
1365 _ = w;
1366 _ = limit;
1367 return error.EndOfStream;
1368}
1369
1370fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
1371 _ = r;
1372 _ = limit;
1373 return error.EndOfStream;
1374}
1375
1376fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1377 _ = r;
1378 _ = w;
1379 _ = limit;
1380 return error.ReadFailed;
1381}
1382
1383fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
1384 _ = r;
1385 _ = limit;
1386 return error.ReadFailed;
1387}
1388
1389test "readAlloc when the backing reader provides one byte at a time" {
1390 const OneByteReader = struct {
1391 str: []const u8,
1392 curr: usize,
1393
1394 fn read(self: *@This(), dest: []u8) usize {
1395 if (self.str.len <= self.curr or dest.len == 0)
1396 return 0;
1397
1398 dest[0] = self.str[self.curr];
1399 self.curr += 1;
1400 return 1;
1401 }
1402 };
1403
1404 const str = "This is a test";
1405 var one_byte_stream: OneByteReader = .init(str);
1406 const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1);
1407 defer std.testing.allocator.free(res);
1408 try std.testing.expectEqualStrings(str, res);
1409}
1410
1411/// Provides a `Reader` implementation by passing data from an underlying
1412/// reader through `Hasher.update`.
1413///
1414/// The underlying reader is best unbuffered.
1415///
1416/// This implementation makes suboptimal buffering decisions due to being
1417/// generic. A better solution will involve creating a reader for each hash
1418/// function, where the discard buffer can be tailored to the hash
1419/// implementation details.
1420pub fn Hashed(comptime Hasher: type) type {
1421 return struct {
1422 in: *Reader,
1423 hasher: Hasher,
1424 interface: Reader,
1425
1426 pub fn init(in: *Reader, hasher: Hasher, buffer: []u8) @This() {
1427 return .{
1428 .in = in,
1429 .hasher = hasher,
1430 .interface = .{
1431 .vtable = &.{
1432 .read = @This().read,
1433 .discard = @This().discard,
1434 },
1435 .buffer = buffer,
1436 .end = 0,
1437 .seek = 0,
1438 },
1439 };
1440 }
1441
1442 fn read(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1443 const this: *@This() = @alignCast(@fieldParentPtr("interface", r));
1444 const data = w.writableVector(limit);
1445 const n = try this.in.readVec(data);
1446 const result = w.advanceVector(n);
1447 var remaining: usize = n;
1448 for (data) |slice| {
1449 if (remaining < slice.len) {
1450 this.hasher.update(slice[0..remaining]);
1451 return result;
1452 } else {
1453 remaining -= slice.len;
1454 this.hasher.update(slice);
1455 }
1456 }
1457 assert(remaining == 0);
1458 return result;
1459 }
1460
1461 fn discard(r: *Reader, limit: Limit) Error!usize {
1462 const this: *@This() = @alignCast(@fieldParentPtr("interface", r));
1463 var w = this.hasher.writer(&.{});
1464 const n = this.in.stream(&w, limit) catch |err| switch (err) {
1465 error.WriteFailed => unreachable,
1466 else => |e| return e,
1467 };
1468 return n;
1469 }
1470 };
386}1471}
lib/std/io/Reader/Limited.zig created+42
...@@ -0,0 +1,42 @@
1const Limited = @This();
2
3const std = @import("../../std.zig");
4const Reader = std.io.Reader;
5const Writer = std.io.Writer;
6const Limit = std.io.Limit;
7
8unlimited: *Reader,
9remaining: Limit,
10interface: Reader,
11
12pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited {
13 return .{
14 .unlimited = reader,
15 .remaining = limit,
16 .interface = .{
17 .vtable = &.{
18 .stream = stream,
19 .discard = discard,
20 },
21 .buffer = buffer,
22 .seek = 0,
23 .end = 0,
24 },
25 };
26}
27
28fn stream(context: ?*anyopaque, w: *Writer, limit: Limit) Reader.StreamError!usize {
29 const l: *Limited = @alignCast(@ptrCast(context));
30 const combined_limit = limit.min(l.remaining);
31 const n = try l.unlimited_reader.read(w, combined_limit);
32 l.remaining = l.remaining.subtract(n).?;
33 return n;
34}
35
36fn discard(context: ?*anyopaque, limit: Limit) Reader.Error!usize {
37 const l: *Limited = @alignCast(@ptrCast(context));
38 const combined_limit = limit.min(l.remaining);
39 const n = try l.unlimited_reader.discard(combined_limit);
40 l.remaining = l.remaining.subtract(n).?;
41 return n;
42}
lib/std/io/Writer.zig+2168-45
...@@ -1,83 +1,2206 @@...@@ -1,83 +1,2206 @@
1const builtin = @import("builtin");
2const native_endian = builtin.target.cpu.arch.endian();
3
4const Writer = @This();
1const std = @import("../std.zig");5const std = @import("../std.zig");
2const assert = std.debug.assert;6const assert = std.debug.assert;
3const mem = std.mem;7const Limit = std.io.Limit;
4const native_endian = @import("builtin").target.cpu.arch.endian();8const File = std.fs.File;
9const testing = std.testing;
10const Allocator = std.mem.Allocator;
11
12vtable: *const VTable,
13/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.
14buffer: []u8,
15/// In `buffer` before this are buffered bytes, after this is `undefined`.
16end: usize = 0,
17/// Tracks total number of bytes written to this `Writer`. This value
18/// only increases. In the case of fixed mode, this value always equals `end`.
19///
20/// This value is maintained by the interface; `VTable` function
21/// implementations need not modify it.
22count: usize = 0,
23
24pub const VTable = struct {
25 /// Sends bytes to the logical sink. A write will only be sent here if it
26 /// could not fit into `buffer`, or during a `flush` operation.
27 ///
28 /// `buffer[0..end]` is consumed first, followed by each slice of `data` in
29 /// order. Elements of `data` may alias each other but may not alias
30 /// `buffer`.
31 ///
32 /// This function modifies `Writer.end` and `Writer.buffer` in an
33 /// implementation-defined manner.
34 ///
35 /// `data.len` must be nonzero.
36 ///
37 /// The last element of `data` is repeated as necessary so that it is
38 /// written `splat` number of times, which may be zero.
39 ///
40 /// Number of bytes consumed from `data` is returned, excluding bytes from
41 /// `buffer`.
42 ///
43 /// Number of bytes returned may be zero, which does not indicate stream
44 /// end. A subsequent call may return nonzero, or signal end of stream via
45 /// `error.WriteFailed`.
46 drain: *const fn (w: *Writer, data: []const []const u8, splat: usize) Error!usize,
47
48 /// Copies contents from an open file to the logical sink. `buffer[0..end]`
49 /// is consumed first, followed by `limit` bytes from `file_reader`.
50 ///
51 /// Number of bytes logically written is returned. This excludes bytes from
52 /// `buffer` because they have already been logically written. Number of
53 /// bytes consumed from `buffer` are tracked by modifying `end`.
54 ///
55 /// Number of bytes returned may be zero, which does not indicate stream
56 /// end. A subsequent call may return nonzero, or signal end of stream via
57 /// `error.WriteFailed`. Caller may check `file_reader` state
58 /// (`File.Reader.atEnd`) to disambiguate between a zero-length read or
59 /// write, and whether the file reached the end.
60 ///
61 /// `error.Unimplemented` indicates the callee cannot offer a more
62 /// efficient implementation than the caller performing its own reads.
63 sendFile: *const fn (
64 w: *Writer,
65 file_reader: *File.Reader,
66 /// Maximum amount of bytes to read from the file. Implementations may
67 /// assume that the file size does not exceed this amount. Data from
68 /// `buffer` does not count towards this limit.
69 limit: Limit,
70 ) FileError!usize = unimplementedSendFile,
71
72 /// Consumes all remaining buffer.
73 ///
74 /// The default flush implementation calls drain repeatedly until `end` is
75 /// zero, however it is legal for implementations to manage `end`
76 /// differently. For instance, `Allocating` flush is a no-op.
77 ///
78 /// There may be subsequent calls to `drain` and `sendFile` after a `flush`
79 /// operation.
80 flush: *const fn (w: *Writer) Error!void = defaultFlush,
81};
82
83pub const Error = error{
84 /// See the `Writer` implementation for detailed diagnostics.
85 WriteFailed,
86};
587
6context: *const anyopaque,88pub const FileAllError = error{
7writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize,89 /// Detailed diagnostics are found on the `File.Reader` struct.
90 ReadFailed,
91 /// See the `Writer` implementation for detailed diagnostics.
92 WriteFailed,
93};
894
9const Self = @This();95pub const FileReadingError = error{
10pub const Error = anyerror;96 /// Detailed diagnostics are found on the `File.Reader` struct.
97 ReadFailed,
98 /// See the `Writer` implementation for detailed diagnostics.
99 WriteFailed,
100 /// Reached the end of the file being read.
101 EndOfStream,
102};
11103
12pub fn write(self: Self, bytes: []const u8) anyerror!usize {104pub const FileError = error{
13 return self.writeFn(self.context, bytes);105 /// Detailed diagnostics are found on the `File.Reader` struct.
106 ReadFailed,
107 /// See the `Writer` implementation for detailed diagnostics.
108 WriteFailed,
109 /// Reached the end of the file being read.
110 EndOfStream,
111 /// Indicates the caller should do its own file reading; the callee cannot
112 /// offer a more efficient implementation.
113 Unimplemented,
114};
115
116/// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless
117/// modified externally, `count` will always equal `end`.
118pub fn fixed(buffer: []u8) Writer {
119 return .{
120 .vtable = &.{ .drain = fixedDrain },
121 .buffer = buffer,
122 };
14}123}
15124
16pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {125pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) {
17 var index: usize = 0;126 return .{ .out = w, .hasher = hasher };
18 while (index != bytes.len) {127}
19 index += try self.write(bytes[index..]);128
129pub const failing: Writer = .{
130 .vtable = &.{
131 .drain = failingDrain,
132 .sendFile = failingSendFile,
133 },
134};
135
136pub fn discarding(buffer: []u8) Writer {
137 return .{
138 .vtable = &.{
139 .drain = discardingDrain,
140 .sendFile = discardingSendFile,
141 },
142 .buffer = buffer,
143 };
144}
145
146/// Returns the contents not yet drained.
147pub fn buffered(w: *const Writer) []u8 {
148 return w.buffer[0..w.end];
149}
150
151pub fn countSplat(data: []const []const u8, splat: usize) usize {
152 var total: usize = 0;
153 for (data[0 .. data.len - 1]) |buf| total += buf.len;
154 total += data[data.len - 1].len * splat;
155 return total;
156}
157
158pub fn countSendFileLowerBound(n: usize, file_reader: *File.Reader, limit: Limit) ?usize {
159 const total: u64 = @min(@intFromEnum(limit), file_reader.getSize() catch return null);
160 return std.math.lossyCast(usize, total + n);
161}
162
163/// If the total number of bytes of `data` fits inside `unusedCapacitySlice`,
164/// this function is guaranteed to not fail, not call into `VTable`, and return
165/// the total bytes inside `data`.
166pub fn writeVec(w: *Writer, data: []const []const u8) Error!usize {
167 return writeSplat(w, data, 1);
168}
169
170/// If the number of bytes to write based on `data` and `splat` fits inside
171/// `unusedCapacitySlice`, this function is guaranteed to not fail, not call
172/// into `VTable`, and return the full number of bytes.
173pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
174 assert(data.len > 0);
175 const buffer = w.buffer;
176 const count = countSplat(data, splat);
177 if (w.end + count > buffer.len) {
178 const n = try w.vtable.drain(w, data, splat);
179 w.count += n;
180 return n;
181 }
182 w.count += count;
183 for (data) |bytes| {
184 @memcpy(buffer[w.end..][0..bytes.len], bytes);
185 w.end += bytes.len;
20 }186 }
187 const pattern = data[data.len - 1];
188 if (splat == 0) {
189 @branchHint(.unlikely);
190 w.end -= pattern.len;
191 return count;
192 }
193 const remaining_splat = splat - 1;
194 switch (pattern.len) {
195 0 => {},
196 1 => {
197 @memset(buffer[w.end..][0..remaining_splat], pattern[0]);
198 w.end += remaining_splat;
199 },
200 else => {
201 const new_end = w.end + pattern.len * remaining_splat;
202 while (w.end < new_end) : (w.end += pattern.len) {
203 @memcpy(buffer[w.end..][0..pattern.len], pattern);
204 }
205 },
206 }
207 return count;
21}208}
22209
23pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void {210/// Equivalent to `writeSplat` but writes at most `limit` bytes.
24 return std.fmt.format(self, format, args);211pub fn writeSplatLimit(
212 w: *Writer,
213 data: []const []const u8,
214 splat: usize,
215 limit: Limit,
216) Error!usize {
217 _ = w;
218 _ = data;
219 _ = splat;
220 _ = limit;
221 @panic("TODO");
25}222}
26223
27pub fn writeByte(self: Self, byte: u8) anyerror!void {224/// Returns how many bytes were consumed from `header` and `data`.
28 const array = [1]u8{byte};225pub fn writeSplatHeader(
29 return self.writeAll(&array);226 w: *Writer,
227 header: []const u8,
228 data: []const []const u8,
229 splat: usize,
230) Error!usize {
231 const new_end = w.end + header.len;
232 if (new_end <= w.buffer.len) {
233 @memcpy(w.buffer[w.end..][0..header.len], header);
234 w.end = new_end;
235 w.count += header.len;
236 return header.len + try writeSplat(w, data, splat);
237 }
238 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
239 var i: usize = 1;
240 vecs[0] = header;
241 for (data) |buf| {
242 if (buf.len == 0) continue;
243 vecs[i] = buf;
244 i += 1;
245 if (vecs.len - i == 0) break;
246 }
247 const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1;
248 const n = try w.vtable.drain(w, vecs[0..i], new_splat);
249 w.count += n;
250 return n;
30}251}
31252
32pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void {253/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.
33 var bytes: [256]u8 = undefined;254pub fn writeSplatHeaderLimit(
34 @memset(bytes[0..], byte);255 w: *Writer,
256 header: []const u8,
257 data: []const []const u8,
258 splat: usize,
259 limit: Limit,
260) Error!usize {
261 _ = w;
262 _ = header;
263 _ = data;
264 _ = splat;
265 _ = limit;
266 @panic("TODO");
267}
35268
36 var remaining: usize = n;269/// Drains all remaining buffered data.
37 while (remaining > 0) {270pub fn flush(w: *Writer) Error!void {
38 const to_write = @min(remaining, bytes.len);271 return w.vtable.flush(w);
39 try self.writeAll(bytes[0..to_write]);272}
40 remaining -= to_write;273
274/// Repeatedly calls `VTable.drain` until `end` is zero.
275pub fn defaultFlush(w: *Writer) Error!void {
276 const drainFn = w.vtable.drain;
277 while (w.end != 0) _ = try drainFn(w, &.{""}, 1);
278}
279
280/// Does nothing.
281pub fn noopFlush(w: *Writer) Error!void {
282 _ = w;
283}
284
285/// Calls `VTable.drain` but hides the last `preserve_length` bytes from the
286/// implementation, keeping them buffered.
287pub fn drainPreserve(w: *Writer, preserve_length: usize) Error!void {
288 const temp_end = w.end -| preserve_length;
289 const preserved = w.buffer[temp_end..w.end];
290 w.end = temp_end;
291 defer w.end += preserved.len;
292 assert(0 == try w.vtable.drain(w, &.{""}, 1));
293 assert(w.end <= temp_end + preserved.len);
294 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
295}
296
297pub fn unusedCapacitySlice(w: *const Writer) []u8 {
298 return w.buffer[w.end..];
299}
300
301pub fn unusedCapacityLen(w: *const Writer) usize {
302 return w.buffer.len - w.end;
303}
304
305/// Asserts the provided buffer has total capacity enough for `len`.
306///
307/// Advances the buffer end position by `len`.
308pub fn writableArray(w: *Writer, comptime len: usize) Error!*[len]u8 {
309 const big_slice = try w.writableSliceGreedy(len);
310 advance(w, len);
311 return big_slice[0..len];
312}
313
314/// Asserts the provided buffer has total capacity enough for `len`.
315///
316/// Advances the buffer end position by `len`.
317pub fn writableSlice(w: *Writer, len: usize) Error![]u8 {
318 const big_slice = try w.writableSliceGreedy(len);
319 advance(w, len);
320 return big_slice[0..len];
321}
322
323/// Asserts the provided buffer has total capacity enough for `minimum_length`.
324///
325/// Does not `advance` the buffer end position.
326///
327/// If `minimum_length` is zero, this is equivalent to `unusedCapacitySlice`.
328pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {
329 assert(w.buffer.len >= minimum_length);
330 while (w.buffer.len - w.end < minimum_length) {
331 assert(0 == try w.vtable.drain(w, &.{""}, 1));
332 } else {
333 @branchHint(.likely);
334 return w.buffer[w.end..];
41 }335 }
42}336}
43337
44pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) anyerror!void {338/// Asserts the provided buffer has total capacity enough for `minimum_length`
339/// and `preserve_length` combined.
340///
341/// Does not `advance` the buffer end position.
342///
343/// When draining the buffer, ensures that at least `preserve_length` bytes
344/// remain buffered.
345///
346/// If `preserve_length` is zero, this is equivalent to `writableSliceGreedy`.
347pub fn writableSliceGreedyPreserve(w: *Writer, preserve_length: usize, minimum_length: usize) Error![]u8 {
348 assert(w.buffer.len >= preserve_length + minimum_length);
349 while (w.buffer.len - w.end < minimum_length) {
350 try drainPreserve(w, preserve_length);
351 } else {
352 @branchHint(.likely);
353 return w.buffer[w.end..];
354 }
355}
356
357pub const WritableVectorIterator = struct {
358 first: []u8,
359 middle: []const []u8 = &.{},
360 last: []u8 = &.{},
361 index: usize = 0,
362
363 pub fn next(it: *WritableVectorIterator) ?[]u8 {
364 while (true) {
365 const i = it.index;
366 it.index += 1;
367 if (i == 0) {
368 if (it.first.len == 0) continue;
369 return it.first;
370 }
371 const middle_index = i - 1;
372 if (middle_index < it.middle.len) {
373 const middle = it.middle[middle_index];
374 if (middle.len == 0) continue;
375 return middle;
376 }
377 if (middle_index == it.middle.len) {
378 if (it.last.len == 0) continue;
379 return it.last;
380 }
381 return null;
382 }
383 }
384};
385
386pub const VectorWrapper = struct {
387 writer: Writer,
388 it: WritableVectorIterator,
389 pub const vtable: VTable = .{ .drain = fixedDrain };
390};
391
392pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {
393 if (w.vtable == &VectorWrapper.vtable) {
394 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
395 return wrapper.it;
396 }
397 return .{ .first = try writableSliceGreedy(w, 1) };
398}
399
400pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit) Error![]std.posix.iovec {
401 var it = try writableVectorIterator(w);
45 var i: usize = 0;402 var i: usize = 0;
46 while (i < n) : (i += 1) {403 var remaining = limit;
47 try self.writeAll(bytes);404 while (it.next()) |full_buffer| {
405 if (!remaining.nonzero()) break;
406 if (buffer.len - i == 0) break;
407 const buf = remaining.slice(full_buffer);
408 if (buf.len == 0) continue;
409 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
410 i += 1;
411 remaining = remaining.subtract(buf.len).?;
412 }
413 return buffer[0..i];
414}
415
416pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
417 _ = try writableSliceGreedy(w, n);
418}
419
420pub fn undo(w: *Writer, n: usize) void {
421 w.end -= n;
422 w.count -= n;
423}
424
425/// After calling `writableSliceGreedy`, this function tracks how many bytes
426/// were written to it.
427///
428/// This is not needed when using `writableSlice` or `writableArray`.
429pub fn advance(w: *Writer, n: usize) void {
430 const new_end = w.end + n;
431 assert(new_end <= w.buffer.len);
432 w.end = new_end;
433 w.count += n;
434}
435
436/// After calling `writableVector`, this function tracks how many bytes were
437/// written to it.
438pub fn advanceVector(w: *Writer, n: usize) usize {
439 w.count += n;
440 return consume(w, n);
441}
442
443/// The `data` parameter is mutable because this function needs to mutate the
444/// fields in order to handle partial writes from `VTable.writeSplat`.
445pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {
446 var index: usize = 0;
447 var truncate: usize = 0;
448 while (index < data.len) {
449 {
450 const untruncated = data[index];
451 data[index] = untruncated[truncate..];
452 defer data[index] = untruncated;
453 truncate += try w.writeVec(data[index..]);
454 }
455 while (index < data.len and truncate >= data[index].len) {
456 truncate -= data[index].len;
457 index += 1;
458 }
48 }459 }
49}460}
50461
51pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {462/// The `data` parameter is mutable because this function needs to mutate the
463/// fields in order to handle partial writes from `VTable.writeSplat`.
464pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void {
465 var index: usize = 0;
466 var truncate: usize = 0;
467 var remaining_splat = splat;
468 while (index + 1 < data.len) {
469 {
470 const untruncated = data[index];
471 data[index] = untruncated[truncate..];
472 defer data[index] = untruncated;
473 truncate += try w.writeSplat(data[index..], remaining_splat);
474 }
475 while (truncate >= data[index].len) {
476 if (index + 1 < data.len) {
477 truncate -= data[index].len;
478 index += 1;
479 } else {
480 const last = data[data.len - 1];
481 remaining_splat -= @divExact(truncate, last.len);
482 while (remaining_splat > 0) {
483 const n = try w.writeSplat(data[data.len - 1 ..][0..1], remaining_splat);
484 remaining_splat -= @divExact(n, last.len);
485 }
486 return;
487 }
488 }
489 }
490}
491
492pub fn write(w: *Writer, bytes: []const u8) Error!usize {
493 if (w.end + bytes.len <= w.buffer.len) {
494 @branchHint(.likely);
495 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
496 w.end += bytes.len;
497 w.count += bytes.len;
498 return bytes.len;
499 }
500 const n = try w.vtable.drain(w, &.{bytes}, 1);
501 w.count += n;
502 return n;
503}
504
505/// Asserts `buffer` capacity exceeds `preserve_length`.
506pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Error!usize {
507 assert(preserve_length <= w.buffer.len);
508 if (w.end + bytes.len <= w.buffer.len) {
509 @branchHint(.likely);
510 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
511 w.end += bytes.len;
512 w.count += bytes.len;
513 return bytes.len;
514 }
515 const temp_end = w.end -| preserve_length;
516 const preserved = w.buffer[temp_end..w.end];
517 w.end = temp_end;
518 defer w.end += preserved.len;
519 const n = try w.vtable.drain(w, &.{bytes}, 1);
520 w.count += n;
521 assert(w.end <= temp_end + preserved.len);
522 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
523 return n;
524}
525
526/// Calls `drain` as many times as necessary such that all of `bytes` are
527/// transferred.
528pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {
529 var index: usize = 0;
530 while (index < bytes.len) index += try w.write(bytes[index..]);
531}
532
533/// Calls `drain` as many times as necessary such that all of `bytes` are
534/// transferred.
535///
536/// When draining the buffer, ensures that at least `preserve_length` bytes
537/// remain buffered.
538///
539/// Asserts `buffer` capacity exceeds `preserve_length`.
540pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Error!void {
541 var index: usize = 0;
542 while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]);
543}
544
545pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {
546 try std.fmt.format(w, format, args);
547}
548
549/// Calls `drain` as many times as necessary such that `byte` is transferred.
550pub fn writeByte(w: *Writer, byte: u8) Error!void {
551 while (w.buffer.len - w.end == 0) {
552 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);
553 if (n > 0) {
554 w.count += 1;
555 return;
556 }
557 } else {
558 @branchHint(.likely);
559 w.buffer[w.end] = byte;
560 w.end += 1;
561 w.count += 1;
562 }
563}
564
565/// When draining the buffer, ensures that at least `preserve_length` bytes
566/// remain buffered.
567pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!void {
568 while (w.buffer.len - w.end == 0) {
569 try drainPreserve(w, preserve_length);
570 } else {
571 @branchHint(.likely);
572 w.buffer[w.end] = byte;
573 w.end += 1;
574 w.count += 1;
575 }
576}
577
578/// Writes the same byte many times, performing the underlying write call as
579/// many times as necessary.
580pub fn splatByteAll(w: *Writer, byte: u8, n: usize) Error!void {
581 var remaining: usize = n;
582 while (remaining > 0) remaining -= try w.splatByte(byte, remaining);
583}
584
585/// Writes the same byte many times, allowing short writes.
586///
587/// Does maximum of one underlying `VTable.drain`.
588pub fn splatByte(w: *Writer, byte: u8, n: usize) Error!usize {
589 return writeSplat(w, &.{&.{byte}}, n);
590}
591
592/// Writes the same slice many times, performing the underlying write call as
593/// many times as necessary.
594pub fn splatBytesAll(w: *Writer, bytes: []const u8, splat: usize) Error!void {
595 var remaining_bytes: usize = bytes.len * splat;
596 remaining_bytes -= try w.splatBytes(bytes, splat);
597 while (remaining_bytes > 0) {
598 const leftover = remaining_bytes % bytes.len;
599 const buffers: [2][]const u8 = .{ bytes[bytes.len - leftover ..], bytes };
600 remaining_bytes -= try w.splatBytes(&buffers, splat);
601 }
602}
603
604/// Writes the same slice many times, allowing short writes.
605///
606/// Does maximum of one underlying `VTable.writeSplat`.
607pub fn splatBytes(w: *Writer, bytes: []const u8, n: usize) Error!usize {
608 return writeSplat(w, &.{bytes}, n);
609}
610
611/// Asserts the `buffer` was initialized with a capacity of at least `@sizeOf(T)` bytes.
612pub inline fn writeInt(w: *Writer, comptime T: type, value: T, endian: std.builtin.Endian) Error!void {
52 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;613 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;
53 mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);614 std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
54 return self.writeAll(&bytes);615 return w.writeAll(&bytes);
55}616}
56617
57pub fn writeStruct(self: Self, value: anytype) anyerror!void {618pub fn writeStruct(w: *Writer, value: anytype) Error!void {
58 // Only extern and packed structs have defined in-memory layout.619 // Only extern and packed structs have defined in-memory layout.
59 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);620 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
60 return self.writeAll(mem.asBytes(&value));621 return w.writeAll(std.mem.asBytes(&value));
61}622}
62623
63pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void {624/// The function is inline to avoid the dead code in case `endian` is
64 // TODO: make sure this value is not a reference type625/// comptime-known and matches host endianness.
626/// TODO: make sure this value is not a reference type
627pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
65 if (native_endian == endian) {628 if (native_endian == endian) {
66 return self.writeStruct(value);629 return w.writeStruct(value);
67 } else {630 } else {
68 var copy = value;631 var copy = value;
69 mem.byteSwapAllFields(@TypeOf(value), &copy);632 std.mem.byteSwapAllFields(@TypeOf(value), &copy);
70 return self.writeStruct(copy);633 return w.writeStruct(copy);
634 }
635}
636
637pub inline fn writeSliceEndian(
638 w: *Writer,
639 Elem: type,
640 slice: []const Elem,
641 endian: std.builtin.Endian,
642) Error!void {
643 if (native_endian == endian) {
644 return writeAll(w, @ptrCast(slice));
645 } else {
646 return w.writeArraySwap(w, Elem, slice);
647 }
648}
649
650/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
651pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {
652 // copy to storage first, then swap in place
653 _ = w;
654 _ = slice;
655 @panic("TODO");
656}
657
658/// Unlike `writeSplat` and `writeVec`, this function will call into `VTable`
659/// even if there is enough buffer capacity for the file contents.
660///
661/// Although it would be possible to eliminate `error.Unimplemented` from the
662/// error set by reading directly into the buffer in such case, this is not
663/// done because it is more efficient to do it higher up the call stack so that
664/// the error does not occur with each write.
665///
666/// See `sendFileReading` for an alternative that does not have
667/// `error.Unimplemented` in the error set.
668pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
669 return w.vtable.sendFile(w, file_reader, limit);
670}
671
672/// Returns how many bytes from `header` and `file_reader` were consumed.
673pub fn sendFileHeader(
674 w: *Writer,
675 header: []const u8,
676 file_reader: *File.Reader,
677 limit: Limit,
678) FileError!usize {
679 const new_end = w.end + header.len;
680 if (new_end <= w.buffer.len) {
681 @memcpy(w.buffer[w.end..][0..header.len], header);
682 w.end = new_end;
683 w.count += header.len;
684 return header.len + try w.vtable.sendFile(w, file_reader, limit);
685 }
686 const buffered_contents = limit.slice(file_reader.interface.buffered());
687 const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1);
688 w.count += n;
689 file_reader.interface.toss(n - header.len);
690 return n;
691}
692
693/// Asserts nonzero buffer capacity.
694pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) FileReadingError!usize {
695 const dest = limit.slice(try w.writableSliceGreedy(1));
696 const n = try file_reader.read(dest);
697 w.advance(n);
698 return n;
699}
700
701pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {
702 var remaining = @intFromEnum(limit);
703 while (remaining > 0) {
704 const n = sendFile(w, file_reader, .limited(remaining)) catch |err| switch (err) {
705 error.EndOfStream => break,
706 error.Unimplemented => {
707 file_reader.mode = file_reader.mode.toReading();
708 remaining -= try w.sendFileReadingAll(file_reader, .limited(remaining));
709 break;
710 },
711 else => |e| return e,
712 };
713 remaining -= n;
714 }
715 return @intFromEnum(limit) - remaining;
716}
717
718/// Equivalent to `sendFileAll` but uses direct `pread` and `read` calls on
719/// `file` rather than `sendFile`. This is generally used as a fallback when
720/// the underlying implementation returns `error.Unimplemented`, which is why
721/// that error code does not appear in this function's error set.
722///
723/// Asserts nonzero buffer capacity.
724pub fn sendFileReadingAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {
725 var remaining = @intFromEnum(limit);
726 while (remaining > 0) {
727 remaining -= sendFileReading(w, file_reader, .limited(remaining)) catch |err| switch (err) {
728 error.EndOfStream => break,
729 else => |e| return e,
730 };
71 }731 }
732 return @intFromEnum(limit) - remaining;
72}733}
73734
74pub fn writeFile(self: Self, file: std.fs.File) anyerror!void {735pub fn alignBuffer(
75 // TODO: figure out how to adjust std lib abstractions so that this ends up736 w: *Writer,
76 // doing sendfile or maybe even copy_file_range under the right conditions.737 buffer: []const u8,
77 var buf: [4000]u8 = undefined;738 width: usize,
739 alignment: std.fmt.Alignment,
740 fill: u8,
741) Error!void {
742 const padding = if (buffer.len < width) width - buffer.len else 0;
743 if (padding == 0) {
744 @branchHint(.likely);
745 return w.writeAll(buffer);
746 }
747 switch (alignment) {
748 .left => {
749 try w.writeAll(buffer);
750 try w.splatByteAll(fill, padding);
751 },
752 .center => {
753 const left_padding = padding / 2;
754 const right_padding = (padding + 1) / 2;
755 try w.splatByteAll(fill, left_padding);
756 try w.writeAll(buffer);
757 try w.splatByteAll(fill, right_padding);
758 },
759 .right => {
760 try w.splatByteAll(fill, padding);
761 try w.writeAll(buffer);
762 },
763 }
764}
765
766pub fn alignBufferOptions(w: *Writer, buffer: []const u8, options: std.fmt.Options) Error!void {
767 return w.alignBuffer(buffer, options.width orelse buffer.len, options.alignment, options.fill);
768}
769
770pub fn printAddress(w: *Writer, value: anytype) Error!void {
771 const T = @TypeOf(value);
772 switch (@typeInfo(T)) {
773 .pointer => |info| {
774 try w.writeAll(@typeName(info.child) ++ "@");
775 if (info.size == .slice)
776 try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{})
777 else
778 try w.printIntOptions(@intFromPtr(value), 16, .lower, .{});
779 return;
780 },
781 .optional => |info| {
782 if (@typeInfo(info.child) == .pointer) {
783 try w.writeAll(@typeName(info.child) ++ "@");
784 try w.printIntOptions(@intFromPtr(value), 16, .lower, .{});
785 return;
786 }
787 },
788 else => {},
789 }
790
791 @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier");
792}
793
794pub fn printValue(
795 w: *Writer,
796 comptime fmt: []const u8,
797 options: std.fmt.Options,
798 value: anytype,
799 max_depth: usize,
800) Error!void {
801 const T = @TypeOf(value);
802
803 if (comptime std.mem.eql(u8, fmt, "*")) {
804 return w.printAddress(value);
805 }
806
807 const is_any = comptime std.mem.eql(u8, fmt, ANY);
808 if (!is_any and std.meta.hasMethod(T, "format")) {
809 if (fmt.len > 0 and fmt[0] == 'f') {
810 return value.format(w, fmt[1..]);
811 } else if (fmt.len == 0) {
812 // after 0.15.0 is tagged, delete the hasMethod condition and this compile error
813 @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
814 }
815 }
816
817 switch (@typeInfo(T)) {
818 .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value),
819 .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value),
820 .bool => {
821 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
822 return w.alignBufferOptions(if (value) "true" else "false", options);
823 },
824 .void => {
825 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
826 return w.alignBufferOptions("void", options);
827 },
828 .optional => {
829 const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '?')
830 stripOptionalOrErrorUnionSpec(fmt)
831 else if (is_any)
832 ANY
833 else
834 @compileError("cannot print optional without a specifier (i.e. {?} or {any})");
835 if (value) |payload| {
836 return w.printValue(remaining_fmt, options, payload, max_depth);
837 } else {
838 return w.alignBufferOptions("null", options);
839 }
840 },
841 .error_union => {
842 const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '!')
843 stripOptionalOrErrorUnionSpec(fmt)
844 else if (is_any)
845 ANY
846 else
847 @compileError("cannot print error union without a specifier (i.e. {!} or {any})");
848 if (value) |payload| {
849 return w.printValue(remaining_fmt, options, payload, max_depth);
850 } else |err| {
851 return w.printValue("", options, err, max_depth);
852 }
853 },
854 .error_set => {
855 if (fmt.len == 1 and fmt[0] == 's') return w.writeAll(@errorName(value));
856 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
857 try printErrorSet(w, value);
858 },
859 .@"enum" => {
860 if (fmt.len == 1 and fmt[0] == 's') {
861 try w.writeAll(@tagName(value));
862 return;
863 }
864 if (!is_any) {
865 if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth);
866 return printValue(w, ANY, options, value, max_depth);
867 }
868 const enum_info = @typeInfo(T).@"enum";
869 if (enum_info.is_exhaustive) {
870 var vecs: [3][]const u8 = .{ @typeName(T), ".", @tagName(value) };
871 try w.writeVecAll(&vecs);
872 return;
873 }
874 try w.writeAll(@typeName(T));
875 @setEvalBranchQuota(3 * enum_info.fields.len);
876 inline for (enum_info.fields) |field| {
877 if (@intFromEnum(value) == field.value) {
878 try w.writeAll(".");
879 try w.writeAll(@tagName(value));
880 return;
881 }
882 }
883 try w.writeByte('(');
884 try w.printValue(ANY, options, @intFromEnum(value), max_depth);
885 try w.writeByte(')');
886 },
887 .@"union" => |info| {
888 if (!is_any) {
889 if (fmt.len != 0) invalidFmtError(fmt, value);
890 return printValue(w, ANY, options, value, max_depth);
891 }
892 try w.writeAll(@typeName(T));
893 if (max_depth == 0) {
894 try w.writeAll("{ ... }");
895 return;
896 }
897 if (info.tag_type) |UnionTagType| {
898 try w.writeAll("{ .");
899 try w.writeAll(@tagName(@as(UnionTagType, value)));
900 try w.writeAll(" = ");
901 inline for (info.fields) |u_field| {
902 if (value == @field(UnionTagType, u_field.name)) {
903 try w.printValue(ANY, options, @field(value, u_field.name), max_depth - 1);
904 }
905 }
906 try w.writeAll(" }");
907 } else {
908 try w.writeByte('@');
909 try w.printIntOptions(@intFromPtr(&value), 16, .lower, options);
910 }
911 },
912 .@"struct" => |info| {
913 if (!is_any) {
914 if (fmt.len != 0) invalidFmtError(fmt, value);
915 return printValue(w, ANY, options, value, max_depth);
916 }
917 if (info.is_tuple) {
918 // Skip the type and field names when formatting tuples.
919 if (max_depth == 0) {
920 try w.writeAll("{ ... }");
921 return;
922 }
923 try w.writeAll("{");
924 inline for (info.fields, 0..) |f, i| {
925 if (i == 0) {
926 try w.writeAll(" ");
927 } else {
928 try w.writeAll(", ");
929 }
930 try w.printValue(ANY, options, @field(value, f.name), max_depth - 1);
931 }
932 try w.writeAll(" }");
933 return;
934 }
935 try w.writeAll(@typeName(T));
936 if (max_depth == 0) {
937 try w.writeAll("{ ... }");
938 return;
939 }
940 try w.writeAll("{");
941 inline for (info.fields, 0..) |f, i| {
942 if (i == 0) {
943 try w.writeAll(" .");
944 } else {
945 try w.writeAll(", .");
946 }
947 try w.writeAll(f.name);
948 try w.writeAll(" = ");
949 try w.printValue(ANY, options, @field(value, f.name), max_depth - 1);
950 }
951 try w.writeAll(" }");
952 },
953 .pointer => |ptr_info| switch (ptr_info.size) {
954 .one => switch (@typeInfo(ptr_info.child)) {
955 .array, .@"enum", .@"union", .@"struct" => {
956 return w.printValue(fmt, options, value.*, max_depth);
957 },
958 else => {
959 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
960 try w.writeVecAll(&buffers);
961 try w.printIntOptions(@intFromPtr(value), 16, .lower, options);
962 return;
963 },
964 },
965 .many, .c => {
966 if (ptr_info.sentinel() != null)
967 return w.printValue(fmt, options, std.mem.span(value), max_depth);
968 if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8)
969 return w.alignBufferOptions(std.mem.span(value), options);
970 if (!is_any and fmt.len == 0)
971 @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
972 if (!is_any and fmt.len != 0)
973 invalidFmtError(fmt, value);
974 try w.printAddress(value);
975 },
976 .slice => {
977 if (!is_any and fmt.len == 0)
978 @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");
979 if (max_depth == 0)
980 return w.writeAll("{ ... }");
981 if (ptr_info.child == u8) switch (fmt.len) {
982 1 => switch (fmt[0]) {
983 's' => return w.alignBufferOptions(value, options),
984 'x' => return w.printHex(value, .lower),
985 'X' => return w.printHex(value, .upper),
986 else => {},
987 },
988 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') {
989 return w.printBase64(value);
990 },
991 else => {},
992 };
993 try w.writeAll("{ ");
994 for (value, 0..) |elem, i| {
995 try w.printValue(fmt, options, elem, max_depth - 1);
996 if (i != value.len - 1) {
997 try w.writeAll(", ");
998 }
999 }
1000 try w.writeAll(" }");
1001 },
1002 },
1003 .array => |info| {
1004 if (fmt.len == 0)
1005 @compileError("cannot format array without a specifier (i.e. {s} or {any})");
1006 if (max_depth == 0) {
1007 return w.writeAll("{ ... }");
1008 }
1009 if (info.child == u8) {
1010 if (fmt[0] == 's') {
1011 return w.alignBufferOptions(&value, options);
1012 } else if (fmt[0] == 'x') {
1013 return w.printHex(&value, .lower);
1014 } else if (fmt[0] == 'X') {
1015 return w.printHex(&value, .upper);
1016 }
1017 }
1018 try w.writeAll("{ ");
1019 for (value, 0..) |elem, i| {
1020 try w.printValue(fmt, options, elem, max_depth - 1);
1021 if (i < value.len - 1) {
1022 try w.writeAll(", ");
1023 }
1024 }
1025 try w.writeAll(" }");
1026 },
1027 .vector => |info| {
1028 if (max_depth == 0) {
1029 return w.writeAll("{ ... }");
1030 }
1031 try w.writeAll("{ ");
1032 var i: usize = 0;
1033 while (i < info.len) : (i += 1) {
1034 try w.printValue(fmt, options, value[i], max_depth - 1);
1035 if (i < info.len - 1) {
1036 try w.writeAll(", ");
1037 }
1038 }
1039 try w.writeAll(" }");
1040 },
1041 .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),
1042 .type => {
1043 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1044 return w.alignBufferOptions(@typeName(value), options);
1045 },
1046 .enum_literal => {
1047 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1048 const buffer = [_]u8{'.'} ++ @tagName(value);
1049 return w.alignBufferOptions(buffer, options);
1050 },
1051 .null => {
1052 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1053 return w.alignBufferOptions("null", options);
1054 },
1055 else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"),
1056 }
1057}
1058
1059fn printErrorSet(w: *Writer, error_set: anyerror) Error!void {
1060 var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) };
1061 try w.writeVecAll(&vecs);
1062}
1063
1064pub fn printInt(
1065 w: *Writer,
1066 comptime fmt: []const u8,
1067 options: std.fmt.Options,
1068 value: anytype,
1069) Error!void {
1070 const int_value = if (@TypeOf(value) == comptime_int) blk: {
1071 const Int = std.math.IntFittingRange(value, value);
1072 break :blk @as(Int, value);
1073 } else value;
1074
1075 switch (fmt.len) {
1076 0 => return w.printIntOptions(int_value, 10, .lower, options),
1077 1 => switch (fmt[0]) {
1078 'd' => return w.printIntOptions(int_value, 10, .lower, options),
1079 'c' => {
1080 if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) {
1081 return w.printAsciiChar(@as(u8, int_value), options);
1082 } else {
1083 @compileError("cannot print integer that is larger than 8 bits as an ASCII character");
1084 }
1085 },
1086 'u' => {
1087 if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) {
1088 return w.printUnicodeCodepoint(@as(u21, int_value), options);
1089 } else {
1090 @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence");
1091 }
1092 },
1093 'b' => return w.printIntOptions(int_value, 2, .lower, options),
1094 'x' => return w.printIntOptions(int_value, 16, .lower, options),
1095 'X' => return w.printIntOptions(int_value, 16, .upper, options),
1096 'o' => return w.printIntOptions(int_value, 8, .lower, options),
1097 'B' => return w.printByteSize(int_value, .decimal, options),
1098 'D' => return w.printDuration(int_value, options),
1099 else => invalidFmtError(fmt, value),
1100 },
1101 2 => {
1102 if (fmt[0] == 'B' and fmt[1] == 'i') {
1103 return w.printByteSize(int_value, .binary, options);
1104 } else {
1105 invalidFmtError(fmt, value);
1106 }
1107 },
1108 else => invalidFmtError(fmt, value),
1109 }
1110 comptime unreachable;
1111}
1112
1113pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void {
1114 return w.alignBufferOptions(@as(*const [1]u8, &c), options);
1115}
1116
1117pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void {
1118 return w.alignBufferOptions(bytes, options);
1119}
1120
1121pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void {
1122 var buf: [4]u8 = undefined;
1123 const len = try std.unicode.utf8Encode(c, &buf);
1124 return w.alignBufferOptions(buf[0..len], options);
1125}
1126
1127pub fn printIntOptions(
1128 w: *Writer,
1129 value: anytype,
1130 base: u8,
1131 case: std.fmt.Case,
1132 options: std.fmt.Options,
1133) Error!void {
1134 assert(base >= 2);
1135
1136 const int_value = if (@TypeOf(value) == comptime_int) blk: {
1137 const Int = std.math.IntFittingRange(value, value);
1138 break :blk @as(Int, value);
1139 } else value;
1140
1141 const value_info = @typeInfo(@TypeOf(int_value)).int;
1142
1143 // The type must have the same size as `base` or be wider in order for the
1144 // division to work
1145 const min_int_bits = comptime @max(value_info.bits, 8);
1146 const MinInt = std.meta.Int(.unsigned, min_int_bits);
1147
1148 const abs_value = @abs(int_value);
1149 // The worst case in terms of space needed is base 2, plus 1 for the sign
1150 var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined;
1151
1152 var a: MinInt = abs_value;
1153 var index: usize = buf.len;
1154
1155 if (base == 10) {
1156 while (a >= 100) : (a = @divTrunc(a, 100)) {
1157 index -= 2;
1158 buf[index..][0..2].* = std.fmt.digits2(@intCast(a % 100));
1159 }
1160
1161 if (a < 10) {
1162 index -= 1;
1163 buf[index] = '0' + @as(u8, @intCast(a));
1164 } else {
1165 index -= 2;
1166 buf[index..][0..2].* = std.fmt.digits2(@intCast(a));
1167 }
1168 } else {
1169 while (true) {
1170 const digit = a % base;
1171 index -= 1;
1172 buf[index] = std.fmt.digitToChar(@intCast(digit), case);
1173 a /= base;
1174 if (a == 0) break;
1175 }
1176 }
1177
1178 if (value_info.signedness == .signed) {
1179 if (value < 0) {
1180 // Negative integer
1181 index -= 1;
1182 buf[index] = '-';
1183 } else if (options.width == null or options.width.? == 0) {
1184 // Positive integer, omit the plus sign
1185 } else {
1186 // Positive integer
1187 index -= 1;
1188 buf[index] = '+';
1189 }
1190 }
1191
1192 return w.alignBufferOptions(buf[index..], options);
1193}
1194
1195pub fn printFloat(
1196 w: *Writer,
1197 comptime fmt: []const u8,
1198 options: std.fmt.Options,
1199 value: anytype,
1200) Error!void {
1201 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
1202
1203 if (fmt.len > 1) invalidFmtError(fmt, value);
1204 switch (if (fmt.len == 0) 'e' else fmt[0]) {
1205 'e' => {
1206 const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) {
1207 error.BufferTooSmall => "(float)",
1208 };
1209 return w.alignBufferOptions(s, options);
1210 },
1211 'd' => {
1212 const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) {
1213 error.BufferTooSmall => "(float)",
1214 };
1215 return w.alignBufferOptions(s, options);
1216 },
1217 'x' => {
1218 var sub_bw: Writer = .fixed(&buf);
1219 sub_bw.printFloatHexadecimal(value, options.precision) catch unreachable;
1220 return w.alignBufferOptions(sub_bw.buffered(), options);
1221 },
1222 else => invalidFmtError(fmt, value),
1223 }
1224}
1225
1226pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void {
1227 if (std.math.signbit(value)) try w.writeByte('-');
1228 if (std.math.isNan(value)) return w.writeAll("nan");
1229 if (std.math.isInf(value)) return w.writeAll("inf");
1230
1231 const T = @TypeOf(value);
1232 const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
1233
1234 const mantissa_bits = std.math.floatMantissaBits(T);
1235 const fractional_bits = std.math.floatFractionalBits(T);
1236 const exponent_bits = std.math.floatExponentBits(T);
1237 const mantissa_mask = (1 << mantissa_bits) - 1;
1238 const exponent_mask = (1 << exponent_bits) - 1;
1239 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
1240
1241 const as_bits: TU = @bitCast(value);
1242 var mantissa = as_bits & mantissa_mask;
1243 var exponent: i32 = @as(u16, @truncate((as_bits >> mantissa_bits) & exponent_mask));
1244
1245 const is_denormal = exponent == 0 and mantissa != 0;
1246 const is_zero = exponent == 0 and mantissa == 0;
1247
1248 if (is_zero) {
1249 // Handle this case here to simplify the logic below.
1250 try w.writeAll("0x0");
1251 if (opt_precision) |precision| {
1252 if (precision > 0) {
1253 try w.writeAll(".");
1254 try w.splatByteAll('0', precision);
1255 }
1256 } else {
1257 try w.writeAll(".0");
1258 }
1259 try w.writeAll("p0");
1260 return;
1261 }
1262
1263 if (is_denormal) {
1264 // Adjust the exponent for printing.
1265 exponent += 1;
1266 } else {
1267 if (fractional_bits == mantissa_bits)
1268 mantissa |= 1 << fractional_bits; // Add the implicit integer bit.
1269 }
1270
1271 const mantissa_digits = (fractional_bits + 3) / 4;
1272 // Fill in zeroes to round the fraction width to a multiple of 4.
1273 mantissa <<= mantissa_digits * 4 - fractional_bits;
1274
1275 if (opt_precision) |precision| {
1276 // Round if needed.
1277 if (precision < mantissa_digits) {
1278 // We always have at least 4 extra bits.
1279 var extra_bits = (mantissa_digits - precision) * 4;
1280 // The result LSB is the Guard bit, we need two more (Round and
1281 // Sticky) to round the value.
1282 while (extra_bits > 2) {
1283 mantissa = (mantissa >> 1) | (mantissa & 1);
1284 extra_bits -= 1;
1285 }
1286 // Round to nearest, tie to even.
1287 mantissa |= @intFromBool(mantissa & 0b100 != 0);
1288 mantissa += 1;
1289 // Drop the excess bits.
1290 mantissa >>= 2;
1291 // Restore the alignment.
1292 mantissa <<= @as(std.math.Log2Int(TU), @intCast((mantissa_digits - precision) * 4));
1293
1294 const overflow = mantissa & (1 << 1 + mantissa_digits * 4) != 0;
1295 // Prefer a normalized result in case of overflow.
1296 if (overflow) {
1297 mantissa >>= 1;
1298 exponent += 1;
1299 }
1300 }
1301 }
1302
1303 // +1 for the decimal part.
1304 var buf: [1 + mantissa_digits]u8 = undefined;
1305 assert(std.fmt.printInt(&buf, mantissa, 16, .lower, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len);
1306
1307 try w.writeAll("0x");
1308 try w.writeByte(buf[0]);
1309 const trimmed = std.mem.trimRight(u8, buf[1..], "0");
1310 if (opt_precision) |precision| {
1311 if (precision > 0) try w.writeAll(".");
1312 } else if (trimmed.len > 0) {
1313 try w.writeAll(".");
1314 }
1315 try w.writeAll(trimmed);
1316 // Add trailing zeros if explicitly requested.
1317 if (opt_precision) |precision| if (precision > 0) {
1318 if (precision > trimmed.len)
1319 try w.splatByteAll('0', precision - trimmed.len);
1320 };
1321 try w.writeAll("p");
1322 try w.printIntOptions(exponent - exponent_bias, 10, .lower, .{});
1323}
1324
1325pub const ByteSizeUnits = enum {
1326 /// This formatter represents the number as multiple of 1000 and uses the SI
1327 /// measurement units (kB, MB, GB, ...).
1328 decimal,
1329 /// This formatter represents the number as multiple of 1024 and uses the IEC
1330 /// measurement units (KiB, MiB, GiB, ...).
1331 binary,
1332};
1333
1334/// Format option `precision` is ignored when `value` is less than 1kB
1335pub fn printByteSize(
1336 w: *std.io.Writer,
1337 value: u64,
1338 comptime units: ByteSizeUnits,
1339 options: std.fmt.Options,
1340) Error!void {
1341 if (value == 0) return w.alignBufferOptions("0B", options);
1342 // The worst case in terms of space needed is 32 bytes + 3 for the suffix.
1343 var buf: [std.fmt.float.min_buffer_size + 3]u8 = undefined;
1344
1345 const mags_si = " kMGTPEZY";
1346 const mags_iec = " KMGTPEZY";
1347
1348 const log2 = std.math.log2(value);
1349 const base = switch (units) {
1350 .decimal => 1000,
1351 .binary => 1024,
1352 };
1353 const magnitude = switch (units) {
1354 .decimal => @min(log2 / comptime std.math.log2(1000), mags_si.len - 1),
1355 .binary => @min(log2 / 10, mags_iec.len - 1),
1356 };
1357 const new_value = std.math.lossyCast(f64, value) / std.math.pow(f64, std.math.lossyCast(f64, base), std.math.lossyCast(f64, magnitude));
1358 const suffix = switch (units) {
1359 .decimal => mags_si[magnitude],
1360 .binary => mags_iec[magnitude],
1361 };
1362
1363 const s = switch (magnitude) {
1364 0 => buf[0..std.fmt.printInt(&buf, value, 10, .lower, .{})],
1365 else => std.fmt.float.render(&buf, new_value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) {
1366 error.BufferTooSmall => unreachable,
1367 },
1368 };
1369
1370 var i: usize = s.len;
1371 if (suffix == ' ') {
1372 buf[i] = 'B';
1373 i += 1;
1374 } else switch (units) {
1375 .decimal => {
1376 buf[i..][0..2].* = [_]u8{ suffix, 'B' };
1377 i += 2;
1378 },
1379 .binary => {
1380 buf[i..][0..3].* = [_]u8{ suffix, 'i', 'B' };
1381 i += 3;
1382 },
1383 }
1384
1385 return w.alignBufferOptions(buf[0..i], options);
1386}
1387
1388// This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948
1389const ANY = "any";
1390
1391fn stripOptionalOrErrorUnionSpec(comptime fmt: []const u8) []const u8 {
1392 return if (std.mem.eql(u8, fmt[1..], ANY))
1393 ANY
1394 else
1395 fmt[1..];
1396}
1397
1398pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn {
1399 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
1400}
1401
1402pub fn printDurationSigned(w: *Writer, ns: i64) Error!void {
1403 if (ns < 0) try w.writeByte('-');
1404 return w.printDurationUnsigned(@abs(ns));
1405}
1406
1407pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
1408 var ns_remaining = ns;
1409 inline for (.{
1410 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
1411 .{ .ns = std.time.ns_per_week, .sep = 'w' },
1412 .{ .ns = std.time.ns_per_day, .sep = 'd' },
1413 .{ .ns = std.time.ns_per_hour, .sep = 'h' },
1414 .{ .ns = std.time.ns_per_min, .sep = 'm' },
1415 }) |unit| {
1416 if (ns_remaining >= unit.ns) {
1417 const units = ns_remaining / unit.ns;
1418 try w.printIntOptions(units, 10, .lower, .{});
1419 try w.writeByte(unit.sep);
1420 ns_remaining -= units * unit.ns;
1421 if (ns_remaining == 0) return;
1422 }
1423 }
1424
1425 inline for (.{
1426 .{ .ns = std.time.ns_per_s, .sep = "s" },
1427 .{ .ns = std.time.ns_per_ms, .sep = "ms" },
1428 .{ .ns = std.time.ns_per_us, .sep = "us" },
1429 }) |unit| {
1430 const kunits = ns_remaining * 1000 / unit.ns;
1431 if (kunits >= 1000) {
1432 try w.printIntOptions(kunits / 1000, 10, .lower, .{});
1433 const frac = kunits % 1000;
1434 if (frac > 0) {
1435 // Write up to 3 decimal places
1436 var decimal_buf = [_]u8{ '.', 0, 0, 0 };
1437 var inner: Writer = .fixed(decimal_buf[1..]);
1438 inner.printIntOptions(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable;
1439 var end: usize = 4;
1440 while (end > 1) : (end -= 1) {
1441 if (decimal_buf[end - 1] != '0') break;
1442 }
1443 try w.writeAll(decimal_buf[0..end]);
1444 }
1445 return w.writeAll(unit.sep);
1446 }
1447 }
1448
1449 try w.printIntOptions(ns_remaining, 10, .lower, .{});
1450 try w.writeAll("ns");
1451}
1452
1453/// Writes number of nanoseconds according to its signed magnitude:
1454/// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s`
1455/// `nanoseconds` must be an integer that coerces into `u64` or `i64`.
1456pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void {
1457 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24
1458 var buf: [24]u8 = undefined;
1459 var sub_bw: Writer = .fixed(&buf);
1460 switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) {
1461 .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable,
1462 .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable,
1463 }
1464 return w.alignBufferOptions(sub_bw.buffered(), options);
1465}
1466
1467pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void {
1468 const charset = switch (case) {
1469 .upper => "0123456789ABCDEF",
1470 .lower => "0123456789abcdef",
1471 };
1472 for (bytes) |c| {
1473 try w.writeByte(charset[c >> 4]);
1474 try w.writeByte(charset[c & 15]);
1475 }
1476}
1477
1478pub fn printBase64(w: *Writer, bytes: []const u8) Error!void {
1479 var chunker = std.mem.window(u8, bytes, 3, 3);
1480 var temp: [5]u8 = undefined;
1481 while (chunker.next()) |chunk| {
1482 try w.writeAll(std.base64.standard.Encoder.encode(&temp, chunk));
1483 }
1484}
1485
1486/// Write a single unsigned integer as LEB128 to the given writer.
1487pub fn writeUleb128(w: *Writer, value: anytype) Error!void {
1488 try w.writeLeb128(switch (@typeInfo(@TypeOf(value))) {
1489 .comptime_int => @as(std.math.IntFittingRange(0, @abs(value)), value),
1490 .int => |value_info| switch (value_info.signedness) {
1491 .signed => @as(@Type(.{ .int = .{ .signedness = .unsigned, .bits = value_info.bits -| 1 } }), @intCast(value)),
1492 .unsigned => value,
1493 },
1494 else => comptime unreachable,
1495 });
1496}
1497
1498/// Write a single signed integer as LEB128 to the given writer.
1499pub fn writeSleb128(w: *Writer, value: anytype) Error!void {
1500 try w.writeLeb128(switch (@typeInfo(@TypeOf(value))) {
1501 .comptime_int => @as(std.math.IntFittingRange(@min(value, -1), @max(0, value)), value),
1502 .int => |value_info| switch (value_info.signedness) {
1503 .signed => value,
1504 .unsigned => @as(@Type(.{ .int = .{ .signedness = .signed, .bits = value_info.bits + 1 } }), value),
1505 },
1506 else => comptime unreachable,
1507 });
1508}
1509
1510/// Write a single integer as LEB128 to the given writer.
1511pub fn writeLeb128(w: *Writer, value: anytype) Error!void {
1512 const value_info = @typeInfo(@TypeOf(value)).int;
1513 try w.writeMultipleOf7Leb128(@as(@Type(.{ .int = .{
1514 .signedness = value_info.signedness,
1515 .bits = std.mem.alignForwardAnyAlign(u16, value_info.bits, 7),
1516 } }), value));
1517}
1518
1519fn writeMultipleOf7Leb128(w: *Writer, value: anytype) Error!void {
1520 const value_info = @typeInfo(@TypeOf(value)).int;
1521 comptime assert(value_info.bits % 7 == 0);
1522 var remaining = value;
78 while (true) {1523 while (true) {
79 const n = try file.readAll(&buf);1524 const buffer: []packed struct(u8) { bits: u7, more: bool } = @ptrCast(try w.writableSliceGreedy(1));
80 try self.writeAll(buf[0..n]);1525 for (buffer, 1..) |*byte, len| {
81 if (n < buf.len) return;1526 const more = switch (value_info.signedness) {
1527 .signed => remaining >> 6 != remaining >> (value_info.bits - 1),
1528 .unsigned => remaining > std.math.maxInt(u7),
1529 };
1530 byte.* = if (@inComptime()) @typeInfo(@TypeOf(buffer)).pointer.child{
1531 .bits = @bitCast(@as(@Type(.{ .int = .{
1532 .signedness = value_info.signedness,
1533 .bits = 7,
1534 } }), @truncate(remaining))),
1535 .more = more,
1536 } else .{
1537 .bits = @bitCast(@as(@Type(.{ .int = .{
1538 .signedness = value_info.signedness,
1539 .bits = 7,
1540 } }), @truncate(remaining))),
1541 .more = more,
1542 };
1543 if (value_info.bits > 7) remaining >>= 7;
1544 if (!more) return w.advance(len);
1545 }
1546 w.advance(buffer.len);
1547 }
1548}
1549
1550test "formatValue max_depth" {
1551 const Vec2 = struct {
1552 const SelfType = @This();
1553 x: f32,
1554 y: f32,
1555
1556 pub fn format(
1557 self: SelfType,
1558 comptime fmt: []const u8,
1559 options: std.fmt.Options,
1560 w: *Writer,
1561 ) Error!void {
1562 _ = options;
1563 if (fmt.len == 0) {
1564 return w.print("({d:.3},{d:.3})", .{ self.x, self.y });
1565 } else {
1566 @compileError("unknown format string: '" ++ fmt ++ "'");
1567 }
1568 }
1569 };
1570 const E = enum {
1571 One,
1572 Two,
1573 Three,
1574 };
1575 const TU = union(enum) {
1576 const SelfType = @This();
1577 float: f32,
1578 int: u32,
1579 ptr: ?*SelfType,
1580 };
1581 const S = struct {
1582 const SelfType = @This();
1583 a: ?*SelfType,
1584 tu: TU,
1585 e: E,
1586 vec: Vec2,
1587 };
1588
1589 var inst = S{
1590 .a = null,
1591 .tu = TU{ .ptr = null },
1592 .e = E.Two,
1593 .vec = Vec2{ .x = 10.2, .y = 2.22 },
1594 };
1595 inst.a = &inst;
1596 inst.tu.ptr = &inst.tu;
1597
1598 var buf: [1000]u8 = undefined;
1599 var w: Writer = .fixed(&buf);
1600 try w.printValue("", .{}, inst, 0);
1601 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ ... }", w.buffered());
1602
1603 w.reset();
1604 try w.printValue("", .{}, inst, 1);
1605 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ ... }, .tu = io.Writer.test.printValue max_depth.TU{ ... }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }", w.buffered());
1606
1607 w.reset();
1608 try w.printValue("", .{}, inst, 2);
1609 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ ... }, .tu = io.Writer.test.printValue max_depth.TU{ ... }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }, .tu = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ ... } }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }", w.buffered());
1610
1611 w.reset();
1612 try w.printValue("", .{}, inst, 3);
1613 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ ... }, .tu = io.Writer.test.printValue max_depth.TU{ ... }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }, .tu = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ ... } }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }, .tu = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ ... } } }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }", w.buffered());
1614
1615 const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 };
1616 w.reset();
1617 try w.printValue("", .{}, vec, 0);
1618 try testing.expectEqualStrings("{ ... }", w.buffered());
1619
1620 w.reset();
1621 try w.printValue("", .{}, vec, 1);
1622 try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());
1623}
1624
1625test printDuration {
1626 testDurationCase("0ns", 0);
1627 testDurationCase("1ns", 1);
1628 testDurationCase("999ns", std.time.ns_per_us - 1);
1629 testDurationCase("1us", std.time.ns_per_us);
1630 testDurationCase("1.45us", 1450);
1631 testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);
1632 testDurationCase("14.5us", 14500);
1633 testDurationCase("145us", 145000);
1634 testDurationCase("999.999us", std.time.ns_per_ms - 1);
1635 testDurationCase("1ms", std.time.ns_per_ms + 1);
1636 testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);
1637 testDurationCase("1.11ms", 1110000);
1638 testDurationCase("1.111ms", 1111000);
1639 testDurationCase("1.111ms", 1111100);
1640 testDurationCase("999.999ms", std.time.ns_per_s - 1);
1641 testDurationCase("1s", std.time.ns_per_s);
1642 testDurationCase("59.999s", std.time.ns_per_min - 1);
1643 testDurationCase("1m", std.time.ns_per_min);
1644 testDurationCase("1h", std.time.ns_per_hour);
1645 testDurationCase("1d", std.time.ns_per_day);
1646 testDurationCase("1w", std.time.ns_per_week);
1647 testDurationCase("1y", 365 * std.time.ns_per_day);
1648 testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1
1649 testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1650 testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1651 testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1652 testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1653 testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1654 testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1655 testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));
1656
1657 testing.expectFmt("=======0ns", "{D:=>10}", .{0});
1658 testing.expectFmt("1ns=======", "{D:=<10}", .{1});
1659 testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});
1660}
1661
1662test printDurationSigned {
1663 testDurationCaseSigned("0ns", 0);
1664 testDurationCaseSigned("1ns", 1);
1665 testDurationCaseSigned("-1ns", -(1));
1666 testDurationCaseSigned("999ns", std.time.ns_per_us - 1);
1667 testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));
1668 testDurationCaseSigned("1us", std.time.ns_per_us);
1669 testDurationCaseSigned("-1us", -(std.time.ns_per_us));
1670 testDurationCaseSigned("1.45us", 1450);
1671 testDurationCaseSigned("-1.45us", -(1450));
1672 testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);
1673 testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));
1674 testDurationCaseSigned("14.5us", 14500);
1675 testDurationCaseSigned("-14.5us", -(14500));
1676 testDurationCaseSigned("145us", 145000);
1677 testDurationCaseSigned("-145us", -(145000));
1678 testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);
1679 testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));
1680 testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);
1681 testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));
1682 testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);
1683 testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));
1684 testDurationCaseSigned("1.11ms", 1110000);
1685 testDurationCaseSigned("-1.11ms", -(1110000));
1686 testDurationCaseSigned("1.111ms", 1111000);
1687 testDurationCaseSigned("-1.111ms", -(1111000));
1688 testDurationCaseSigned("1.111ms", 1111100);
1689 testDurationCaseSigned("-1.111ms", -(1111100));
1690 testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);
1691 testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));
1692 testDurationCaseSigned("1s", std.time.ns_per_s);
1693 testDurationCaseSigned("-1s", -(std.time.ns_per_s));
1694 testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);
1695 testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));
1696 testDurationCaseSigned("1m", std.time.ns_per_min);
1697 testDurationCaseSigned("-1m", -(std.time.ns_per_min));
1698 testDurationCaseSigned("1h", std.time.ns_per_hour);
1699 testDurationCaseSigned("-1h", -(std.time.ns_per_hour));
1700 testDurationCaseSigned("1d", std.time.ns_per_day);
1701 testDurationCaseSigned("-1d", -(std.time.ns_per_day));
1702 testDurationCaseSigned("1w", std.time.ns_per_week);
1703 testDurationCaseSigned("-1w", -(std.time.ns_per_week));
1704 testDurationCaseSigned("1y", 365 * std.time.ns_per_day);
1705 testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));
1706 testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
1707 testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d
1708 testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1709 testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms));
1710 testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1711 testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us));
1712 testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1713 testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1));
1714 testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1715 testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms));
1716 testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1717 testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1));
1718 testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1719 testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
1720 testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));
1721 testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
1722 testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));
1723
1724 testing.expectFmt("=======0ns", "{s:=>10}", .{0});
1725 testing.expectFmt("1ns=======", "{s:=<10}", .{1});
1726 testing.expectFmt("-1ns======", "{s:=<10}", .{-(1)});
1727 testing.expectFmt(" -999ns ", "{s:^10}", .{-(std.time.ns_per_us - 1)});
1728}
1729
1730fn testDurationCase(expected: []const u8, input: u64) !void {
1731 var buf: [24]u8 = undefined;
1732 var w: Writer = .fixed(&buf);
1733 try w.printDurationUnsigned(input);
1734 try testing.expectEqualStrings(expected, w.buffered());
1735}
1736
1737fn testDurationCaseSigned(expected: []const u8, input: i64) !void {
1738 var buf: [24]u8 = undefined;
1739 var w: Writer = .fixed(&buf);
1740 try w.printDurationSigned(input);
1741 try testing.expectEqualStrings(expected, w.buffered());
1742}
1743
1744test printIntOptions {
1745 try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{});
1746
1747 try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{});
1748 try testPrintIntCase("-12345678", @as(i32, -12345678), 10, .lower, .{});
1749 try testPrintIntCase("-bc614e", @as(i32, -12345678), 16, .lower, .{});
1750 try testPrintIntCase("-BC614E", @as(i32, -12345678), 16, .upper, .{});
1751
1752 try testPrintIntCase("12345678", @as(u32, 12345678), 10, .upper, .{});
1753
1754 try testPrintIntCase(" 666", @as(u32, 666), 10, .lower, .{ .width = 6 });
1755 try testPrintIntCase(" 1234", @as(u32, 0x1234), 16, .lower, .{ .width = 6 });
1756 try testPrintIntCase("1234", @as(u32, 0x1234), 16, .lower, .{ .width = 1 });
1757
1758 try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 });
1759 try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 });
1760}
1761
1762test "printInt with comptime_int" {
1763 var buf: [20]u8 = undefined;
1764 var w: Writer = .fixed(&buf);
1765 try w.printInt(@as(comptime_int, 123456789123456789), "", .{});
1766 try std.testing.expectEqualStrings("123456789123456789", w.buffered());
1767}
1768
1769test "printFloat with comptime_float" {
1770 var buf: [20]u8 = undefined;
1771 var w: Writer = .fixed(&buf);
1772 try w.printFloat("", .{}, @as(comptime_float, 1.0));
1773 try std.testing.expectEqualStrings(w.buffered(), "1e0");
1774 try std.testing.expectFmt("1e0", "{}", .{1.0});
1775}
1776
1777fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {
1778 var buffer: [100]u8 = undefined;
1779 var w: Writer = .fixed(&buffer);
1780 w.printIntOptions(value, base, case, options);
1781 try testing.expectEqualStrings(expected, w.buffered());
1782}
1783
1784test printByteSize {
1785 try testing.expectFmt("file size: 42B\n", "file size: {B}\n", .{42});
1786 try testing.expectFmt("file size: 42B\n", "file size: {Bi}\n", .{42});
1787 try testing.expectFmt("file size: 63MB\n", "file size: {B}\n", .{63 * 1000 * 1000});
1788 try testing.expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{63 * 1024 * 1024});
1789 try testing.expectFmt("file size: 42B\n", "file size: {B:.2}\n", .{42});
1790 try testing.expectFmt("file size: 42B\n", "file size: {B:>9.2}\n", .{42});
1791 try testing.expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{63 * 1024 * 1024});
1792 try testing.expectFmt("file size: 60.08MiB\n", "file size: {Bi:.2}\n", .{63 * 1000 * 1000});
1793 try testing.expectFmt("file size: =66.06MB=\n", "file size: {B:=^9.2}\n", .{63 * 1024 * 1024});
1794 try testing.expectFmt("file size: 66.06MB\n", "file size: {B: >9.2}\n", .{63 * 1024 * 1024});
1795 try testing.expectFmt("file size: 66.06MB \n", "file size: {B: <9.2}\n", .{63 * 1024 * 1024});
1796 try testing.expectFmt("file size: 0.01844674407370955ZB\n", "file size: {B}\n", .{std.math.maxInt(u64)});
1797}
1798
1799test "bytes.hex" {
1800 const some_bytes = "\xCA\xFE\xBA\xBE";
1801 try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
1802 try std.testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
1803 try std.testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
1804 try std.testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
1805 const bytes_with_zeros = "\x00\x0E\xBA\xBE";
1806 try std.testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
1807}
1808
1809test fixed {
1810 {
1811 var buf: [255]u8 = undefined;
1812 var w: Writer = .fixed(&buf);
1813 try w.print("{s}{s}!", .{ "Hello", "World" });
1814 try testing.expectEqualStrings("HelloWorld!", w.buffered());
1815 }
1816
1817 comptime {
1818 var buf: [255]u8 = undefined;
1819 var w: Writer = .fixed(&buf);
1820 try w.print("{s}{s}!", .{ "Hello", "World" });
1821 try testing.expectEqualStrings("HelloWorld!", w.buffered());
1822 }
1823}
1824
1825test "fixed output" {
1826 var buffer: [10]u8 = undefined;
1827 var w: Writer = .fixed(&buffer);
1828
1829 try w.writeAll("Hello");
1830 try testing.expect(std.mem.eql(u8, w.buffered(), "Hello"));
1831
1832 try w.writeAll("world");
1833 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
1834
1835 try testing.expectError(error.WriteStreamEnd, w.writeAll("!"));
1836 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
1837
1838 w.reset();
1839 try testing.expect(w.buffered().len == 0);
1840
1841 try testing.expectError(error.WriteStreamEnd, w.writeAll("Hello world!"));
1842 try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl"));
1843
1844 try w.seekTo((try w.getEndPos()) + 1);
1845 try testing.expectError(error.WriteStreamEnd, w.writeAll("H"));
1846}
1847
1848pub fn failingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1849 _ = w;
1850 _ = data;
1851 _ = splat;
1852 return error.WriteFailed;
1853}
1854
1855pub fn failingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
1856 _ = w;
1857 _ = file_reader;
1858 _ = limit;
1859 return error.WriteFailed;
1860}
1861
1862pub fn discardingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1863 const slice = data[0 .. data.len - 1];
1864 const pattern = data[slice.len..];
1865 var written: usize = pattern.len * splat;
1866 for (slice) |bytes| written += bytes.len;
1867 w.end = 0;
1868 return written;
1869}
1870
1871pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
1872 if (File.Handle == void) return error.Unimplemented;
1873 w.end = 0;
1874 if (file_reader.getSize()) |size| {
1875 const n = limit.minInt(size - file_reader.pos);
1876 file_reader.seekBy(@intCast(n)) catch return error.Unimplemented;
1877 w.end = 0;
1878 return n;
1879 } else |_| {
1880 // Error is observable on `file_reader` instance, and it is better to
1881 // treat the file as a pipe.
1882 return error.Unimplemented;
1883 }
1884}
1885
1886/// Removes the first `n` bytes from `buffer` by shifting buffer contents,
1887/// returning how many bytes are left after consuming the entire buffer, or
1888/// zero if the entire buffer was not consumed.
1889///
1890/// Useful for `VTable.drain` function implementations to implement partial
1891/// drains.
1892pub fn consume(w: *Writer, n: usize) usize {
1893 if (n < w.end) {
1894 const remaining = w.buffer[n..w.end];
1895 @memmove(w.buffer[0..remaining.len], remaining);
1896 w.end = remaining.len;
1897 return 0;
1898 }
1899 defer w.end = 0;
1900 return n - w.end;
1901}
1902
1903/// Shortcut for setting `end` to zero and returning zero. Equivalent to
1904/// calling `consume` with `end`.
1905pub fn consumeAll(w: *Writer) usize {
1906 w.end = 0;
1907 return 0;
1908}
1909
1910/// For use when the `Writer` implementation can cannot offer a more efficient
1911/// implementation than a basic read/write loop on the file.
1912pub fn unimplementedSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
1913 _ = w;
1914 _ = file_reader;
1915 _ = limit;
1916 return error.Unimplemented;
1917}
1918
1919/// When this function is called it usually means the buffer got full, so it's
1920/// time to return an error. However, we still need to make sure all of the
1921/// available buffer has been filled. Also, it may be called from `flush` in
1922/// which case it should return successfully.
1923pub fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1924 if (data.len == 0) return 0;
1925 for (data[0 .. data.len - 1]) |bytes| {
1926 const dest = w.buffer[w.end..];
1927 const len = @min(bytes.len, dest.len);
1928 @memcpy(dest[0..len], bytes[0..len]);
1929 w.end += len;
1930 if (bytes.len > dest.len) return error.WriteFailed;
1931 }
1932 const pattern = data[data.len - 1];
1933 const dest = w.buffer[w.end..];
1934 switch (pattern.len) {
1935 0 => return w.end,
1936 1 => {
1937 assert(splat >= dest.len);
1938 @memset(dest, pattern[0]);
1939 w.end += dest.len;
1940 return error.WriteFailed;
1941 },
1942 else => {
1943 for (0..splat) |i| {
1944 const remaining = dest[i * pattern.len ..];
1945 const len = @min(pattern.len, remaining.len);
1946 @memcpy(remaining[0..len], pattern[0..len]);
1947 w.end += len;
1948 if (pattern.len > remaining.len) return error.WriteFailed;
1949 }
1950 unreachable;
1951 },
82 }1952 }
83}1953}
1954
1955/// Provides a `Writer` implementation based on calling `Hasher.update`, sending
1956/// all data also to an underlying `Writer`.
1957///
1958/// When using this, the underlying writer is best unbuffered because all
1959/// writes are passed on directly to it.
1960///
1961/// This implementation makes suboptimal buffering decisions due to being
1962/// generic. A better solution will involve creating a writer for each hash
1963/// function, where the splat buffer can be tailored to the hash implementation
1964/// details.
1965pub fn Hashed(comptime Hasher: type) type {
1966 return struct {
1967 out: *Writer,
1968 hasher: Hasher,
1969 interface: Writer,
1970
1971 pub fn init(out: *Writer) @This() {
1972 return .{
1973 .out = out,
1974 .hasher = .{},
1975 .interface = .{
1976 .vtable = &.{@This().drain},
1977 },
1978 };
1979 }
1980
1981 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1982 const this: *@This() = @alignCast(@fieldParentPtr("interface", w));
1983 if (data.len == 0) {
1984 const buf = w.buffered();
1985 try this.out.writeAll(buf);
1986 this.hasher.update(buf);
1987 w.end = 0;
1988 return buf.len;
1989 }
1990 const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat);
1991 if (aux_n < w.end) {
1992 this.hasher.update(w.buffer[0..aux_n]);
1993 const remaining = w.buffer[aux_n..w.end];
1994 @memmove(w.buffer[0..remaining.len], remaining);
1995 w.end = remaining.len;
1996 return 0;
1997 }
1998 this.hasher.update(w.buffered());
1999 const n = aux_n - w.end;
2000 w.end = 0;
2001 var remaining: usize = n;
2002 const short_data = data[0 .. data.len - @intFromBool(splat == 0)];
2003 for (short_data) |slice| {
2004 if (remaining < slice.len) {
2005 this.hasher.update(slice[0..remaining]);
2006 return n;
2007 } else {
2008 remaining -= slice.len;
2009 this.hasher.update(slice);
2010 }
2011 }
2012 const remaining_splat = switch (splat) {
2013 0, 1 => {
2014 assert(remaining == 0);
2015 return n;
2016 },
2017 else => splat - 1,
2018 };
2019 const pattern = data[data.len - 1];
2020 assert(remaining == remaining_splat * pattern.len);
2021 switch (pattern.len) {
2022 0 => {
2023 assert(remaining == 0);
2024 },
2025 1 => {
2026 var buffer: [64]u8 = undefined;
2027 @memset(&buffer, pattern[0]);
2028 while (remaining > 0) {
2029 const update_len = @min(remaining, buffer.len);
2030 this.hasher.update(buffer[0..update_len]);
2031 remaining -= update_len;
2032 }
2033 },
2034 else => {
2035 while (remaining > 0) {
2036 const update_len = @min(remaining, pattern.len);
2037 this.hasher.update(pattern[0..update_len]);
2038 remaining -= update_len;
2039 }
2040 },
2041 }
2042 return n;
2043 }
2044 };
2045}
2046
2047/// Maintains `Writer` state such that it writes to the unused capacity of an
2048/// array list, filling it up completely before making a call through the
2049/// vtable, causing a resize. Consequently, the same, optimized, non-generic
2050/// machine code that uses `std.io.Reader`, such as formatted printing, takes
2051/// the hot paths when using this API.
2052///
2053/// When using this API, it is not necessary to call `flush`.
2054pub const Allocating = struct {
2055 allocator: Allocator,
2056 interface: Writer,
2057
2058 pub fn init(allocator: Allocator) Allocating {
2059 return .{
2060 .allocator = allocator,
2061 .interface = .{
2062 .buffer = &.{},
2063 .vtable = &vtable,
2064 },
2065 };
2066 }
2067
2068 pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating {
2069 return .{
2070 .allocator = allocator,
2071 .interface = .{
2072 .buffer = try allocator.alloc(u8, capacity),
2073 .vtable = &vtable,
2074 },
2075 };
2076 }
2077
2078 pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating {
2079 return .{
2080 .allocator = allocator,
2081 .interface = .{
2082 .buffer = slice,
2083 .vtable = &vtable,
2084 },
2085 };
2086 }
2087
2088 /// Replaces `array_list` with empty, taking ownership of the memory.
2089 pub fn fromArrayList(allocator: Allocator, array_list: *std.ArrayListUnmanaged(u8)) Allocating {
2090 defer array_list.* = .empty;
2091 return .{
2092 .allocator = allocator,
2093 .interface = .{
2094 .vtable = &vtable,
2095 .buffer = array_list.allocatedSlice(),
2096 .end = array_list.items.len,
2097 },
2098 };
2099 }
2100
2101 const vtable: VTable = .{
2102 .drain = Allocating.drain,
2103 .sendFile = Allocating.sendFile,
2104 .flush = noopFlush,
2105 };
2106
2107 pub fn deinit(a: *Allocating) void {
2108 a.allocator.free(a.interface.buffer);
2109 a.* = undefined;
2110 }
2111
2112 /// Returns an array list that takes ownership of the allocated memory.
2113 /// Resets the `Allocating` to an empty state.
2114 pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) {
2115 const w = &a.interface;
2116 const result: std.ArrayListUnmanaged(u8) = .{
2117 .items = w.buffer[0..w.end],
2118 .capacity = w.buffer.len,
2119 };
2120 w.buffer = &.{};
2121 w.end = 0;
2122 return result;
2123 }
2124
2125 pub fn toOwnedSlice(a: *Allocating) error{OutOfMemory}![]u8 {
2126 var list = a.toArrayList();
2127 return list.toOwnedSlice(a.allocator);
2128 }
2129
2130 pub fn toOwnedSliceSentinel(a: *Allocating, comptime sentinel: u8) error{OutOfMemory}![:sentinel]u8 {
2131 const gpa = a.allocator;
2132 var list = toArrayList(a);
2133 return list.toOwnedSliceSentinel(gpa, sentinel);
2134 }
2135
2136 pub fn getWritten(a: *Allocating) []u8 {
2137 return a.interface.buffered();
2138 }
2139
2140 pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void {
2141 const shrink_by = a.interface.end - new_len;
2142 a.interface.end = new_len;
2143 a.interface.count -= shrink_by;
2144 }
2145
2146 pub fn clearRetainingCapacity(a: *Allocating) void {
2147 a.shrinkRetainingCapacity(0);
2148 }
2149
2150 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
2151 const a: *Allocating = @fieldParentPtr("interface", w);
2152 const gpa = a.allocator;
2153 const pattern = data[data.len - 1];
2154 const splat_len = pattern.len * splat;
2155 var list = a.toArrayList();
2156 defer setArrayList(a, list);
2157 const start_len = list.items.len;
2158 for (data) |bytes| {
2159 list.ensureUnusedCapacity(gpa, bytes.len + splat_len) catch return error.WriteFailed;
2160 list.appendSliceAssumeCapacity(bytes);
2161 }
2162 if (splat == 0) {
2163 list.items.len -= pattern.len;
2164 } else switch (pattern.len) {
2165 0 => {},
2166 1 => list.appendNTimesAssumeCapacity(pattern[0], splat - 1),
2167 else => for (0..splat - 1) |_| list.appendSliceAssumeCapacity(pattern),
2168 }
2169 return list.items.len - start_len;
2170 }
2171
2172 fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize {
2173 if (File.Handle == void) return error.Unimplemented;
2174 const a: *Allocating = @fieldParentPtr("interface", w);
2175 const gpa = a.allocator;
2176 var list = a.toArrayList();
2177 defer setArrayList(a, list);
2178 const pos = file_reader.pos;
2179 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
2180 list.ensureUnusedCapacity(gpa, limit.minInt(additional)) catch return error.WriteFailed;
2181 const dest = limit.slice(list.unusedCapacitySlice());
2182 const n = file_reader.read(dest) catch |err| switch (err) {
2183 error.ReadFailed => return error.ReadFailed,
2184 error.EndOfStream => 0,
2185 };
2186 list.items.len += n;
2187 return n;
2188 }
2189
2190 fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void {
2191 a.interface.buffer = list.allocatedSlice();
2192 a.interface.end = list.items.len;
2193 }
2194
2195 test Allocating {
2196 var a: Allocating = .init(std.testing.allocator);
2197 defer a.deinit();
2198 const w = &a.interface;
2199
2200 const x: i32 = 42;
2201 const y: i32 = 1234;
2202 try w.print("x: {}\ny: {}\n", .{ x, y });
2203
2204 try testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", a.getWritten());
2205 }
2206};
lib/std/io/buffered_atomic_file.zig+1-1
...@@ -11,7 +11,7 @@ pub const BufferedAtomicFile = struct {...@@ -11,7 +11,7 @@ pub const BufferedAtomicFile = struct {
1111
12 pub const buffer_size = 4096;12 pub const buffer_size = 4096;
13 pub const BufferedWriter = std.io.BufferedWriter(buffer_size, File.Writer);13 pub const BufferedWriter = std.io.BufferedWriter(buffer_size, File.Writer);
14 pub const Writer = std.io.Writer(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write);14 pub const Writer = std.io.GenericWriter(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write);
1515
16 /// TODO when https://github.com/ziglang/zig/issues/2761 is solved16 /// TODO when https://github.com/ziglang/zig/issues/2761 is solved
17 /// this API will not need an allocator17 /// this API will not need an allocator
lib/std/io/buffered_reader.zig+3-3
...@@ -12,7 +12,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty...@@ -12,7 +12,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
12 end: usize = 0,12 end: usize = 0,
1313
14 pub const Error = ReaderType.Error;14 pub const Error = ReaderType.Error;
15 pub const Reader = io.Reader(*Self, Error, read);15 pub const Reader = io.GenericReader(*Self, Error, read);
1616
17 const Self = @This();17 const Self = @This();
1818
...@@ -61,7 +61,7 @@ test "OneByte" {...@@ -61,7 +61,7 @@ test "OneByte" {
6161
62 const Error = error{NoError};62 const Error = error{NoError};
63 const Self = @This();63 const Self = @This();
64 const Reader = io.Reader(*Self, Error, read);64 const Reader = io.GenericReader(*Self, Error, read);
6565
66 fn init(str: []const u8) Self {66 fn init(str: []const u8) Self {
67 return Self{67 return Self{
...@@ -105,7 +105,7 @@ test "Block" {...@@ -105,7 +105,7 @@ test "Block" {
105105
106 const Error = error{NoError};106 const Error = error{NoError};
107 const Self = @This();107 const Self = @This();
108 const Reader = io.Reader(*Self, Error, read);108 const Reader = io.GenericReader(*Self, Error, read);
109109
110 fn init(block: []const u8, reads_allowed: usize) Self {110 fn init(block: []const u8, reads_allowed: usize) Self {
111 return Self{111 return Self{
lib/std/io/buffered_writer.zig+1-1
...@@ -10,7 +10,7 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty...@@ -10,7 +10,7 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty
10 end: usize = 0,10 end: usize = 0,
1111
12 pub const Error = WriterType.Error;12 pub const Error = WriterType.Error;
13 pub const Writer = io.Writer(*Self, Error, write);13 pub const Writer = io.GenericWriter(*Self, Error, write);
1414
15 const Self = @This();15 const Self = @This();
1616
lib/std/io/c_writer.zig+1-1
...@@ -3,7 +3,7 @@ const builtin = @import("builtin");...@@ -3,7 +3,7 @@ const builtin = @import("builtin");
3const io = std.io;3const io = std.io;
4const testing = std.testing;4const testing = std.testing;
55
6pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite);6pub const CWriter = io.GenericWriter(*std.c.FILE, std.fs.File.WriteError, cWriterWrite);
77
8pub fn cWriter(c_file: *std.c.FILE) CWriter {8pub fn cWriter(c_file: *std.c.FILE) CWriter {
9 return .{ .context = c_file };9 return .{ .context = c_file };
lib/std/io/change_detection_stream.zig+1-1
...@@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type {...@@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type {
8 return struct {8 return struct {
9 const Self = @This();9 const Self = @This();
10 pub const Error = WriterType.Error;10 pub const Error = WriterType.Error;
11 pub const Writer = io.Writer(*Self, Error, write);11 pub const Writer = io.GenericWriter(*Self, Error, write);
1212
13 anything_changed: bool,13 anything_changed: bool,
14 underlying_writer: WriterType,14 underlying_writer: WriterType,
lib/std/io/counting_reader.zig+1-1
...@@ -9,7 +9,7 @@ pub fn CountingReader(comptime ReaderType: anytype) type {...@@ -9,7 +9,7 @@ pub fn CountingReader(comptime ReaderType: anytype) type {
9 bytes_read: u64 = 0,9 bytes_read: u64 = 0,
1010
11 pub const Error = ReaderType.Error;11 pub const Error = ReaderType.Error;
12 pub const Reader = io.Reader(*@This(), Error, read);12 pub const Reader = io.GenericReader(*@This(), Error, read);
1313
14 pub fn read(self: *@This(), buf: []u8) Error!usize {14 pub fn read(self: *@This(), buf: []u8) Error!usize {
15 const amt = try self.child_reader.read(buf);15 const amt = try self.child_reader.read(buf);
lib/std/io/counting_writer.zig+1-1
...@@ -9,7 +9,7 @@ pub fn CountingWriter(comptime WriterType: type) type {...@@ -9,7 +9,7 @@ pub fn CountingWriter(comptime WriterType: type) type {
9 child_stream: WriterType,9 child_stream: WriterType,
1010
11 pub const Error = WriterType.Error;11 pub const Error = WriterType.Error;
12 pub const Writer = io.Writer(*Self, Error, write);12 pub const Writer = io.GenericWriter(*Self, Error, write);
1313
14 const Self = @This();14 const Self = @This();
1515
lib/std/io/find_byte_writer.zig+1-1
...@@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type {...@@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type {
8 return struct {8 return struct {
9 const Self = @This();9 const Self = @This();
10 pub const Error = UnderlyingWriter.Error;10 pub const Error = UnderlyingWriter.Error;
11 pub const Writer = io.Writer(*Self, Error, write);11 pub const Writer = io.GenericWriter(*Self, Error, write);
1212
13 underlying_writer: UnderlyingWriter,13 underlying_writer: UnderlyingWriter,
14 byte_found: bool,14 byte_found: bool,
lib/std/io/fixed_buffer_stream.zig+4-4
...@@ -4,8 +4,8 @@ const testing = std.testing;...@@ -4,8 +4,8 @@ const testing = std.testing;
4const mem = std.mem;4const mem = std.mem;
5const assert = std.debug.assert;5const assert = std.debug.assert;
66
7/// This turns a byte buffer into an `io.Writer`, `io.Reader`, or `io.SeekableStream`.7/// This turns a byte buffer into an `io.GenericWriter`, `io.GenericReader`, or `io.SeekableStream`.
8/// If the supplied byte buffer is const, then `io.Writer` is not available.8/// If the supplied byte buffer is const, then `io.GenericWriter` is not available.
9pub fn FixedBufferStream(comptime Buffer: type) type {9pub fn FixedBufferStream(comptime Buffer: type) type {
10 return struct {10 return struct {
11 /// `Buffer` is either a `[]u8` or `[]const u8`.11 /// `Buffer` is either a `[]u8` or `[]const u8`.
...@@ -17,8 +17,8 @@ pub fn FixedBufferStream(comptime Buffer: type) type {...@@ -17,8 +17,8 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
17 pub const SeekError = error{};17 pub const SeekError = error{};
18 pub const GetSeekPosError = error{};18 pub const GetSeekPosError = error{};
1919
20 pub const Reader = io.Reader(*Self, ReadError, read);20 pub const Reader = io.GenericReader(*Self, ReadError, read);
21 pub const Writer = io.Writer(*Self, WriteError, write);21 pub const Writer = io.GenericWriter(*Self, WriteError, write);
2222
23 pub const SeekableStream = io.SeekableStream(23 pub const SeekableStream = io.SeekableStream(
24 *Self,24 *Self,
lib/std/io/limited_reader.zig+1-1
...@@ -9,7 +9,7 @@ pub fn LimitedReader(comptime ReaderType: type) type {...@@ -9,7 +9,7 @@ pub fn LimitedReader(comptime ReaderType: type) type {
9 bytes_left: u64,9 bytes_left: u64,
1010
11 pub const Error = ReaderType.Error;11 pub const Error = ReaderType.Error;
12 pub const Reader = io.Reader(*Self, Error, read);12 pub const Reader = io.GenericReader(*Self, Error, read);
1313
14 const Self = @This();14 const Self = @This();
1515
lib/std/io/multi_writer.zig+1-1
...@@ -15,7 +15,7 @@ pub fn MultiWriter(comptime Writers: type) type {...@@ -15,7 +15,7 @@ pub fn MultiWriter(comptime Writers: type) type {
15 streams: Writers,15 streams: Writers,
1616
17 pub const Error = ErrSet;17 pub const Error = ErrSet;
18 pub const Writer = io.Writer(*Self, Error, write);18 pub const Writer = io.GenericWriter(*Self, Error, write);
1919
20 pub fn writer(self: *Self) Writer {20 pub fn writer(self: *Self) Writer {
21 return .{ .context = self };21 return .{ .context = self };
lib/std/io/stream_source.zig+4-4
...@@ -2,9 +2,9 @@ const std = @import("../std.zig");...@@ -2,9 +2,9 @@ const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const io = std.io;3const io = std.io;
44
5/// Provides `io.Reader`, `io.Writer`, and `io.SeekableStream` for in-memory buffers as5/// Provides `io.GenericReader`, `io.GenericWriter`, and `io.SeekableStream` for in-memory buffers as
6/// well as files.6/// well as files.
7/// For memory sources, if the supplied byte buffer is const, then `io.Writer` is not available.7/// For memory sources, if the supplied byte buffer is const, then `io.GenericWriter` is not available.
8/// The error set of the stream functions is the error set of the corresponding file functions.8/// The error set of the stream functions is the error set of the corresponding file functions.
9pub const StreamSource = union(enum) {9pub const StreamSource = union(enum) {
10 // TODO: expose UEFI files to std.os in a way that allows this to be true10 // TODO: expose UEFI files to std.os in a way that allows this to be true
...@@ -26,8 +26,8 @@ pub const StreamSource = union(enum) {...@@ -26,8 +26,8 @@ pub const StreamSource = union(enum) {
26 pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{});26 pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{});
27 pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{});27 pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{});
2828
29 pub const Reader = io.Reader(*StreamSource, ReadError, read);29 pub const Reader = io.GenericReader(*StreamSource, ReadError, read);
30 pub const Writer = io.Writer(*StreamSource, WriteError, write);30 pub const Writer = io.GenericWriter(*StreamSource, WriteError, write);
31 pub const SeekableStream = io.SeekableStream(31 pub const SeekableStream = io.SeekableStream(
32 *StreamSource,32 *StreamSource,
33 SeekError,33 SeekError,
lib/std/json.zig+2-2
...@@ -1,12 +1,12 @@...@@ -1,12 +1,12 @@
1//! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc82591//! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259
2//!2//!
3//! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs,3//! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs,
4//! The `Reader` API connects a `std.io.Reader` to a `Scanner`.4//! The `Reader` API connects a `std.io.GenericReader` to a `Scanner`.
5//!5//!
6//! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type.6//! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type.
7//! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection.7//! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection.
8//!8//!
9//! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.Writer`.9//! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.GenericWriter`.
10//! The high-level `stringify` serializes a Zig or `Value` type into JSON.10//! The high-level `stringify` serializes a Zig or `Value` type into JSON.
1111
12const builtin = @import("builtin");12const builtin = @import("builtin");
lib/std/json/scanner.zig+1-1
...@@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always };...@@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always };
219/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.219/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.
220pub const default_max_value_len = 4 * 1024 * 1024;220pub const default_max_value_len = 4 * 1024 * 1024;
221221
222/// Connects a `std.io.Reader` to a `std.json.Scanner`.222/// Connects a `std.io.GenericReader` to a `std.json.Scanner`.
223/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.223/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.
224pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {224pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {
225 return struct {225 return struct {
lib/std/json/stringify.zig+2-2
...@@ -38,7 +38,7 @@ pub const StringifyOptions = struct {...@@ -38,7 +38,7 @@ pub const StringifyOptions = struct {
38 emit_nonportable_numbers_as_strings: bool = false,38 emit_nonportable_numbers_as_strings: bool = false,
39};39};
4040
41/// Writes the given value to the `std.io.Writer` stream.41/// Writes the given value to the `std.io.GenericWriter` stream.
42/// See `WriteStream` for how the given value is serialized into JSON.42/// See `WriteStream` for how the given value is serialized into JSON.
43/// The maximum nesting depth of the output JSON document is 256.43/// The maximum nesting depth of the output JSON document is 256.
44/// See also `stringifyMaxDepth` and `stringifyArbitraryDepth`.44/// See also `stringifyMaxDepth` and `stringifyArbitraryDepth`.
...@@ -81,7 +81,7 @@ pub fn stringifyArbitraryDepth(...@@ -81,7 +81,7 @@ pub fn stringifyArbitraryDepth(
81}81}
8282
83/// Calls `stringifyArbitraryDepth` and stores the result in dynamically allocated memory83/// Calls `stringifyArbitraryDepth` and stores the result in dynamically allocated memory
84/// instead of taking a `std.io.Writer`.84/// instead of taking a `std.io.GenericWriter`.
85///85///
86/// Caller owns returned memory.86/// Caller owns returned memory.
87pub fn stringifyAlloc(87pub fn stringifyAlloc(
lib/std/json/stringify_test.zig+1-1
...@@ -307,7 +307,7 @@ test "stringify tuple" {...@@ -307,7 +307,7 @@ test "stringify tuple" {
307fn testStringify(expected: []const u8, value: anytype, options: StringifyOptions) !void {307fn testStringify(expected: []const u8, value: anytype, options: StringifyOptions) !void {
308 const ValidationWriter = struct {308 const ValidationWriter = struct {
309 const Self = @This();309 const Self = @This();
310 pub const Writer = std.io.Writer(*Self, Error, write);310 pub const Writer = std.io.GenericWriter(*Self, Error, write);
311 pub const Error = error{311 pub const Error = error{
312 TooMuchData,312 TooMuchData,
313 DifferentData,313 DifferentData,
lib/std/net.zig+2-2
...@@ -1845,8 +1845,8 @@ pub const Stream = struct {...@@ -1845,8 +1845,8 @@ pub const Stream = struct {
1845 pub const ReadError = posix.ReadError;1845 pub const ReadError = posix.ReadError;
1846 pub const WriteError = posix.WriteError;1846 pub const WriteError = posix.WriteError;
18471847
1848 pub const Reader = io.Reader(Stream, ReadError, read);1848 pub const Reader = io.GenericReader(Stream, ReadError, read);
1849 pub const Writer = io.Writer(Stream, WriteError, write);1849 pub const Writer = io.GenericWriter(Stream, WriteError, write);
18501850
1851 pub fn reader(self: Stream) Reader {1851 pub fn reader(self: Stream) Reader {
1852 return .{ .context = self };1852 return .{ .context = self };
lib/std/os/uefi/protocol/file.zig+2-2
...@@ -88,8 +88,8 @@ pub const File = extern struct {...@@ -88,8 +88,8 @@ pub const File = extern struct {
88 getPosition,88 getPosition,
89 getEndPos,89 getEndPos,
90 );90 );
91 pub const Reader = io.Reader(*File, ReadError, read);91 pub const Reader = io.GenericReader(*File, ReadError, read);
92 pub const Writer = io.Writer(*File, WriteError, write);92 pub const Writer = io.GenericWriter(*File, WriteError, write);
9393
94 pub fn seekableStream(self: *File) SeekableStream {94 pub fn seekableStream(self: *File) SeekableStream {
95 return .{ .context = self };95 return .{ .context = self };
lib/std/tar.zig+1-1
...@@ -348,7 +348,7 @@ pub fn Iterator(comptime ReaderType: type) type {...@@ -348,7 +348,7 @@ pub fn Iterator(comptime ReaderType: type) type {
348 unread_bytes: *u64,348 unread_bytes: *u64,
349 parent_reader: ReaderType,349 parent_reader: ReaderType,
350350
351 pub const Reader = std.io.Reader(File, ReaderType.Error, File.read);351 pub const Reader = std.io.GenericReader(File, ReaderType.Error, File.read);
352352
353 pub fn reader(self: File) Reader {353 pub fn reader(self: File) Reader {
354 return .{ .context = self };354 return .{ .context = self };
lib/std/zig/llvm/Builder.zig+1-1
...@@ -9520,7 +9520,7 @@ fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) ty...@@ -9520,7 +9520,7 @@ fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) ty
9520 backing_writer: BackingWriter,9520 backing_writer: BackingWriter,
95219521
9522 pub const Error = BackingWriter.Error || ExtraErrors;9522 pub const Error = BackingWriter.Error || ExtraErrors;
9523 pub const Writer = std.io.Writer(*const Self, Error, write);9523 pub const Writer = std.io.GenericWriter(*const Self, Error, write);
95249524
9525 const Self = @This();9525 const Self = @This();
95269526
lib/std/zig/render.zig+1-1
...@@ -3245,7 +3245,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3245,7 +3245,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
3245 return struct {3245 return struct {
3246 const Self = @This();3246 const Self = @This();
3247 pub const WriteError = UnderlyingWriter.Error;3247 pub const WriteError = UnderlyingWriter.Error;
3248 pub const Writer = std.io.Writer(*Self, WriteError, write);3248 pub const Writer = std.io.GenericWriter(*Self, WriteError, write);
32493249
3250 pub const IndentType = enum {3250 pub const IndentType = enum {
3251 normal,3251 normal,
lib/std/zig/string_literal.zig+1-1
...@@ -322,7 +322,7 @@ test parseCharLiteral {...@@ -322,7 +322,7 @@ test parseCharLiteral {
322 );322 );
323}323}
324324
325/// Parses `bytes` as a Zig string literal and writes the result to the std.io.Writer type.325/// Parses `bytes` as a Zig string literal and writes the result to the `std.io.GenericWriter` type.
326/// Asserts `bytes` has '"' at beginning and end.326/// Asserts `bytes` has '"' at beginning and end.
327pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result {327pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result {
328 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');328 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
lib/std/zip.zig+2-2
...@@ -106,7 +106,7 @@ pub const EndRecord = extern struct {...@@ -106,7 +106,7 @@ pub const EndRecord = extern struct {
106/// Find and return the end record for the given seekable zip stream.106/// Find and return the end record for the given seekable zip stream.
107/// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and107/// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and
108/// its context must also have a `.reader()` method that returns an instance of108/// its context must also have a `.reader()` method that returns an instance of
109/// `std.io.Reader`.109/// `std.io.GenericReader`.
110pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord {110pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord {
111 var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined;111 var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined;
112 const record_len_max = @min(stream_len, buf.len);112 const record_len_max = @min(stream_len, buf.len);
...@@ -617,7 +617,7 @@ pub const ExtractOptions = struct {...@@ -617,7 +617,7 @@ pub const ExtractOptions = struct {
617/// Extract the zipped files inside `seekable_stream` to the given `dest` directory.617/// Extract the zipped files inside `seekable_stream` to the given `dest` directory.
618/// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and618/// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and
619/// its context must also have a `.reader()` method that returns an instance of619/// its context must also have a `.reader()` method that returns an instance of
620/// `std.io.Reader`.620/// `std.io.GenericReader`.
621pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptions) !void {621pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptions) !void {
622 const SeekableStream = @TypeOf(seekable_stream);622 const SeekableStream = @TypeOf(seekable_stream);
623 var iter = try Iterator(SeekableStream).init(seekable_stream);623 var iter = try Iterator(SeekableStream).init(seekable_stream);
src/Package/Fetch/git.zig+1-1
...@@ -1026,7 +1026,7 @@ pub const Session = struct {...@@ -1026,7 +1026,7 @@ pub const Session = struct {
1026 ProtocolError,1026 ProtocolError,
1027 UnexpectedPacket,1027 UnexpectedPacket,
1028 };1028 };
1029 pub const Reader = std.io.Reader(*FetchStream, ReadError, read);1029 pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read);
10301030
1031 const StreamCode = enum(u8) {1031 const StreamCode = enum(u8) {
1032 pack_data = 1,1032 pack_data = 1,