| author | |
| committer | |
| log | 9f27d770a1832cf3017a8b2f7281b6faf0347a51 |
| tree | ef5207f926478533af2e4f84ff7fdc62e6f61a6f |
| parent | fc2c1883b36a6ba8c7303d12b57147656dc7dd70 |
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 | 2949 | slurped_header: [size]u8 = [_]u8{0x00} ** size, |
| 2950 | 2950 | |
| 2951 | 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); | |
| 2953 | 2953 | |
| 2954 | 2954 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 2955 | 2955 | const amt = try self.child_reader.read(buf); |
| ... | ... | @@ -2983,7 +2983,7 @@ pub fn LimitedWriter(comptime WriterType: type) type { |
| 2983 | 2983 | bytes_left: u64, |
| 2984 | 2984 | |
| 2985 | 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); | |
| 2987 | 2987 | |
| 2988 | 2988 | const Self = @This(); |
| 2989 | 2989 |
lib/compiler/resinator/main.zig+1-1| ... | ... | @@ -471,7 +471,7 @@ const IoStream = struct { |
| 471 | 471 | allocator: std.mem.Allocator, |
| 472 | 472 | }; |
| 473 | 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); | |
| 475 | 475 | |
| 476 | 476 | pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize { |
| 477 | 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 | 338 | @memcpy(self.items[old_len..][0..items.len], items); |
| 339 | 339 | } |
| 340 | 340 | |
| 341 | pub const Writer = if (T != u8) | |
| 342 | @compileError("The Writer interface is only defined for ArrayList(u8) " ++ | |
| 343 | "but the given type is ArrayList(" ++ @typeName(T) ++ ")") | |
| 344 | else | |
| 345 | std.io.Writer(*Self, Allocator.Error, appendWrite); | |
| 341 | pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { | |
| 342 | const gpa = self.allocator; | |
| 343 | var unmanaged = self.moveToUnmanaged(); | |
| 344 | defer self.* = unmanaged.toManaged(gpa); | |
| 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); | |
| 346 | 349 | |
| 347 | 350 | /// Initializes a Writer which will append to the list. |
| 348 | 351 | pub fn writer(self: *Self) Writer { |
| ... | ... | @@ -350,14 +353,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 350 | 353 | } |
| 351 | 354 | |
| 352 | 355 | /// 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 | 357 | /// Invalidates element pointers if additional memory is needed. |
| 355 | 358 | fn appendWrite(self: *Self, m: []const u8) Allocator.Error!usize { |
| 356 | 359 | try self.appendSlice(m); |
| 357 | 360 | return m.len; |
| 358 | 361 | } |
| 359 | 362 | |
| 360 | pub const FixedWriter = std.io.Writer(*Self, Allocator.Error, appendWriteFixed); | |
| 363 | pub const FixedWriter = std.io.GenericWriter(*Self, Allocator.Error, appendWriteFixed); | |
| 361 | 364 | |
| 362 | 365 | /// Initializes a Writer which will append to the list but will return |
| 363 | 366 | /// `error.OutOfMemory` rather than increasing capacity. |
| ... | ... | @@ -365,7 +368,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 365 | 368 | return .{ .context = self }; |
| 366 | 369 | } |
| 367 | 370 | |
| 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 | 372 | fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { |
| 370 | 373 | const available_capacity = self.capacity - self.items.len; |
| 371 | 374 | if (m.len > available_capacity) |
| ... | ... | @@ -933,40 +936,56 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 933 | 936 | @memcpy(self.items[old_len..][0..items.len], items); |
| 934 | 937 | } |
| 935 | 938 | |
| 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 | 957 | pub const WriterContext = struct { |
| 937 | 958 | self: *Self, |
| 938 | 959 | allocator: Allocator, |
| 939 | 960 | }; |
| 940 | 961 | |
| 962 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. | |
| 941 | 963 | pub const Writer = if (T != u8) |
| 942 | 964 | @compileError("The Writer interface is only defined for ArrayList(u8) " ++ |
| 943 | 965 | "but the given type is ArrayList(" ++ @typeName(T) ++ ")") |
| 944 | 966 | else |
| 945 | std.io.Writer(WriterContext, Allocator.Error, appendWrite); | |
| 967 | std.io.GenericWriter(WriterContext, Allocator.Error, appendWrite); | |
| 946 | 968 | |
| 947 | /// Initializes a Writer which will append to the list. | |
| 969 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. | |
| 948 | 970 | pub fn writer(self: *Self, gpa: Allocator) Writer { |
| 949 | 971 | return .{ .context = .{ .self = self, .allocator = gpa } }; |
| 950 | 972 | } |
| 951 | 973 | |
| 952 | /// Same as `append` except it returns the number of bytes written, | |
| 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. | |
| 974 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. | |
| 956 | 975 | fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize { |
| 957 | 976 | try context.self.appendSlice(context.allocator, m); |
| 958 | 977 | return m.len; |
| 959 | 978 | } |
| 960 | 979 | |
| 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); | |
| 962 | 982 | |
| 963 | /// Initializes a Writer which will append to the list but will return | |
| 964 | /// `error.OutOfMemory` rather than increasing capacity. | |
| 983 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. | |
| 965 | 984 | pub fn fixedWriter(self: *Self) FixedWriter { |
| 966 | 985 | return .{ .context = self }; |
| 967 | 986 | } |
| 968 | 987 | |
| 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 | 989 | fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { |
| 971 | 990 | const available_capacity = self.capacity - self.items.len; |
| 972 | 991 | if (m.len > available_capacity) |
lib/std/base64.zig+3-3| ... | ... | @@ -108,7 +108,7 @@ pub const Base64Encoder = struct { |
| 108 | 108 | } |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | // dest must be compatible with std.io.Writer's writeAll interface | |
| 111 | // dest must be compatible with std.io.GenericWriter's writeAll interface | |
| 112 | 112 | pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void { |
| 113 | 113 | var chunker = window(u8, source, 3, 3); |
| 114 | 114 | while (chunker.next()) |chunk| { |
| ... | ... | @@ -118,8 +118,8 @@ pub const Base64Encoder = struct { |
| 118 | 118 | } |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | // destWriter must be compatible with std.io.Writer's writeAll interface | |
| 122 | // sourceReader must be compatible with std.io.Reader's read interface | |
| 121 | // destWriter must be compatible with std.io.GenericWriter's writeAll interface | |
| 122 | // sourceReader must be compatible with `std.io.GenericReader` read interface | |
| 123 | 123 | pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void { |
| 124 | 124 | while (true) { |
| 125 | 125 | var tempSource: [3]u8 = undefined; |
lib/std/bounded_array.zig+2-2| ... | ... | @@ -277,7 +277,7 @@ pub fn BoundedArrayAligned( |
| 277 | 277 | @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ |
| 278 | 278 | "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") |
| 279 | 279 | else |
| 280 | std.io.Writer(*Self, error{Overflow}, appendWrite); | |
| 280 | std.io.GenericWriter(*Self, error{Overflow}, appendWrite); | |
| 281 | 281 | |
| 282 | 282 | /// Initializes a writer which will write into the array. |
| 283 | 283 | pub fn writer(self: *Self) Writer { |
| ... | ... | @@ -285,7 +285,7 @@ pub fn BoundedArrayAligned( |
| 285 | 285 | } |
| 286 | 286 | |
| 287 | 287 | /// 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 | 289 | fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { |
| 290 | 290 | try self.appendSlice(m); |
| 291 | 291 | return m.len; |
lib/std/compress.zig+2-2| ... | ... | @@ -16,7 +16,7 @@ pub fn HashedReader(ReaderType: type, HasherType: type) type { |
| 16 | 16 | hasher: HasherType, |
| 17 | 17 | |
| 18 | 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); | |
| 20 | 20 | |
| 21 | 21 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 22 | 22 | const amt = try self.child_reader.read(buf); |
| ... | ... | @@ -43,7 +43,7 @@ pub fn HashedWriter(WriterType: type, HasherType: type) type { |
| 43 | 43 | hasher: HasherType, |
| 44 | 44 | |
| 45 | 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); | |
| 47 | 47 | |
| 48 | 48 | pub fn write(self: *@This(), buf: []const u8) Error!usize { |
| 49 | 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 | 355 | |
| 356 | 356 | // Writer interface |
| 357 | 357 | |
| 358 | pub const Writer = io.Writer(*Self, Error, write); | |
| 358 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 359 | 359 | pub const Error = BlockWriterType.Error; |
| 360 | 360 | |
| 361 | 361 | /// Write `input` of uncompressed data. |
| ... | ... | @@ -512,7 +512,7 @@ fn SimpleCompressor( |
| 512 | 512 | |
| 513 | 513 | // Writer interface |
| 514 | 514 | |
| 515 | pub const Writer = io.Writer(*Self, Error, write); | |
| 515 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 516 | 516 | pub const Error = BlockWriterType.Error; |
| 517 | 517 | |
| 518 | 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 | 341 | |
| 342 | 342 | // Reader interface |
| 343 | 343 | |
| 344 | pub const Reader = std.io.Reader(*Self, Error, read); | |
| 344 | pub const Reader = std.io.GenericReader(*Self, Error, read); | |
| 345 | 345 | |
| 346 | 346 | /// Returns the number of bytes read. It may be less than buffer.len. |
| 347 | 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 | 30 | Allocator.Error || |
| 31 | 31 | error{ CorruptInput, EndOfStream, Overflow }; |
| 32 | 32 | |
| 33 | pub const Reader = std.io.Reader(*Self, Error, read); | |
| 33 | pub const Reader = std.io.GenericReader(*Self, Error, read); | |
| 34 | 34 | |
| 35 | 35 | allocator: Allocator, |
| 36 | 36 | in_reader: ReaderType, |
lib/std/compress/xz.zig+1-1| ... | ... | @@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 34 | 34 | const Self = @This(); |
| 35 | 35 | |
| 36 | 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); | |
| 38 | 38 | |
| 39 | 39 | allocator: Allocator, |
| 40 | 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 | 27 | ReaderType.Error || |
| 28 | 28 | DecodeError || |
| 29 | 29 | Allocator.Error; |
| 30 | pub const Reader = std.io.Reader(*Self, Error, read); | |
| 30 | pub const Reader = std.io.GenericReader(*Self, Error, read); | |
| 31 | 31 | |
| 32 | 32 | allocator: Allocator, |
| 33 | 33 | inner_reader: ReaderType, |
lib/std/compress/zstandard.zig+1-1| ... | ... | @@ -50,7 +50,7 @@ pub fn Decompressor(comptime ReaderType: type) type { |
| 50 | 50 | OutOfMemory, |
| 51 | 51 | }; |
| 52 | 52 | |
| 53 | pub const Reader = std.io.Reader(*Self, Error, read); | |
| 53 | pub const Reader = std.io.GenericReader(*Self, Error, read); | |
| 54 | 54 | |
| 55 | 55 | pub fn init(source: ReaderType, options: DecompressorOptions) Self { |
| 56 | 56 | return .{ |
lib/std/compress/zstandard/readers.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ pub const ReversedByteReader = struct { |
| 4 | 4 | remaining_bytes: usize, |
| 5 | 5 | bytes: []const u8, |
| 6 | 6 | |
| 7 | const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); | |
| 7 | const Reader = std.io.GenericReader(*ReversedByteReader, error{}, readFn); | |
| 8 | 8 | |
| 9 | 9 | pub fn init(bytes: []const u8) ReversedByteReader { |
| 10 | 10 | return .{ |
lib/std/crypto/aegis.zig+1-1| ... | ... | @@ -803,7 +803,7 @@ fn AegisMac(comptime T: type) type { |
| 803 | 803 | } |
| 804 | 804 | |
| 805 | 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); | |
| 807 | 807 | |
| 808 | 808 | fn write(self: *Mac, bytes: []const u8) Error!usize { |
| 809 | 809 | self.update(bytes); |
lib/std/crypto/blake2.zig+1-1| ... | ... | @@ -187,7 +187,7 @@ pub fn Blake2s(comptime out_bits: usize) type { |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | 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); | |
| 191 | 191 | |
| 192 | 192 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 193 | 193 | self.update(bytes); |
lib/std/crypto/blake3.zig+1-1| ... | ... | @@ -476,7 +476,7 @@ pub const Blake3 = struct { |
| 476 | 476 | } |
| 477 | 477 | |
| 478 | 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); | |
| 480 | 480 | |
| 481 | 481 | fn write(self: *Blake3, bytes: []const u8) Error!usize { |
| 482 | 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 | 45 | self.data.ptr = begin; |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize); | |
| 48 | pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize); | |
| 49 | 49 | /// Warning: This writer writes backwards. `fn print` will NOT work as expected. |
| 50 | 50 | pub fn writer(self: *ArrayListReverse) Writer { |
| 51 | 51 | return .{ .context = self }; |
lib/std/crypto/sha1.zig+1-1| ... | ... | @@ -269,7 +269,7 @@ pub const Sha1 = struct { |
| 269 | 269 | } |
| 270 | 270 | |
| 271 | 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); | |
| 273 | 273 | |
| 274 | 274 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 275 | 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 | 376 | } |
| 377 | 377 | |
| 378 | 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); | |
| 380 | 380 | |
| 381 | 381 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 382 | 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 | 82 | } |
| 83 | 83 | |
| 84 | 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); | |
| 86 | 86 | |
| 87 | 87 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 88 | 88 | self.update(bytes); |
| ... | ... | @@ -193,7 +193,7 @@ fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | 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); | |
| 197 | 197 | |
| 198 | 198 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 199 | 199 | self.update(bytes); |
| ... | ... | @@ -286,7 +286,7 @@ fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime |
| 286 | 286 | } |
| 287 | 287 | |
| 288 | 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); | |
| 290 | 290 | |
| 291 | 291 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 292 | 292 | self.update(bytes); |
| ... | ... | @@ -392,7 +392,7 @@ fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime r |
| 392 | 392 | } |
| 393 | 393 | |
| 394 | 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); | |
| 396 | 396 | |
| 397 | 397 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 398 | 398 | self.update(bytes); |
| ... | ... | @@ -484,7 +484,7 @@ fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, compt |
| 484 | 484 | } |
| 485 | 485 | |
| 486 | 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); | |
| 488 | 488 | |
| 489 | 489 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 490 | 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 | 240 | } |
| 241 | 241 | |
| 242 | 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); | |
| 244 | 244 | |
| 245 | 245 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 246 | 246 | self.update(bytes); |
lib/std/debug/Pdb.zig+1-1| ... | ... | @@ -562,7 +562,7 @@ const MsfStream = struct { |
| 562 | 562 | return block * self.block_size + offset; |
| 563 | 563 | } |
| 564 | 564 | |
| 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 | 566 | return .{ .context = self }; |
| 567 | 567 | } |
| 568 | 568 | }; |
lib/std/fifo.zig+4-4| ... | ... | @@ -38,8 +38,8 @@ pub fn LinearFifo( |
| 38 | 38 | count: usize, |
| 39 | 39 | |
| 40 | 40 | const Self = @This(); |
| 41 | pub const Reader = std.io.Reader(*Self, error{}, readFn); | |
| 42 | pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite); | |
| 41 | pub const Reader = std.io.GenericReader(*Self, error{}, readFn); | |
| 42 | pub const Writer = std.io.GenericWriter(*Self, error{OutOfMemory}, appendWrite); | |
| 43 | 43 | |
| 44 | 44 | // Type of Self argument for slice operations. |
| 45 | 45 | // If buffer is inline (Static) then we need to ensure we haven't |
| ... | ... | @@ -231,7 +231,7 @@ pub fn LinearFifo( |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | 233 | /// 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 | 235 | fn readFn(self: *Self, dest: []u8) error{}!usize { |
| 236 | 236 | return self.read(dest); |
| 237 | 237 | } |
| ... | ... | @@ -320,7 +320,7 @@ pub fn LinearFifo( |
| 320 | 320 | } |
| 321 | 321 | |
| 322 | 322 | /// 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 | 324 | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { |
| 325 | 325 | try self.write(bytes); |
| 326 | 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 | 1581 | } |
| 1582 | 1582 | } |
| 1583 | 1583 | |
| 1584 | pub const Reader = io.Reader(File, ReadError, read); | |
| 1584 | pub const Reader = io.GenericReader(File, ReadError, read); | |
| 1585 | 1585 | |
| 1586 | 1586 | pub fn reader(file: File) Reader { |
| 1587 | 1587 | return .{ .context = file }; |
| 1588 | 1588 | } |
| 1589 | 1589 | |
| 1590 | pub const Writer = io.Writer(File, WriteError, write); | |
| 1590 | pub const Writer = io.GenericWriter(File, WriteError, write); | |
| 1591 | 1591 | |
| 1592 | 1592 | pub fn writer(file: File) Writer { |
| 1593 | 1593 | return .{ .context = file }; |
lib/std/http/Client.zig+5-5| ... | ... | @@ -311,7 +311,7 @@ pub const Connection = struct { |
| 311 | 311 | EndOfStream, |
| 312 | 312 | }; |
| 313 | 313 | |
| 314 | pub const Reader = std.io.Reader(*Connection, ReadError, read); | |
| 314 | pub const Reader = std.io.GenericReader(*Connection, ReadError, read); | |
| 315 | 315 | |
| 316 | 316 | pub fn reader(conn: *Connection) Reader { |
| 317 | 317 | return Reader{ .context = conn }; |
| ... | ... | @@ -374,7 +374,7 @@ pub const Connection = struct { |
| 374 | 374 | UnexpectedWriteFailure, |
| 375 | 375 | }; |
| 376 | 376 | |
| 377 | pub const Writer = std.io.Writer(*Connection, WriteError, write); | |
| 377 | pub const Writer = std.io.GenericWriter(*Connection, WriteError, write); | |
| 378 | 378 | |
| 379 | 379 | pub fn writer(conn: *Connection) Writer { |
| 380 | 380 | return Writer{ .context = conn }; |
| ... | ... | @@ -934,7 +934,7 @@ pub const Request = struct { |
| 934 | 934 | |
| 935 | 935 | const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError; |
| 936 | 936 | |
| 937 | const TransferReader = std.io.Reader(*Request, TransferReadError, transferRead); | |
| 937 | const TransferReader = std.io.GenericReader(*Request, TransferReadError, transferRead); | |
| 938 | 938 | |
| 939 | 939 | fn transferReader(req: *Request) TransferReader { |
| 940 | 940 | return .{ .context = req }; |
| ... | ... | @@ -1094,7 +1094,7 @@ pub const Request = struct { |
| 1094 | 1094 | pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || |
| 1095 | 1095 | error{ DecompressionFailure, InvalidTrailers }; |
| 1096 | 1096 | |
| 1097 | pub const Reader = std.io.Reader(*Request, ReadError, read); | |
| 1097 | pub const Reader = std.io.GenericReader(*Request, ReadError, read); | |
| 1098 | 1098 | |
| 1099 | 1099 | pub fn reader(req: *Request) Reader { |
| 1100 | 1100 | return .{ .context = req }; |
| ... | ... | @@ -1134,7 +1134,7 @@ pub const Request = struct { |
| 1134 | 1134 | |
| 1135 | 1135 | pub const WriteError = Connection.WriteError || error{ NotWriteable, MessageTooLong }; |
| 1136 | 1136 | |
| 1137 | pub const Writer = std.io.Writer(*Request, WriteError, write); | |
| 1137 | pub const Writer = std.io.GenericWriter(*Request, WriteError, write); | |
| 1138 | 1138 | |
| 1139 | 1139 | pub fn writer(req: *Request) Writer { |
| 1140 | 1140 | return .{ .context = req }; |
lib/std/http/protocol.zig+2-2| ... | ... | @@ -344,7 +344,7 @@ const MockBufferedConnection = struct { |
| 344 | 344 | } |
| 345 | 345 | |
| 346 | 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); | |
| 348 | 348 | |
| 349 | 349 | pub fn reader(conn: *MockBufferedConnection) Reader { |
| 350 | 350 | return Reader{ .context = conn }; |
| ... | ... | @@ -359,7 +359,7 @@ const MockBufferedConnection = struct { |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | 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); | |
| 363 | 363 | |
| 364 | 364 | pub fn writer(conn: *MockBufferedConnection) Writer { |
| 365 | 365 | return Writer{ .context = conn }; |
lib/std/io.zig+71-11| ... | ... | @@ -14,6 +14,69 @@ const File = std.fs.File; |
| 14 | 14 | const Allocator = std.mem.Allocator; |
| 15 | 15 | const Alignment = std.mem.Alignment; |
| 16 | 16 | |
| 17 | pub 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 | ||
| 77 | pub const Reader = @import("io/Reader.zig"); | |
| 78 | pub const Writer = @import("io/Writer.zig"); | |
| 79 | ||
| 17 | 80 | fn getStdOutHandle() posix.fd_t { |
| 18 | 81 | if (is_windows) { |
| 19 | 82 | return windows.peb().ProcessParameters.hStdOutput; |
| ... | ... | @@ -62,6 +125,7 @@ pub fn getStdIn() File { |
| 62 | 125 | return .{ .handle = getStdInHandle() }; |
| 63 | 126 | } |
| 64 | 127 | |
| 128 | /// Deprecated in favor of `Reader`. | |
| 65 | 129 | pub fn GenericReader( |
| 66 | 130 | comptime Context: type, |
| 67 | 131 | comptime ReadError: type, |
| ... | ... | @@ -289,6 +353,7 @@ pub fn GenericReader( |
| 289 | 353 | }; |
| 290 | 354 | } |
| 291 | 355 | |
| 356 | /// Deprecated in favor of `Writer`. | |
| 292 | 357 | pub fn GenericWriter( |
| 293 | 358 | comptime Context: type, |
| 294 | 359 | comptime WriteError: type, |
| ... | ... | @@ -350,15 +415,10 @@ pub fn GenericWriter( |
| 350 | 415 | }; |
| 351 | 416 | } |
| 352 | 417 | |
| 353 | /// Deprecated; consider switching to `AnyReader` or use `GenericReader` | |
| 354 | /// to use previous API. | |
| 355 | pub const Reader = GenericReader; | |
| 356 | /// Deprecated; consider switching to `AnyWriter` or use `GenericWriter` | |
| 357 | /// to use previous API. | |
| 358 | pub const Writer = GenericWriter; | |
| 359 | ||
| 360 | pub const AnyReader = @import("io/Reader.zig"); | |
| 361 | pub const AnyWriter = @import("io/Writer.zig"); | |
| 418 | /// Deprecated in favor of `Reader`. | |
| 419 | pub const AnyReader = @import("io/DeprecatedReader.zig"); | |
| 420 | /// Deprecated in favor of `Writer`. | |
| 421 | pub const AnyWriter = @import("io/DeprecatedWriter.zig"); | |
| 362 | 422 | |
| 363 | 423 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 364 | 424 | |
| ... | ... | @@ -819,8 +879,8 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 819 | 879 | } |
| 820 | 880 | |
| 821 | 881 | test { |
| 822 | _ = AnyReader; | |
| 823 | _ = AnyWriter; | |
| 882 | _ = Reader; | |
| 883 | _ = Writer; | |
| 824 | 884 | _ = @import("io/bit_reader.zig"); |
| 825 | 885 | _ = @import("io/bit_writer.zig"); |
| 826 | 886 | _ = @import("io/buffered_atomic_file.zig"); |
lib/std/io/DeprecatedReader.zig created+386| ... | ... | @@ -0,0 +1,386 @@ |
| 1 | context: *const anyopaque, | |
| 2 | readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize, | |
| 3 | ||
| 4 | pub 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. | |
| 9 | pub 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. | |
| 16 | pub 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. | |
| 25 | pub 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. | |
| 37 | pub 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. | |
| 47 | pub 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 | ||
| 55 | pub 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. | |
| 89 | pub 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. | |
| 101 | pub 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. | |
| 116 | pub 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. | |
| 135 | pub 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. | |
| 151 | pub 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. | |
| 176 | pub 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. | |
| 195 | pub 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. | |
| 221 | pub 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`. | |
| 232 | pub 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. | |
| 240 | pub 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 | |
| 246 | pub 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()` | |
| 256 | pub 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. | |
| 273 | pub 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 | ||
| 279 | pub 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 | ||
| 284 | pub 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` | |
| 298 | pub 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 | |
| 304 | pub 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 | |
| 316 | pub 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 | ||
| 327 | pub 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 | ||
| 335 | pub 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 | |
| 346 | pub 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. | |
| 365 | pub 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 | ||
| 375 | const std = @import("../std.zig"); | |
| 376 | const Self = @This(); | |
| 377 | const math = std.math; | |
| 378 | const assert = std.debug.assert; | |
| 379 | const mem = std.mem; | |
| 380 | const testing = std.testing; | |
| 381 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 382 | const Alignment = std.mem.Alignment; | |
| 383 | ||
| 384 | test { | |
| 385 | _ = @import("Reader/test.zig"); | |
| 386 | } |
lib/std/io/DeprecatedWriter.zig created+83| ... | ... | @@ -0,0 +1,83 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const mem = std.mem; | |
| 4 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 5 | ||
| 6 | context: *const anyopaque, | |
| 7 | writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize, | |
| 8 | ||
| 9 | const Self = @This(); | |
| 10 | pub const Error = anyerror; | |
| 11 | ||
| 12 | pub fn write(self: Self, bytes: []const u8) anyerror!usize { | |
| 13 | return self.writeFn(self.context, bytes); | |
| 14 | } | |
| 15 | ||
| 16 | pub 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 | ||
| 23 | pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void { | |
| 24 | return std.fmt.format(self, format, args); | |
| 25 | } | |
| 26 | ||
| 27 | pub fn writeByte(self: Self, byte: u8) anyerror!void { | |
| 28 | const array = [1]u8{byte}; | |
| 29 | return self.writeAll(&array); | |
| 30 | } | |
| 31 | ||
| 32 | pub 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 | ||
| 44 | pub 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 | ||
| 51 | pub 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 | ||
| 57 | pub 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 | ||
| 63 | pub 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 | ||
| 74 | pub 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 | context: *const anyopaque, | |
| 2 | readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize, | |
| 1 | const Reader = @This(); | |
| 3 | 2 | |
| 4 | pub const Error = anyerror; | |
| 3 | const builtin = @import("builtin"); | |
| 4 | const native_endian = builtin.target.cpu.arch.endian(); | |
| 5 | 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. | |
| 9 | pub fn read(self: Self, buffer: []u8) anyerror!usize { | |
| 10 | return self.readFn(self.context, buffer); | |
| 6 | const std = @import("../std.zig"); | |
| 7 | const Writer = std.io.Writer; | |
| 8 | const assert = std.debug.assert; | |
| 9 | const testing = std.testing; | |
| 10 | const Allocator = std.mem.Allocator; | |
| 11 | const ArrayList = std.ArrayListUnmanaged; | |
| 12 | const Limit = std.io.Limit; | |
| 13 | ||
| 14 | pub const Limited = @import("Reader/Limited.zig"); | |
| 15 | ||
| 16 | vtable: *const VTable, | |
| 17 | buffer: []u8, | |
| 18 | /// Number of bytes which have been consumed from `buffer`. | |
| 19 | seek: usize, | |
| 20 | /// In `buffer` before this are buffered bytes, after this is `undefined`. | |
| 21 | end: usize, | |
| 22 | ||
| 23 | pub 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 | ||
| 61 | pub 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 | ||
| 71 | pub const Error = error{ | |
| 72 | /// See the `Reader` implementation for detailed diagnostics. | |
| 73 | ReadFailed, | |
| 74 | EndOfStream, | |
| 75 | }; | |
| 76 | ||
| 77 | pub 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 | ||
| 84 | pub const ShortError = error{ | |
| 85 | /// See the `Reader` implementation for detailed diagnostics. | |
| 86 | ReadFailed, | |
| 87 | }; | |
| 88 | ||
| 89 | pub 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. | |
| 101 | const ending_state: Reader = .fixed(&.{}); | |
| 102 | pub const ending: *Reader = @constCast(&ending_state); | |
| 103 | ||
| 104 | pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited { | |
| 105 | return Limited.init(r, limit, buffer); | |
| 11 | 106 | } |
| 12 | 107 | |
| 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. | |
| 16 | pub fn readAll(self: Self, buffer: []u8) anyerror!usize { | |
| 17 | return readAtLeast(self, buffer, buffer.len); | |
| 108 | /// Constructs a `Reader` such that it will read from `buffer` and then end. | |
| 109 | pub fn fixed(buffer: []const u8) Reader { | |
| 110 | return .{ | |
| 111 | .vtable = &.{ | |
| 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 | } |
| 19 | 122 | |
| 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. | |
| 25 | pub 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; | |
| 123 | pub fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 124 | const buffer = limit.slice(r.buffer[r.seek..r.end]); | |
| 125 | if (buffer.len > 0) { | |
| 126 | @branchHint(.likely); | |
| 127 | const n = try w.write(buffer); | |
| 128 | r.seek += n; | |
| 129 | return n; | |
| 130 | } | |
| 131 | const before = w.count; | |
| 132 | const n = try r.vtable.stream(r, w, limit); | |
| 133 | assert(n <= @intFromEnum(limit)); | |
| 134 | assert(w.count == before + n); | |
| 135 | return n; | |
| 136 | } | |
| 137 | ||
| 138 | pub 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 | ||
| 154 | pub 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; | |
| 34 | } | |
| 35 | ||
| 36 | /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. | |
| 37 | pub 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. | |
| 47 | pub 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 | ||
| 55 | pub 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; | |
| 170 | return n; | |
| 171 | } | |
| 172 | ||
| 173 | /// "Pump" exactly `n` bytes from the reader to the writer. | |
| 174 | pub fn streamExact(r: *Reader, w: *Writer, n: usize) StreamError!void { | |
| 175 | var remaining = n; | |
| 176 | while (remaining != 0) remaining -= try r.stream(w, .limited(remaining)); | |
| 177 | } | |
| 178 | ||
| 179 | /// "Pump" data from the reader to the writer, handling `error.EndOfStream` as | |
| 180 | /// a success case. | |
| 181 | /// | |
| 182 | /// Returns total number of bytes written to `w`. | |
| 183 | pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize { | |
| 184 | var offset: usize = 0; | |
| 64 | 185 | 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; | |
| 186 | offset += r.stream(w, .unlimited) catch |err| switch (err) { | |
| 187 | error.EndOfStream => return offset, | |
| 188 | else => |e| return e, | |
| 189 | }; | |
| 190 | } | |
| 191 | } | |
| 192 | ||
| 193 | /// Consumes the stream until the end, ignoring all the data, returning the | |
| 194 | /// number of bytes discarded. | |
| 195 | pub 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 | ||
| 207 | pub 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` | |
| 220 | pub 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 | } | |
| 69 | 226 | |
| 70 | if (start_index - original_len > max_append_size) { | |
| 71 | array_list.shrinkAndFree(original_len + max_append_size); | |
| 227 | /// Transfers all bytes from the current position to the end of the stream, up | |
| 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` | |
| 238 | pub 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 | 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. | |
| 284 | pub fn readVec(r: *Reader, data: []const []u8) Error!usize { | |
| 285 | return readVecLimit(r, data, .unlimited); | |
| 286 | } | |
| 74 | 287 | |
| 75 | if (bytes_read != dest_slice.len) { | |
| 76 | array_list.shrinkAndFree(start_index); | |
| 77 | return; | |
| 288 | /// Equivalent to `readVec` but reads at most `limit` bytes. | |
| 289 | /// | |
| 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. | |
| 295 | pub 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 | ||
| 361 | pub fn buffered(r: *Reader) []u8 { | |
| 362 | return r.buffer[r.seek..r.end]; | |
| 363 | } | |
| 364 | ||
| 365 | pub fn bufferedLen(r: *const Reader) usize { | |
| 366 | return r.end - r.seek; | |
| 367 | } | |
| 368 | ||
| 369 | pub fn hashed(r: *Reader, hasher: anytype) Hashed(@TypeOf(hasher)) { | |
| 370 | return .{ .in = r, .hasher = hasher }; | |
| 371 | } | |
| 79 | 372 | |
| 80 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 81 | try array_list.ensureTotalCapacity(start_index + 1); | |
| 373 | pub fn readVecAll(r: *Reader, data: [][]u8) Error!void { | |
| 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 | } |
| 84 | 389 | |
| 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. | |
| 89 | pub 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. | |
| 101 | pub 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. | |
| 116 | pub 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. | |
| 135 | pub 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. | |
| 151 | pub 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; | |
| 390 | /// Returns the next `len` bytes from the stream, filling the buffer as | |
| 391 | /// necessary. | |
| 392 | /// | |
| 393 | /// Invalidates previously returned values from `peek`. | |
| 394 | /// | |
| 395 | /// Asserts that the `Reader` was initialized with a buffer capacity at | |
| 396 | /// least as big as `len`. | |
| 397 | /// | |
| 398 | /// If there are fewer than `len` bytes left in the stream, `error.EndOfStream` | |
| 399 | /// is returned instead. | |
| 400 | /// | |
| 401 | /// See also: | |
| 402 | /// * `peek` | |
| 403 | /// * `toss` | |
| 404 | pub fn peek(r: *Reader, n: usize) Error![]u8 { | |
| 405 | try r.fill(n); | |
| 406 | return r.buffer[r.seek..][0..n]; | |
| 407 | } | |
| 408 | ||
| 409 | /// Returns all the next buffered bytes, after filling the buffer to ensure it | |
| 410 | /// contains at least `n` bytes. | |
| 411 | /// | |
| 412 | /// Invalidates previously returned values from `peek` and `peekGreedy`. | |
| 413 | /// | |
| 414 | /// Asserts that the `Reader` was initialized with a buffer capacity at | |
| 415 | /// least as big as `n`. | |
| 416 | /// | |
| 417 | /// If there are fewer than `n` bytes left in the stream, `error.EndOfStream` | |
| 418 | /// is returned instead. | |
| 419 | /// | |
| 420 | /// See also: | |
| 421 | /// * `peek` | |
| 422 | /// * `toss` | |
| 423 | pub fn peekGreedy(r: *Reader, n: usize) Error![]u8 { | |
| 424 | try r.fill(n); | |
| 425 | return r.buffer[r.seek..r.end]; | |
| 426 | } | |
| 427 | ||
| 428 | /// Skips the next `n` bytes from the stream, advancing the seek position. This | |
| 429 | /// is typically and safely used after `peek`. | |
| 430 | /// | |
| 431 | /// Asserts that the number of bytes buffered is at least as many as `n`. | |
| 432 | /// | |
| 433 | /// The "tossed" memory remains alive until a "peek" operation occurs. | |
| 434 | /// | |
| 435 | /// See also: | |
| 436 | /// * `peek`. | |
| 437 | /// * `discard`. | |
| 438 | pub fn toss(r: *Reader, n: usize) void { | |
| 439 | r.seek += n; | |
| 440 | assert(r.seek <= r.end); | |
| 441 | } | |
| 442 | ||
| 443 | /// Equivalent to `toss(r.bufferedLen())`. | |
| 444 | pub fn tossAll(r: *Reader) void { | |
| 445 | r.seek = 0; | |
| 446 | r.end = 0; | |
| 447 | } | |
| 448 | ||
| 449 | /// Equivalent to `peek` followed by `toss`. | |
| 450 | /// | |
| 451 | /// The data returned is invalidated by the next call to `take`, `peek`, | |
| 452 | /// `fill`, and functions with those prefixes. | |
| 453 | pub fn take(r: *Reader, n: usize) Error![]u8 { | |
| 454 | const result = try r.peek(n); | |
| 455 | r.toss(n); | |
| 456 | return result; | |
| 457 | } | |
| 458 | ||
| 459 | /// Returns the next `n` bytes from the stream as an array, filling the buffer | |
| 460 | /// as necessary and advancing the seek position `n` bytes. | |
| 461 | /// | |
| 462 | /// Asserts that the `Reader` was initialized with a buffer capacity at | |
| 463 | /// least as big as `n`. | |
| 464 | /// | |
| 465 | /// If there are fewer than `n` bytes left in the stream, `error.EndOfStream` | |
| 466 | /// is returned instead. | |
| 467 | /// | |
| 468 | /// See also: | |
| 469 | /// * `take` | |
| 470 | pub 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` | |
| 486 | pub 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` | |
| 501 | pub fn discardAll(r: *Reader, n: usize) Error!void { | |
| 502 | if ((try r.discardShort(n)) != n) return error.EndOfStream; | |
| 503 | } | |
| 504 | ||
| 505 | pub 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` | |
| 525 | pub 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` | |
| 556 | pub 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` | |
| 571 | pub 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` | |
| 631 | pub 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 | ||
| 641 | pub 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. | |
| 645 | pub 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 | ||
| 659 | pub 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 | ||
| 666 | pub 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` | |
| 689 | pub 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 | ||
| 695 | pub 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` | |
| 711 | pub 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` | |
| 728 | pub 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` | |
| 777 | pub 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 | 784 | else => |e| return e, |
| 164 | 785 | }; |
| 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. | |
| 176 | pub 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 | }, | |
| 786 | r.toss(result.len); | |
| 787 | return result[0 .. result.len - 1]; | |
| 788 | } | |
| 182 | 789 | |
| 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` | |
| 807 | pub 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 | 813 | else => |e| return e, |
| 184 | 814 | }; |
| 185 | const output = fbs.getWritten(); | |
| 186 | buf[output.len] = delimiter; // emulating old behaviour | |
| 187 | return output; | |
| 815 | return result[0 .. result.len - 1]; | |
| 188 | 816 | } |
| 189 | 817 | |
| 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. | |
| 822 | pub 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 | 832 | /// 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. | |
| 195 | pub fn streamUntilDelimiter( | |
| 196 | self: Self, | |
| 197 | writer: anytype, | |
| 833 | /// | |
| 834 | /// Succeeds if stream ends before delimiter found. | |
| 835 | /// | |
| 836 | /// Returns number of bytes streamed. The end is not signaled to the writer. | |
| 837 | pub fn readDelimiterEnding( | |
| 838 | r: *Reader, | |
| 839 | w: *Writer, | |
| 198 | 840 | 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. | |
| 841 | ) StreamRemainingError!usize { | |
| 842 | const amount, const to = try r.readAny(w, delimiter, .unlimited); | |
| 843 | return switch (to) { | |
| 844 | .delimiter, .end => amount, | |
| 845 | .limit => unreachable, | |
| 846 | }; | |
| 847 | } | |
| 848 | ||
| 849 | pub const StreamDelimiterLimitedError = StreamRemainingError || error{ | |
| 850 | /// Stream ended before the delimiter was found. | |
| 851 | EndOfStream, | |
| 852 | /// The delimiter was not found within the limit. | |
| 853 | StreamTooLong, | |
| 854 | }; | |
| 855 | ||
| 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. | |
| 860 | pub 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 | ||
| 874 | fn 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 | } |
| 217 | 899 | |
| 218 | 900 | /// Reads from the stream until specified byte is found, discarding all data, |
| 219 | 901 | /// including the delimiter. |
| 220 | /// If end-of-stream is found, this function succeeds. | |
| 221 | pub 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, | |
| 902 | /// | |
| 903 | /// If end of stream is found, this function succeeds. | |
| 904 | pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void { | |
| 905 | _ = r; | |
| 906 | _ = delimiter; | |
| 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. | |
| 914 | pub 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`. | |
| 927 | pub 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 | } |
| 230 | 946 | |
| 231 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | |
| 232 | pub 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. | |
| 240 | pub 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 | |
| 246 | pub 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()` | |
| 256 | pub 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)); | |
| 947 | /// Without advancing the seek position, does exactly one underlying read, filling the buffer as | |
| 948 | /// much as possible. This may result in zero bytes added to the buffer, which is not an end of | |
| 949 | /// stream condition. End of stream is communicated via returning `error.EndOfStream`. | |
| 950 | /// | |
| 951 | /// Asserts buffer capacity is at least 1. | |
| 952 | pub fn fillMore(r: *Reader) Error!void { | |
| 953 | rebaseCapacity(r, 1); | |
| 954 | var writer: Writer = .{ | |
| 955 | .buffer = r.buffer, | |
| 956 | .end = r.end, | |
| 957 | .vtable = &.{ .drain = Writer.fixedDrain }, | |
| 958 | }; | |
| 959 | r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { | |
| 960 | error.WriteFailed => unreachable, | |
| 961 | else => |e| return e, | |
| 962 | }; | |
| 963 | } | |
| 964 | ||
| 965 | /// Returns the next byte from the stream or returns `error.EndOfStream`. | |
| 966 | /// | |
| 967 | /// Does not advance the seek position. | |
| 968 | /// | |
| 969 | /// Asserts the buffer capacity is nonzero. | |
| 970 | pub fn peekByte(r: *Reader) Error!u8 { | |
| 971 | const buffer = r.buffer[0..r.end]; | |
| 972 | const seek = r.seek; | |
| 973 | if (seek >= buffer.len) { | |
| 974 | @branchHint(.unlikely); | |
| 975 | try fill(r, 1); | |
| 269 | 976 | } |
| 977 | return buffer[seek]; | |
| 270 | 978 | } |
| 271 | 979 | |
| 272 | /// Reads at most `num_bytes` and returns as a bounded array. | |
| 273 | pub 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); | |
| 980 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | |
| 981 | /// | |
| 982 | /// Asserts the buffer capacity is nonzero. | |
| 983 | pub fn takeByte(r: *Reader) Error!u8 { | |
| 984 | const result = try peekByte(r); | |
| 985 | r.seek += 1; | |
| 276 | 986 | return result; |
| 277 | 987 | } |
| 278 | 988 | |
| 279 | pub 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); | |
| 989 | /// Same as `takeByte` except the returned byte is signed. | |
| 990 | pub fn takeByteSigned(r: *Reader) Error!i8 { | |
| 991 | return @bitCast(try r.takeByte()); | |
| 282 | 992 | } |
| 283 | 993 | |
| 284 | pub 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` | |
| 298 | pub 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 | |
| 304 | pub 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; | |
| 994 | /// Asserts the buffer was initialized with a capacity at least `@bitSizeOf(T) / 8`. | |
| 995 | pub inline fn takeInt(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { | |
| 996 | const n = @divExact(@typeInfo(T).int.bits, 8); | |
| 997 | return std.mem.readInt(T, try r.takeArray(n), endian); | |
| 998 | } | |
| 307 | 999 | |
| 308 | while (remaining > 0) { | |
| 309 | const amt = @min(remaining, options.buf_size); | |
| 310 | try self.readNoEof(buf[0..amt]); | |
| 311 | remaining -= amt; | |
| 312 | } | |
| 1000 | /// Asserts the buffer was initialized with a capacity at least `n`. | |
| 1001 | pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n: usize) Error!Int { | |
| 1002 | assert(n <= @sizeOf(Int)); | |
| 1003 | return std.mem.readVarInt(Int, try r.take(n), endian); | |
| 313 | 1004 | } |
| 314 | 1005 | |
| 315 | /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice | |
| 316 | pub 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; | |
| 1006 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. | |
| 1007 | /// | |
| 1008 | /// Advances the seek position. | |
| 1009 | /// | |
| 1010 | /// See also: | |
| 1011 | /// * `peekStruct` | |
| 1012 | pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T { | |
| 1013 | // Only extern and packed structs have defined in-memory layout. | |
| 1014 | comptime assert(@typeInfo(T).@"struct".layout != .auto); | |
| 1015 | return @ptrCast(try r.takeArray(@sizeOf(T))); | |
| 325 | 1016 | } |
| 326 | 1017 | |
| 327 | pub 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` | |
| 1024 | pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T { | |
| 328 | 1025 | // Only extern and packed structs have defined in-memory layout. |
| 329 | 1026 | 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]; | |
| 1027 | return @ptrCast(try r.peekArray(@sizeOf(T))); | |
| 333 | 1028 | } |
| 334 | 1029 | |
| 335 | pub 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 | } | |
| 1030 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. | |
| 1031 | /// | |
| 1032 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` | |
| 1033 | /// when `endian` is comptime-known and matches the host endianness. | |
| 1034 | pub 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. | |
| 1044 | pub 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 | 1047 | return res; |
| 341 | 1048 | } |
| 342 | 1049 | |
| 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 | |
| 346 | pub 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, | |
| 1050 | pub const TakeEnumError = Error || error{InvalidEnumTag}; | |
| 1051 | ||
| 1052 | /// Reads an integer with the same size as the given enum's tag type. If the | |
| 1053 | /// integer matches an enum tag, casts the integer to the enum tag and returns | |
| 1054 | /// it. Otherwise, returns `error.InvalidEnumTag`. | |
| 1055 | /// | |
| 1056 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`. | |
| 1057 | pub 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)`. | |
| 1066 | pub 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"; | |
| 352 | const tag = try self.readInt(type_info.tag_type, endian); | |
| 1074 | } | |
| 353 | 1075 | |
| 354 | inline for (std.meta.fields(Enum)) |field| { | |
| 355 | if (tag == field.value) { | |
| 356 | return @field(Enum, field.name); | |
| 357 | } | |
| 1076 | pub const TakeLeb128Error = Error || error{Overflow}; | |
| 1077 | ||
| 1078 | /// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit. | |
| 1079 | pub 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 | ||
| 1087 | pub 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 | ||
| 1098 | pub const FillAllocError = Error || Allocator.Error; | |
| 1099 | ||
| 1100 | pub 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. | |
| 1110 | pub 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 | } | |
| 359 | 1128 | |
| 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. | |
| 1131 | pub fn advanceBufferEnd(r: *Reader, n: usize) void { | |
| 1132 | assert(n <= r.buffer.len - r.end); | |
| 1133 | r.end += n; | |
| 361 | 1134 | } |
| 362 | 1135 | |
| 363 | /// Reads the stream until the end, ignoring all the data. | |
| 364 | /// Returns the number of bytes discarded. | |
| 365 | pub fn discard(self: Self) anyerror!u64 { | |
| 366 | var trash: [4096]u8 = undefined; | |
| 367 | var index: u64 = 0; | |
| 1136 | fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result { | |
| 1137 | const result_info = @typeInfo(Result).int; | |
| 1138 | comptime assert(result_info.bits % 7 == 0); | |
| 1139 | var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits; | |
| 1140 | const UnsignedResult = @Type(.{ .int = .{ | |
| 1141 | .signedness = .unsigned, | |
| 1142 | .bits = result_info.bits, | |
| 1143 | } }); | |
| 1144 | var result: UnsignedResult = 0; | |
| 1145 | var fits = true; | |
| 368 | 1146 | while (true) { |
| 369 | const n = try self.read(&trash); | |
| 370 | if (n == 0) return index; | |
| 371 | index += n; | |
| 1147 | const buffer: []const packed struct(u8) { bits: u7, more: bool } = @ptrCast(try r.peekGreedy(1)); | |
| 1148 | for (buffer, 1..) |byte, len| { | |
| 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 | } |
| 374 | 1165 | |
| 375 | const std = @import("../std.zig"); | |
| 376 | const Self = @This(); | |
| 377 | const math = std.math; | |
| 378 | const assert = std.debug.assert; | |
| 379 | const mem = std.mem; | |
| 380 | const testing = std.testing; | |
| 381 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 382 | const Alignment = std.mem.Alignment; | |
| 1166 | /// Left-aligns data such that `r.seek` becomes zero. | |
| 1167 | pub fn rebase(r: *Reader) void { | |
| 1168 | if (r.seek == 0) return; | |
| 1169 | const data = r.buffer[r.seek..r.end]; | |
| 1170 | @memmove(r.buffer[0..data.len], data); | |
| 1171 | r.seek = 0; | |
| 1172 | r.end = data.len; | |
| 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. | |
| 1179 | pub 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. | |
| 1191 | pub 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. | |
| 1202 | pub 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 | ||
| 1208 | test 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 | ||
| 1220 | test peek { | |
| 1221 | return error.Unimplemented; | |
| 1222 | } | |
| 1223 | ||
| 1224 | test peekGreedy { | |
| 1225 | return error.Unimplemented; | |
| 1226 | } | |
| 1227 | ||
| 1228 | test toss { | |
| 1229 | return error.Unimplemented; | |
| 1230 | } | |
| 1231 | ||
| 1232 | test take { | |
| 1233 | return error.Unimplemented; | |
| 1234 | } | |
| 1235 | ||
| 1236 | test takeArray { | |
| 1237 | return error.Unimplemented; | |
| 1238 | } | |
| 1239 | ||
| 1240 | test peekArray { | |
| 1241 | return error.Unimplemented; | |
| 1242 | } | |
| 1243 | ||
| 1244 | test 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 | ||
| 1252 | test discardRemaining { | |
| 1253 | return error.Unimplemented; | |
| 1254 | } | |
| 1255 | ||
| 1256 | test stream { | |
| 1257 | return error.Unimplemented; | |
| 1258 | } | |
| 383 | 1259 | |
| 384 | test { | |
| 385 | _ = @import("Reader/test.zig"); | |
| 1260 | test takeSentinel { | |
| 1261 | return error.Unimplemented; | |
| 1262 | } | |
| 1263 | ||
| 1264 | test peekSentinel { | |
| 1265 | return error.Unimplemented; | |
| 1266 | } | |
| 1267 | ||
| 1268 | test takeDelimiterInclusive { | |
| 1269 | return error.Unimplemented; | |
| 1270 | } | |
| 1271 | ||
| 1272 | test peekDelimiterInclusive { | |
| 1273 | return error.Unimplemented; | |
| 1274 | } | |
| 1275 | ||
| 1276 | test takeDelimiterExclusive { | |
| 1277 | return error.Unimplemented; | |
| 1278 | } | |
| 1279 | ||
| 1280 | test peekDelimiterExclusive { | |
| 1281 | return error.Unimplemented; | |
| 1282 | } | |
| 1283 | ||
| 1284 | test readDelimiter { | |
| 1285 | return error.Unimplemented; | |
| 1286 | } | |
| 1287 | ||
| 1288 | test readDelimiterEnding { | |
| 1289 | return error.Unimplemented; | |
| 1290 | } | |
| 1291 | ||
| 1292 | test readDelimiterLimit { | |
| 1293 | return error.Unimplemented; | |
| 1294 | } | |
| 1295 | ||
| 1296 | test discardDelimiterExclusive { | |
| 1297 | return error.Unimplemented; | |
| 1298 | } | |
| 1299 | ||
| 1300 | test discardDelimiterInclusive { | |
| 1301 | return error.Unimplemented; | |
| 1302 | } | |
| 1303 | ||
| 1304 | test fill { | |
| 1305 | return error.Unimplemented; | |
| 1306 | } | |
| 1307 | ||
| 1308 | test takeByte { | |
| 1309 | return error.Unimplemented; | |
| 1310 | } | |
| 1311 | ||
| 1312 | test takeByteSigned { | |
| 1313 | return error.Unimplemented; | |
| 1314 | } | |
| 1315 | ||
| 1316 | test takeInt { | |
| 1317 | return error.Unimplemented; | |
| 1318 | } | |
| 1319 | ||
| 1320 | test takeVarInt { | |
| 1321 | return error.Unimplemented; | |
| 1322 | } | |
| 1323 | ||
| 1324 | test takeStruct { | |
| 1325 | return error.Unimplemented; | |
| 1326 | } | |
| 1327 | ||
| 1328 | test peekStruct { | |
| 1329 | return error.Unimplemented; | |
| 1330 | } | |
| 1331 | ||
| 1332 | test takeStructEndian { | |
| 1333 | return error.Unimplemented; | |
| 1334 | } | |
| 1335 | ||
| 1336 | test peekStructEndian { | |
| 1337 | return error.Unimplemented; | |
| 1338 | } | |
| 1339 | ||
| 1340 | test takeEnum { | |
| 1341 | return error.Unimplemented; | |
| 1342 | } | |
| 1343 | ||
| 1344 | test takeLeb128 { | |
| 1345 | return error.Unimplemented; | |
| 1346 | } | |
| 1347 | ||
| 1348 | test readSliceShort { | |
| 1349 | return error.Unimplemented; | |
| 1350 | } | |
| 1351 | ||
| 1352 | test readVec { | |
| 1353 | return error.Unimplemented; | |
| 1354 | } | |
| 1355 | ||
| 1356 | test "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 | ||
| 1363 | fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1364 | _ = r; | |
| 1365 | _ = w; | |
| 1366 | _ = limit; | |
| 1367 | return error.EndOfStream; | |
| 1368 | } | |
| 1369 | ||
| 1370 | fn endingDiscard(r: *Reader, limit: Limit) Error!usize { | |
| 1371 | _ = r; | |
| 1372 | _ = limit; | |
| 1373 | return error.EndOfStream; | |
| 1374 | } | |
| 1375 | ||
| 1376 | fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1377 | _ = r; | |
| 1378 | _ = w; | |
| 1379 | _ = limit; | |
| 1380 | return error.ReadFailed; | |
| 1381 | } | |
| 1382 | ||
| 1383 | fn failingDiscard(r: *Reader, limit: Limit) Error!usize { | |
| 1384 | _ = r; | |
| 1385 | _ = limit; | |
| 1386 | return error.ReadFailed; | |
| 1387 | } | |
| 1388 | ||
| 1389 | test "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. | |
| 1420 | pub 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 @@ |
| 1 | const Limited = @This(); | |
| 2 | ||
| 3 | const std = @import("../../std.zig"); | |
| 4 | const Reader = std.io.Reader; | |
| 5 | const Writer = std.io.Writer; | |
| 6 | const Limit = std.io.Limit; | |
| 7 | ||
| 8 | unlimited: *Reader, | |
| 9 | remaining: Limit, | |
| 10 | interface: Reader, | |
| 11 | ||
| 12 | pub 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 | ||
| 28 | fn 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 | ||
| 36 | fn 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 | const builtin = @import("builtin"); | |
| 2 | const native_endian = builtin.target.cpu.arch.endian(); | |
| 3 | ||
| 4 | const Writer = @This(); | |
| 1 | 5 | const std = @import("../std.zig"); |
| 2 | 6 | const assert = std.debug.assert; |
| 3 | const mem = std.mem; | |
| 4 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 7 | const Limit = std.io.Limit; | |
| 8 | const File = std.fs.File; | |
| 9 | const testing = std.testing; | |
| 10 | const Allocator = std.mem.Allocator; | |
| 11 | ||
| 12 | vtable: *const VTable, | |
| 13 | /// If this has length zero, the writer is unbuffered, and `flush` is a no-op. | |
| 14 | buffer: []u8, | |
| 15 | /// In `buffer` before this are buffered bytes, after this is `undefined`. | |
| 16 | end: 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. | |
| 22 | count: usize = 0, | |
| 23 | ||
| 24 | pub 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 | ||
| 83 | pub const Error = error{ | |
| 84 | /// See the `Writer` implementation for detailed diagnostics. | |
| 85 | WriteFailed, | |
| 86 | }; | |
| 5 | 87 | |
| 6 | context: *const anyopaque, | |
| 7 | writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize, | |
| 88 | pub const FileAllError = error{ | |
| 89 | /// Detailed diagnostics are found on the `File.Reader` struct. | |
| 90 | ReadFailed, | |
| 91 | /// See the `Writer` implementation for detailed diagnostics. | |
| 92 | WriteFailed, | |
| 93 | }; | |
| 8 | 94 | |
| 9 | const Self = @This(); | |
| 10 | pub const Error = anyerror; | |
| 95 | pub const FileReadingError = error{ | |
| 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 | }; | |
| 11 | 103 | |
| 12 | pub fn write(self: Self, bytes: []const u8) anyerror!usize { | |
| 13 | return self.writeFn(self.context, bytes); | |
| 104 | pub const FileError = error{ | |
| 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`. | |
| 118 | pub fn fixed(buffer: []u8) Writer { | |
| 119 | return .{ | |
| 120 | .vtable = &.{ .drain = fixedDrain }, | |
| 121 | .buffer = buffer, | |
| 122 | }; | |
| 14 | 123 | } |
| 15 | 124 | |
| 16 | pub 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..]); | |
| 125 | pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) { | |
| 126 | return .{ .out = w, .hasher = hasher }; | |
| 127 | } | |
| 128 | ||
| 129 | pub const failing: Writer = .{ | |
| 130 | .vtable = &.{ | |
| 131 | .drain = failingDrain, | |
| 132 | .sendFile = failingSendFile, | |
| 133 | }, | |
| 134 | }; | |
| 135 | ||
| 136 | pub 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. | |
| 147 | pub fn buffered(w: *const Writer) []u8 { | |
| 148 | return w.buffer[0..w.end]; | |
| 149 | } | |
| 150 | ||
| 151 | pub 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 | ||
| 158 | pub 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`. | |
| 166 | pub 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. | |
| 173 | pub 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 | } |
| 22 | 209 | |
| 23 | pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void { | |
| 24 | return std.fmt.format(self, format, args); | |
| 210 | /// Equivalent to `writeSplat` but writes at most `limit` bytes. | |
| 211 | pub 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 | } |
| 26 | 223 | |
| 27 | pub fn writeByte(self: Self, byte: u8) anyerror!void { | |
| 28 | const array = [1]u8{byte}; | |
| 29 | return self.writeAll(&array); | |
| 224 | /// Returns how many bytes were consumed from `header` and `data`. | |
| 225 | pub fn writeSplatHeader( | |
| 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 | } |
| 31 | 252 | |
| 32 | pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void { | |
| 33 | var bytes: [256]u8 = undefined; | |
| 34 | @memset(bytes[0..], byte); | |
| 253 | /// Equivalent to `writeSplatHeader` but writes at most `limit` bytes. | |
| 254 | pub fn writeSplatHeaderLimit( | |
| 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 | } | |
| 35 | 268 | |
| 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; | |
| 269 | /// Drains all remaining buffered data. | |
| 270 | pub fn flush(w: *Writer) Error!void { | |
| 271 | return w.vtable.flush(w); | |
| 272 | } | |
| 273 | ||
| 274 | /// Repeatedly calls `VTable.drain` until `end` is zero. | |
| 275 | pub 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. | |
| 281 | pub 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. | |
| 287 | pub 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 | ||
| 297 | pub fn unusedCapacitySlice(w: *const Writer) []u8 { | |
| 298 | return w.buffer[w.end..]; | |
| 299 | } | |
| 300 | ||
| 301 | pub 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`. | |
| 308 | pub 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`. | |
| 317 | pub 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`. | |
| 328 | pub 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 | } |
| 43 | 337 | |
| 44 | pub 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`. | |
| 347 | pub 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 | ||
| 357 | pub 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 | ||
| 386 | pub const VectorWrapper = struct { | |
| 387 | writer: Writer, | |
| 388 | it: WritableVectorIterator, | |
| 389 | pub const vtable: VTable = .{ .drain = fixedDrain }; | |
| 390 | }; | |
| 391 | ||
| 392 | pub 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 | ||
| 400 | pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit) Error![]std.posix.iovec { | |
| 401 | var it = try writableVectorIterator(w); | |
| 45 | 402 | var i: usize = 0; |
| 46 | while (i < n) : (i += 1) { | |
| 47 | try self.writeAll(bytes); | |
| 403 | var remaining = limit; | |
| 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 | ||
| 416 | pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void { | |
| 417 | _ = try writableSliceGreedy(w, n); | |
| 418 | } | |
| 419 | ||
| 420 | pub 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`. | |
| 429 | pub 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. | |
| 438 | pub 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`. | |
| 445 | pub 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 | } |
| 50 | 461 | |
| 51 | pub 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`. | |
| 464 | pub 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 | ||
| 492 | pub 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`. | |
| 506 | pub 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. | |
| 528 | pub 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`. | |
| 540 | pub 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 | ||
| 545 | pub 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. | |
| 550 | pub 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. | |
| 567 | pub 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. | |
| 580 | pub 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`. | |
| 588 | pub 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. | |
| 594 | pub 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`. | |
| 607 | pub 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. | |
| 612 | pub inline fn writeInt(w: *Writer, comptime T: type, value: T, endian: std.builtin.Endian) Error!void { | |
| 52 | 613 | 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); | |
| 614 | std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian); | |
| 615 | return w.writeAll(&bytes); | |
| 55 | 616 | } |
| 56 | 617 | |
| 57 | pub fn writeStruct(self: Self, value: anytype) anyerror!void { | |
| 618 | pub fn writeStruct(w: *Writer, value: anytype) Error!void { | |
| 58 | 619 | // Only extern and packed structs have defined in-memory layout. |
| 59 | 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 | } |
| 62 | 623 | |
| 63 | pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void { | |
| 64 | // TODO: make sure this value is not a reference type | |
| 624 | /// The function is inline to avoid the dead code in case `endian` is | |
| 625 | /// comptime-known and matches host endianness. | |
| 626 | /// TODO: make sure this value is not a reference type | |
| 627 | pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void { | |
| 65 | 628 | if (native_endian == endian) { |
| 66 | return self.writeStruct(value); | |
| 629 | return w.writeStruct(value); | |
| 67 | 630 | } else { |
| 68 | 631 | var copy = value; |
| 69 | mem.byteSwapAllFields(@TypeOf(value), &copy); | |
| 70 | return self.writeStruct(copy); | |
| 632 | std.mem.byteSwapAllFields(@TypeOf(value), &copy); | |
| 633 | return w.writeStruct(copy); | |
| 634 | } | |
| 635 | } | |
| 636 | ||
| 637 | pub 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)` | |
| 651 | pub 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. | |
| 668 | pub 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. | |
| 673 | pub 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. | |
| 694 | pub 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 | ||
| 701 | pub 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. | |
| 724 | pub 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 | } |
| 73 | 734 | |
| 74 | pub 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; | |
| 735 | pub fn alignBuffer( | |
| 736 | w: *Writer, | |
| 737 | buffer: []const u8, | |
| 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 | ||
| 766 | pub 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 | ||
| 770 | pub 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 | ||
| 794 | pub 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 | ||
| 1059 | fn 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 | ||
| 1064 | pub 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 | ||
| 1113 | pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { | |
| 1114 | return w.alignBufferOptions(@as(*const [1]u8, &c), options); | |
| 1115 | } | |
| 1116 | ||
| 1117 | pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { | |
| 1118 | return w.alignBufferOptions(bytes, options); | |
| 1119 | } | |
| 1120 | ||
| 1121 | pub 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 | ||
| 1127 | pub 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 | ||
| 1195 | pub 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 | ||
| 1226 | pub 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 | ||
| 1325 | pub 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 | |
| 1335 | pub 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 | |
| 1389 | const ANY = "any"; | |
| 1390 | ||
| 1391 | fn 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 | ||
| 1398 | pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn { | |
| 1399 | @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); | |
| 1400 | } | |
| 1401 | ||
| 1402 | pub fn printDurationSigned(w: *Writer, ns: i64) Error!void { | |
| 1403 | if (ns < 0) try w.writeByte('-'); | |
| 1404 | return w.printDurationUnsigned(@abs(ns)); | |
| 1405 | } | |
| 1406 | ||
| 1407 | pub 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`. | |
| 1456 | pub 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 | ||
| 1467 | pub 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 | ||
| 1478 | pub 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. | |
| 1487 | pub 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. | |
| 1499 | pub 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. | |
| 1511 | pub 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 | ||
| 1519 | fn 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 | 1523 | while (true) { |
| 79 | const n = try file.readAll(&buf); | |
| 80 | try self.writeAll(buf[0..n]); | |
| 81 | if (n < buf.len) return; | |
| 1524 | const buffer: []packed struct(u8) { bits: u7, more: bool } = @ptrCast(try w.writableSliceGreedy(1)); | |
| 1525 | for (buffer, 1..) |*byte, len| { | |
| 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 | ||
| 1550 | test "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 | ||
| 1625 | test 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 | ||
| 1662 | test 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 | ||
| 1730 | fn 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 | ||
| 1737 | fn 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 | ||
| 1744 | test 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 | ||
| 1762 | test "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 | ||
| 1769 | test "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 | ||
| 1777 | fn 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 | ||
| 1784 | test 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 | ||
| 1799 | test "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 | ||
| 1809 | test 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 | ||
| 1825 | test "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 | ||
| 1848 | pub fn failingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { | |
| 1849 | _ = w; | |
| 1850 | _ = data; | |
| 1851 | _ = splat; | |
| 1852 | return error.WriteFailed; | |
| 1853 | } | |
| 1854 | ||
| 1855 | pub 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 | ||
| 1862 | pub 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 | ||
| 1871 | pub 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. | |
| 1892 | pub 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`. | |
| 1905 | pub 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. | |
| 1912 | pub 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. | |
| 1923 | pub 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. | |
| 1965 | pub 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`. | |
| 2054 | pub 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 | 11 | |
| 12 | 12 | pub const buffer_size = 4096; |
| 13 | 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); | |
| 15 | 15 | |
| 16 | 16 | /// TODO when https://github.com/ziglang/zig/issues/2761 is solved |
| 17 | 17 | /// 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 | 12 | end: usize = 0, |
| 13 | 13 | |
| 14 | 14 | pub const Error = ReaderType.Error; |
| 15 | pub const Reader = io.Reader(*Self, Error, read); | |
| 15 | pub const Reader = io.GenericReader(*Self, Error, read); | |
| 16 | 16 | |
| 17 | 17 | const Self = @This(); |
| 18 | 18 | |
| ... | ... | @@ -61,7 +61,7 @@ test "OneByte" { |
| 61 | 61 | |
| 62 | 62 | const Error = error{NoError}; |
| 63 | 63 | const Self = @This(); |
| 64 | const Reader = io.Reader(*Self, Error, read); | |
| 64 | const Reader = io.GenericReader(*Self, Error, read); | |
| 65 | 65 | |
| 66 | 66 | fn init(str: []const u8) Self { |
| 67 | 67 | return Self{ |
| ... | ... | @@ -105,7 +105,7 @@ test "Block" { |
| 105 | 105 | |
| 106 | 106 | const Error = error{NoError}; |
| 107 | 107 | const Self = @This(); |
| 108 | const Reader = io.Reader(*Self, Error, read); | |
| 108 | const Reader = io.GenericReader(*Self, Error, read); | |
| 109 | 109 | |
| 110 | 110 | fn init(block: []const u8, reads_allowed: usize) Self { |
| 111 | 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 | 10 | end: usize = 0, |
| 11 | 11 | |
| 12 | 12 | pub const Error = WriterType.Error; |
| 13 | pub const Writer = io.Writer(*Self, Error, write); | |
| 13 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 14 | 14 | |
| 15 | 15 | const Self = @This(); |
| 16 | 16 |
lib/std/io/c_writer.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const testing = std.testing; |
| 5 | 5 | |
| 6 | pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite); | |
| 6 | pub const CWriter = io.GenericWriter(*std.c.FILE, std.fs.File.WriteError, cWriterWrite); | |
| 7 | 7 | |
| 8 | 8 | pub fn cWriter(c_file: *std.c.FILE) CWriter { |
| 9 | 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 | 8 | return struct { |
| 9 | 9 | const Self = @This(); |
| 10 | 10 | pub const Error = WriterType.Error; |
| 11 | pub const Writer = io.Writer(*Self, Error, write); | |
| 11 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 12 | 12 | |
| 13 | 13 | anything_changed: bool, |
| 14 | 14 | underlying_writer: WriterType, |
lib/std/io/counting_reader.zig+1-1| ... | ... | @@ -9,7 +9,7 @@ pub fn CountingReader(comptime ReaderType: anytype) type { |
| 9 | 9 | bytes_read: u64 = 0, |
| 10 | 10 | |
| 11 | 11 | pub const Error = ReaderType.Error; |
| 12 | pub const Reader = io.Reader(*@This(), Error, read); | |
| 12 | pub const Reader = io.GenericReader(*@This(), Error, read); | |
| 13 | 13 | |
| 14 | 14 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 15 | 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 | 9 | child_stream: WriterType, |
| 10 | 10 | |
| 11 | 11 | pub const Error = WriterType.Error; |
| 12 | pub const Writer = io.Writer(*Self, Error, write); | |
| 12 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 13 | 13 | |
| 14 | 14 | const Self = @This(); |
| 15 | 15 |
lib/std/io/find_byte_writer.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type { |
| 8 | 8 | return struct { |
| 9 | 9 | const Self = @This(); |
| 10 | 10 | pub const Error = UnderlyingWriter.Error; |
| 11 | pub const Writer = io.Writer(*Self, Error, write); | |
| 11 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 12 | 12 | |
| 13 | 13 | underlying_writer: UnderlyingWriter, |
| 14 | 14 | byte_found: bool, |
lib/std/io/fixed_buffer_stream.zig+4-4| ... | ... | @@ -4,8 +4,8 @@ const testing = std.testing; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | |
| 7 | /// This turns a byte buffer into an `io.Writer`, `io.Reader`, or `io.SeekableStream`. | |
| 8 | /// If the supplied byte buffer is const, then `io.Writer` is not available. | |
| 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.GenericWriter` is not available. | |
| 9 | 9 | pub fn FixedBufferStream(comptime Buffer: type) type { |
| 10 | 10 | return struct { |
| 11 | 11 | /// `Buffer` is either a `[]u8` or `[]const u8`. |
| ... | ... | @@ -17,8 +17,8 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 17 | 17 | pub const SeekError = error{}; |
| 18 | 18 | pub const GetSeekPosError = error{}; |
| 19 | 19 | |
| 20 | pub const Reader = io.Reader(*Self, ReadError, read); | |
| 21 | pub const Writer = io.Writer(*Self, WriteError, write); | |
| 20 | pub const Reader = io.GenericReader(*Self, ReadError, read); | |
| 21 | pub const Writer = io.GenericWriter(*Self, WriteError, write); | |
| 22 | 22 | |
| 23 | 23 | pub const SeekableStream = io.SeekableStream( |
| 24 | 24 | *Self, |
lib/std/io/limited_reader.zig+1-1| ... | ... | @@ -9,7 +9,7 @@ pub fn LimitedReader(comptime ReaderType: type) type { |
| 9 | 9 | bytes_left: u64, |
| 10 | 10 | |
| 11 | 11 | pub const Error = ReaderType.Error; |
| 12 | pub const Reader = io.Reader(*Self, Error, read); | |
| 12 | pub const Reader = io.GenericReader(*Self, Error, read); | |
| 13 | 13 | |
| 14 | 14 | const Self = @This(); |
| 15 | 15 |
lib/std/io/multi_writer.zig+1-1| ... | ... | @@ -15,7 +15,7 @@ pub fn MultiWriter(comptime Writers: type) type { |
| 15 | 15 | streams: Writers, |
| 16 | 16 | |
| 17 | 17 | pub const Error = ErrSet; |
| 18 | pub const Writer = io.Writer(*Self, Error, write); | |
| 18 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 19 | 19 | |
| 20 | 20 | pub fn writer(self: *Self) Writer { |
| 21 | 21 | return .{ .context = self }; |
lib/std/io/stream_source.zig+4-4| ... | ... | @@ -2,9 +2,9 @@ const std = @import("../std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const io = std.io; |
| 4 | 4 | |
| 5 | /// Provides `io.Reader`, `io.Writer`, and `io.SeekableStream` for in-memory buffers as | |
| 5 | /// Provides `io.GenericReader`, `io.GenericWriter`, and `io.SeekableStream` for in-memory buffers as | |
| 6 | 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 | 8 | /// The error set of the stream functions is the error set of the corresponding file functions. |
| 9 | 9 | pub const StreamSource = union(enum) { |
| 10 | 10 | // 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 | 26 | pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{}); |
| 27 | 27 | pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{}); |
| 28 | 28 | |
| 29 | pub const Reader = io.Reader(*StreamSource, ReadError, read); | |
| 30 | pub const Writer = io.Writer(*StreamSource, WriteError, write); | |
| 29 | pub const Reader = io.GenericReader(*StreamSource, ReadError, read); | |
| 30 | pub const Writer = io.GenericWriter(*StreamSource, WriteError, write); | |
| 31 | 31 | pub const SeekableStream = io.SeekableStream( |
| 32 | 32 | *StreamSource, |
| 33 | 33 | SeekError, |
lib/std/json.zig+2-2| ... | ... | @@ -1,12 +1,12 @@ |
| 1 | 1 | //! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259 |
| 2 | 2 | //! |
| 3 | 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 | 6 | //! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type. |
| 7 | 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 | 10 | //! The high-level `stringify` serializes a Zig or `Value` type into JSON. |
| 11 | 11 | |
| 12 | 12 | const builtin = @import("builtin"); |
lib/std/json/scanner.zig+1-1| ... | ... | @@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always }; |
| 219 | 219 | /// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`. |
| 220 | 220 | pub const default_max_value_len = 4 * 1024 * 1024; |
| 221 | 221 | |
| 222 | /// Connects a `std.io.Reader` to a `std.json.Scanner`. | |
| 222 | /// Connects a `std.io.GenericReader` to a `std.json.Scanner`. | |
| 223 | 223 | /// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader. |
| 224 | 224 | pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type { |
| 225 | 225 | return struct { |
lib/std/json/stringify.zig+2-2| ... | ... | @@ -38,7 +38,7 @@ pub const StringifyOptions = struct { |
| 38 | 38 | emit_nonportable_numbers_as_strings: bool = false, |
| 39 | 39 | }; |
| 40 | 40 | |
| 41 | /// Writes the given value to the `std.io.Writer` stream. | |
| 41 | /// Writes the given value to the `std.io.GenericWriter` stream. | |
| 42 | 42 | /// See `WriteStream` for how the given value is serialized into JSON. |
| 43 | 43 | /// The maximum nesting depth of the output JSON document is 256. |
| 44 | 44 | /// See also `stringifyMaxDepth` and `stringifyArbitraryDepth`. |
| ... | ... | @@ -81,7 +81,7 @@ pub fn stringifyArbitraryDepth( |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | 83 | /// 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 | 86 | /// Caller owns returned memory. |
| 87 | 87 | pub fn stringifyAlloc( |
lib/std/json/stringify_test.zig+1-1| ... | ... | @@ -307,7 +307,7 @@ test "stringify tuple" { |
| 307 | 307 | fn testStringify(expected: []const u8, value: anytype, options: StringifyOptions) !void { |
| 308 | 308 | const ValidationWriter = struct { |
| 309 | 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 | 311 | pub const Error = error{ |
| 312 | 312 | TooMuchData, |
| 313 | 313 | DifferentData, |
lib/std/net.zig+2-2| ... | ... | @@ -1845,8 +1845,8 @@ pub const Stream = struct { |
| 1845 | 1845 | pub const ReadError = posix.ReadError; |
| 1846 | 1846 | pub const WriteError = posix.WriteError; |
| 1847 | 1847 | |
| 1848 | pub const Reader = io.Reader(Stream, ReadError, read); | |
| 1849 | pub const Writer = io.Writer(Stream, WriteError, write); | |
| 1848 | pub const Reader = io.GenericReader(Stream, ReadError, read); | |
| 1849 | pub const Writer = io.GenericWriter(Stream, WriteError, write); | |
| 1850 | 1850 | |
| 1851 | 1851 | pub fn reader(self: Stream) Reader { |
| 1852 | 1852 | return .{ .context = self }; |
lib/std/os/uefi/protocol/file.zig+2-2| ... | ... | @@ -88,8 +88,8 @@ pub const File = extern struct { |
| 88 | 88 | getPosition, |
| 89 | 89 | getEndPos, |
| 90 | 90 | ); |
| 91 | pub const Reader = io.Reader(*File, ReadError, read); | |
| 92 | pub const Writer = io.Writer(*File, WriteError, write); | |
| 91 | pub const Reader = io.GenericReader(*File, ReadError, read); | |
| 92 | pub const Writer = io.GenericWriter(*File, WriteError, write); | |
| 93 | 93 | |
| 94 | 94 | pub fn seekableStream(self: *File) SeekableStream { |
| 95 | 95 | return .{ .context = self }; |
lib/std/tar.zig+1-1| ... | ... | @@ -348,7 +348,7 @@ pub fn Iterator(comptime ReaderType: type) type { |
| 348 | 348 | unread_bytes: *u64, |
| 349 | 349 | parent_reader: ReaderType, |
| 350 | 350 | |
| 351 | pub const Reader = std.io.Reader(File, ReaderType.Error, File.read); | |
| 351 | pub const Reader = std.io.GenericReader(File, ReaderType.Error, File.read); | |
| 352 | 352 | |
| 353 | 353 | pub fn reader(self: File) Reader { |
| 354 | 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 | 9520 | backing_writer: BackingWriter, |
| 9521 | 9521 | |
| 9522 | 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); | |
| 9524 | 9524 | |
| 9525 | 9525 | const Self = @This(); |
| 9526 | 9526 |
lib/std/zig/render.zig+1-1| ... | ... | @@ -3245,7 +3245,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type { |
| 3245 | 3245 | return struct { |
| 3246 | 3246 | const Self = @This(); |
| 3247 | 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); | |
| 3249 | 3249 | |
| 3250 | 3250 | pub const IndentType = enum { |
| 3251 | 3251 | normal, |
lib/std/zig/string_literal.zig+1-1| ... | ... | @@ -322,7 +322,7 @@ test parseCharLiteral { |
| 322 | 322 | ); |
| 323 | 323 | } |
| 324 | 324 | |
| 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 | 326 | /// Asserts `bytes` has '"' at beginning and end. |
| 327 | 327 | pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result { |
| 328 | 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 | 106 | /// Find and return the end record for the given seekable zip stream. |
| 107 | 107 | /// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and |
| 108 | 108 | /// its context must also have a `.reader()` method that returns an instance of |
| 109 | /// `std.io.Reader`. | |
| 109 | /// `std.io.GenericReader`. | |
| 110 | 110 | pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord { |
| 111 | 111 | var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined; |
| 112 | 112 | const record_len_max = @min(stream_len, buf.len); |
| ... | ... | @@ -617,7 +617,7 @@ pub const ExtractOptions = struct { |
| 617 | 617 | /// Extract the zipped files inside `seekable_stream` to the given `dest` directory. |
| 618 | 618 | /// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and |
| 619 | 619 | /// its context must also have a `.reader()` method that returns an instance of |
| 620 | /// `std.io.Reader`. | |
| 620 | /// `std.io.GenericReader`. | |
| 621 | 621 | pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptions) !void { |
| 622 | 622 | const SeekableStream = @TypeOf(seekable_stream); |
| 623 | 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 | 1026 | ProtocolError, |
| 1027 | 1027 | UnexpectedPacket, |
| 1028 | 1028 | }; |
| 1029 | pub const Reader = std.io.Reader(*FetchStream, ReadError, read); | |
| 1029 | pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read); | |
| 1030 | 1030 | |
| 1031 | 1031 | const StreamCode = enum(u8) { |
| 1032 | 1032 | pack_data = 1, |