| 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,7 +2949,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype) |
| 2949 | slurped_header: [size]u8 = [_]u8{0x00} ** size, | 2949 | slurped_header: [size]u8 = [_]u8{0x00} ** size, |
| 2950 | 2950 | ||
| 2951 | pub const Error = ReaderType.Error; | 2951 | pub const Error = ReaderType.Error; |
| 2952 | pub const Reader = std.io.Reader(*@This(), Error, read); | 2952 | pub const Reader = std.io.GenericReader(*@This(), Error, read); |
| 2953 | 2953 | ||
| 2954 | pub fn read(self: *@This(), buf: []u8) Error!usize { | 2954 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 2955 | const amt = try self.child_reader.read(buf); | 2955 | const amt = try self.child_reader.read(buf); |
| ... | @@ -2983,7 +2983,7 @@ pub fn LimitedWriter(comptime WriterType: type) type { | ... | @@ -2983,7 +2983,7 @@ pub fn LimitedWriter(comptime WriterType: type) type { |
| 2983 | bytes_left: u64, | 2983 | bytes_left: u64, |
| 2984 | 2984 | ||
| 2985 | pub const Error = error{NoSpaceLeft} || WriterType.Error; | 2985 | pub const Error = error{NoSpaceLeft} || WriterType.Error; |
| 2986 | pub const Writer = std.io.Writer(*Self, Error, write); | 2986 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 2987 | 2987 | ||
| 2988 | const Self = @This(); | 2988 | const Self = @This(); |
| 2989 | 2989 |
lib/compiler/resinator/main.zig+1-1| ... | @@ -471,7 +471,7 @@ const IoStream = struct { | ... | @@ -471,7 +471,7 @@ const IoStream = struct { |
| 471 | allocator: std.mem.Allocator, | 471 | allocator: std.mem.Allocator, |
| 472 | }; | 472 | }; |
| 473 | pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError; | 473 | pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError; |
| 474 | pub const Writer = std.io.Writer(WriterContext, WriteError, write); | 474 | pub const Writer = std.io.GenericWriter(WriterContext, WriteError, write); |
| 475 | 475 | ||
| 476 | pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize { | 476 | pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize { |
| 477 | switch (ctx.self.*) { | 477 | switch (ctx.self.*) { |
lib/std/array_list.zig+37-18| ... | @@ -338,11 +338,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty | ... | @@ -338,11 +338,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 338 | @memcpy(self.items[old_len..][0..items.len], items); | 338 | @memcpy(self.items[old_len..][0..items.len], items); |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | pub const Writer = if (T != u8) | 341 | pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { |
| 342 | @compileError("The Writer interface is only defined for ArrayList(u8) " ++ | 342 | const gpa = self.allocator; |
| 343 | "but the given type is ArrayList(" ++ @typeName(T) ++ ")") | 343 | var unmanaged = self.moveToUnmanaged(); |
| 344 | else | 344 | defer self.* = unmanaged.toManaged(gpa); |
| 345 | std.io.Writer(*Self, Allocator.Error, appendWrite); | 345 | try unmanaged.print(gpa, fmt, args); |
| 346 | } | ||
| 347 | |||
| 348 | pub const Writer = if (T != u8) void else std.io.GenericWriter(*Self, Allocator.Error, appendWrite); | ||
| 346 | 349 | ||
| 347 | /// Initializes a Writer which will append to the list. | 350 | /// Initializes a Writer which will append to the list. |
| 348 | pub fn writer(self: *Self) Writer { | 351 | pub fn writer(self: *Self) Writer { |
| ... | @@ -350,14 +353,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty | ... | @@ -350,14 +353,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 350 | } | 353 | } |
| 351 | 354 | ||
| 352 | /// Same as `append` except it returns the number of bytes written, which is always the same | 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 | /// Invalidates element pointers if additional memory is needed. | 357 | /// Invalidates element pointers if additional memory is needed. |
| 355 | fn appendWrite(self: *Self, m: []const u8) Allocator.Error!usize { | 358 | fn appendWrite(self: *Self, m: []const u8) Allocator.Error!usize { |
| 356 | try self.appendSlice(m); | 359 | try self.appendSlice(m); |
| 357 | return m.len; | 360 | return m.len; |
| 358 | } | 361 | } |
| 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 | /// Initializes a Writer which will append to the list but will return | 365 | /// Initializes a Writer which will append to the list but will return |
| 363 | /// `error.OutOfMemory` rather than increasing capacity. | 366 | /// `error.OutOfMemory` rather than increasing capacity. |
| ... | @@ -365,7 +368,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty | ... | @@ -365,7 +368,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 365 | return .{ .context = self }; | 368 | return .{ .context = self }; |
| 366 | } | 369 | } |
| 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 | fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { | 372 | fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { |
| 370 | const available_capacity = self.capacity - self.items.len; | 373 | const available_capacity = self.capacity - self.items.len; |
| 371 | if (m.len > available_capacity) | 374 | if (m.len > available_capacity) |
| ... | @@ -933,40 +936,56 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig | ... | @@ -933,40 +936,56 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 933 | @memcpy(self.items[old_len..][0..items.len], items); | 936 | @memcpy(self.items[old_len..][0..items.len], items); |
| 934 | } | 937 | } |
| 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 | pub const WriterContext = struct { | 957 | pub const WriterContext = struct { |
| 937 | self: *Self, | 958 | self: *Self, |
| 938 | allocator: Allocator, | 959 | allocator: Allocator, |
| 939 | }; | 960 | }; |
| 940 | 961 | ||
| 962 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. | ||
| 941 | pub const Writer = if (T != u8) | 963 | pub const Writer = if (T != u8) |
| 942 | @compileError("The Writer interface is only defined for ArrayList(u8) " ++ | 964 | @compileError("The Writer interface is only defined for ArrayList(u8) " ++ |
| 943 | "but the given type is ArrayList(" ++ @typeName(T) ++ ")") | 965 | "but the given type is ArrayList(" ++ @typeName(T) ++ ")") |
| 944 | else | 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 | pub fn writer(self: *Self, gpa: Allocator) Writer { | 970 | pub fn writer(self: *Self, gpa: Allocator) Writer { |
| 949 | return .{ .context = .{ .self = self, .allocator = gpa } }; | 971 | return .{ .context = .{ .self = self, .allocator = gpa } }; |
| 950 | } | 972 | } |
| 951 | 973 | ||
| 952 | /// Same as `append` except it returns the number of bytes written, | 974 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. |
| 953 | /// which is always the same as `m.len`. The purpose of this function | ||
| 954 | /// existing is to match `std.io.Writer` API. | ||
| 955 | /// Invalidates element pointers if additional memory is needed. | ||
| 956 | fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize { | 975 | fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize { |
| 957 | try context.self.appendSlice(context.allocator, m); | 976 | try context.self.appendSlice(context.allocator, m); |
| 958 | return m.len; | 977 | return m.len; |
| 959 | } | 978 | } |
| 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 | 983 | /// Deprecated in favor of `print` or `std.io.Writer.Allocating`. |
| 964 | /// `error.OutOfMemory` rather than increasing capacity. | ||
| 965 | pub fn fixedWriter(self: *Self) FixedWriter { | 984 | pub fn fixedWriter(self: *Self) FixedWriter { |
| 966 | return .{ .context = self }; | 985 | return .{ .context = self }; |
| 967 | } | 986 | } |
| 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 | fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { | 989 | fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { |
| 971 | const available_capacity = self.capacity - self.items.len; | 990 | const available_capacity = self.capacity - self.items.len; |
| 972 | if (m.len > available_capacity) | 991 | if (m.len > available_capacity) |
lib/std/base64.zig+3-3| ... | @@ -108,7 +108,7 @@ pub const Base64Encoder = struct { | ... | @@ -108,7 +108,7 @@ pub const Base64Encoder = struct { |
| 108 | } | 108 | } |
| 109 | } | 109 | } |
| 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 | pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void { | 112 | pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void { |
| 113 | var chunker = window(u8, source, 3, 3); | 113 | var chunker = window(u8, source, 3, 3); |
| 114 | while (chunker.next()) |chunk| { | 114 | while (chunker.next()) |chunk| { |
| ... | @@ -118,8 +118,8 @@ pub const Base64Encoder = struct { | ... | @@ -118,8 +118,8 @@ pub const Base64Encoder = struct { |
| 118 | } | 118 | } |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | // destWriter must be compatible with std.io.Writer's writeAll interface | 121 | // destWriter must be compatible with std.io.GenericWriter's writeAll interface |
| 122 | // sourceReader must be compatible with std.io.Reader's read interface | 122 | // sourceReader must be compatible with `std.io.GenericReader` read interface |
| 123 | pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void { | 123 | pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void { |
| 124 | while (true) { | 124 | while (true) { |
| 125 | var tempSource: [3]u8 = undefined; | 125 | var tempSource: [3]u8 = undefined; |
lib/std/bounded_array.zig+2-2| ... | @@ -277,7 +277,7 @@ pub fn BoundedArrayAligned( | ... | @@ -277,7 +277,7 @@ pub fn BoundedArrayAligned( |
| 277 | @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ | 277 | @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ |
| 278 | "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") | 278 | "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") |
| 279 | else | 279 | else |
| 280 | std.io.Writer(*Self, error{Overflow}, appendWrite); | 280 | std.io.GenericWriter(*Self, error{Overflow}, appendWrite); |
| 281 | 281 | ||
| 282 | /// Initializes a writer which will write into the array. | 282 | /// Initializes a writer which will write into the array. |
| 283 | pub fn writer(self: *Self) Writer { | 283 | pub fn writer(self: *Self) Writer { |
| ... | @@ -285,7 +285,7 @@ pub fn BoundedArrayAligned( | ... | @@ -285,7 +285,7 @@ pub fn BoundedArrayAligned( |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | /// Same as `appendSlice` except it returns the number of bytes written, which is always the same | 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 | fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { | 289 | fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { |
| 290 | try self.appendSlice(m); | 290 | try self.appendSlice(m); |
| 291 | return m.len; | 291 | return m.len; |
lib/std/compress.zig+2-2| ... | @@ -16,7 +16,7 @@ pub fn HashedReader(ReaderType: type, HasherType: type) type { | ... | @@ -16,7 +16,7 @@ pub fn HashedReader(ReaderType: type, HasherType: type) type { |
| 16 | hasher: HasherType, | 16 | hasher: HasherType, |
| 17 | 17 | ||
| 18 | pub const Error = ReaderType.Error; | 18 | pub const Error = ReaderType.Error; |
| 19 | pub const Reader = std.io.Reader(*@This(), Error, read); | 19 | pub const Reader = std.io.GenericReader(*@This(), Error, read); |
| 20 | 20 | ||
| 21 | pub fn read(self: *@This(), buf: []u8) Error!usize { | 21 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 22 | const amt = try self.child_reader.read(buf); | 22 | const amt = try self.child_reader.read(buf); |
| ... | @@ -43,7 +43,7 @@ pub fn HashedWriter(WriterType: type, HasherType: type) type { | ... | @@ -43,7 +43,7 @@ pub fn HashedWriter(WriterType: type, HasherType: type) type { |
| 43 | hasher: HasherType, | 43 | hasher: HasherType, |
| 44 | 44 | ||
| 45 | pub const Error = WriterType.Error; | 45 | pub const Error = WriterType.Error; |
| 46 | pub const Writer = std.io.Writer(*@This(), Error, write); | 46 | pub const Writer = std.io.GenericWriter(*@This(), Error, write); |
| 47 | 47 | ||
| 48 | pub fn write(self: *@This(), buf: []const u8) Error!usize { | 48 | pub fn write(self: *@This(), buf: []const u8) Error!usize { |
| 49 | const amt = try self.child_writer.write(buf); | 49 | const amt = try self.child_writer.write(buf); |
lib/std/compress/flate/deflate.zig+2-2| ... | @@ -355,7 +355,7 @@ fn Deflate(comptime container: Container, comptime WriterType: type, comptime Bl | ... | @@ -355,7 +355,7 @@ fn Deflate(comptime container: Container, comptime WriterType: type, comptime Bl |
| 355 | 355 | ||
| 356 | // Writer interface | 356 | // Writer interface |
| 357 | 357 | ||
| 358 | pub const Writer = io.Writer(*Self, Error, write); | 358 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 359 | pub const Error = BlockWriterType.Error; | 359 | pub const Error = BlockWriterType.Error; |
| 360 | 360 | ||
| 361 | /// Write `input` of uncompressed data. | 361 | /// Write `input` of uncompressed data. |
| ... | @@ -512,7 +512,7 @@ fn SimpleCompressor( | ... | @@ -512,7 +512,7 @@ fn SimpleCompressor( |
| 512 | 512 | ||
| 513 | // Writer interface | 513 | // Writer interface |
| 514 | 514 | ||
| 515 | pub const Writer = io.Writer(*Self, Error, write); | 515 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 516 | pub const Error = BlockWriterType.Error; | 516 | pub const Error = BlockWriterType.Error; |
| 517 | 517 | ||
| 518 | // Write `input` of uncompressed data. | 518 | // Write `input` of uncompressed data. |
lib/std/compress/flate/inflate.zig+1-1| ... | @@ -341,7 +341,7 @@ pub fn Inflate(comptime container: Container, comptime LookaheadType: type, comp | ... | @@ -341,7 +341,7 @@ pub fn Inflate(comptime container: Container, comptime LookaheadType: type, comp |
| 341 | 341 | ||
| 342 | // Reader interface | 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 | /// Returns the number of bytes read. It may be less than buffer.len. | 346 | /// Returns the number of bytes read. It may be less than buffer.len. |
| 347 | /// If the number of bytes read is 0, it means end of stream. | 347 | /// If the number of bytes read is 0, it means end of stream. |
lib/std/compress/lzma.zig+1-1| ... | @@ -30,7 +30,7 @@ pub fn Decompress(comptime ReaderType: type) type { | ... | @@ -30,7 +30,7 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 30 | Allocator.Error || | 30 | Allocator.Error || |
| 31 | error{ CorruptInput, EndOfStream, Overflow }; | 31 | error{ CorruptInput, EndOfStream, Overflow }; |
| 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 | allocator: Allocator, | 35 | allocator: Allocator, |
| 36 | in_reader: ReaderType, | 36 | in_reader: ReaderType, |
lib/std/compress/xz.zig+1-1| ... | @@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type { | ... | @@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 34 | const Self = @This(); | 34 | const Self = @This(); |
| 35 | 35 | ||
| 36 | pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error; | 36 | pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error; |
| 37 | pub const Reader = std.io.Reader(*Self, Error, read); | 37 | pub const Reader = std.io.GenericReader(*Self, Error, read); |
| 38 | 38 | ||
| 39 | allocator: Allocator, | 39 | allocator: Allocator, |
| 40 | block_decoder: block.Decoder(ReaderType), | 40 | block_decoder: block.Decoder(ReaderType), |
lib/std/compress/xz/block.zig+1-1| ... | @@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 27 | ReaderType.Error || | 27 | ReaderType.Error || |
| 28 | DecodeError || | 28 | DecodeError || |
| 29 | Allocator.Error; | 29 | Allocator.Error; |
| 30 | pub const Reader = std.io.Reader(*Self, Error, read); | 30 | pub const Reader = std.io.GenericReader(*Self, Error, read); |
| 31 | 31 | ||
| 32 | allocator: Allocator, | 32 | allocator: Allocator, |
| 33 | inner_reader: ReaderType, | 33 | inner_reader: ReaderType, |
lib/std/compress/zstandard.zig+1-1| ... | @@ -50,7 +50,7 @@ pub fn Decompressor(comptime ReaderType: type) type { | ... | @@ -50,7 +50,7 @@ pub fn Decompressor(comptime ReaderType: type) type { |
| 50 | OutOfMemory, | 50 | OutOfMemory, |
| 51 | }; | 51 | }; |
| 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 | pub fn init(source: ReaderType, options: DecompressorOptions) Self { | 55 | pub fn init(source: ReaderType, options: DecompressorOptions) Self { |
| 56 | return .{ | 56 | return .{ |
lib/std/compress/zstandard/readers.zig+1-1| ... | @@ -4,7 +4,7 @@ pub const ReversedByteReader = struct { | ... | @@ -4,7 +4,7 @@ pub const ReversedByteReader = struct { |
| 4 | remaining_bytes: usize, | 4 | remaining_bytes: usize, |
| 5 | bytes: []const u8, | 5 | bytes: []const u8, |
| 6 | 6 | ||
| 7 | const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); | 7 | const Reader = std.io.GenericReader(*ReversedByteReader, error{}, readFn); |
| 8 | 8 | ||
| 9 | pub fn init(bytes: []const u8) ReversedByteReader { | 9 | pub fn init(bytes: []const u8) ReversedByteReader { |
| 10 | return .{ | 10 | return .{ |
lib/std/crypto/aegis.zig+1-1| ... | @@ -803,7 +803,7 @@ fn AegisMac(comptime T: type) type { | ... | @@ -803,7 +803,7 @@ fn AegisMac(comptime T: type) type { |
| 803 | } | 803 | } |
| 804 | 804 | ||
| 805 | pub const Error = error{}; | 805 | pub const Error = error{}; |
| 806 | pub const Writer = std.io.Writer(*Mac, Error, write); | 806 | pub const Writer = std.io.GenericWriter(*Mac, Error, write); |
| 807 | 807 | ||
| 808 | fn write(self: *Mac, bytes: []const u8) Error!usize { | 808 | fn write(self: *Mac, bytes: []const u8) Error!usize { |
| 809 | self.update(bytes); | 809 | self.update(bytes); |
lib/std/crypto/blake2.zig+1-1| ... | @@ -187,7 +187,7 @@ pub fn Blake2s(comptime out_bits: usize) type { | ... | @@ -187,7 +187,7 @@ pub fn Blake2s(comptime out_bits: usize) type { |
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | pub const Error = error{}; | 189 | pub const Error = error{}; |
| 190 | pub const Writer = std.io.Writer(*Self, Error, write); | 190 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 191 | 191 | ||
| 192 | fn write(self: *Self, bytes: []const u8) Error!usize { | 192 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 193 | self.update(bytes); | 193 | self.update(bytes); |
lib/std/crypto/blake3.zig+1-1| ... | @@ -476,7 +476,7 @@ pub const Blake3 = struct { | ... | @@ -476,7 +476,7 @@ pub const Blake3 = struct { |
| 476 | } | 476 | } |
| 477 | 477 | ||
| 478 | pub const Error = error{}; | 478 | pub const Error = error{}; |
| 479 | pub const Writer = std.io.Writer(*Blake3, Error, write); | 479 | pub const Writer = std.io.GenericWriter(*Blake3, Error, write); |
| 480 | 480 | ||
| 481 | fn write(self: *Blake3, bytes: []const u8) Error!usize { | 481 | fn write(self: *Blake3, bytes: []const u8) Error!usize { |
| 482 | self.update(bytes); | 482 | self.update(bytes); |
lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig+1-1| ... | @@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void { | ... | @@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void { |
| 45 | self.data.ptr = begin; | 45 | self.data.ptr = begin; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize); | 48 | pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize); |
| 49 | /// Warning: This writer writes backwards. `fn print` will NOT work as expected. | 49 | /// Warning: This writer writes backwards. `fn print` will NOT work as expected. |
| 50 | pub fn writer(self: *ArrayListReverse) Writer { | 50 | pub fn writer(self: *ArrayListReverse) Writer { |
| 51 | return .{ .context = self }; | 51 | return .{ .context = self }; |
lib/std/crypto/sha1.zig+1-1| ... | @@ -269,7 +269,7 @@ pub const Sha1 = struct { | ... | @@ -269,7 +269,7 @@ pub const Sha1 = struct { |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | pub const Error = error{}; | 271 | pub const Error = error{}; |
| 272 | pub const Writer = std.io.Writer(*Self, Error, write); | 272 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 273 | 273 | ||
| 274 | fn write(self: *Self, bytes: []const u8) Error!usize { | 274 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 275 | self.update(bytes); | 275 | self.update(bytes); |
lib/std/crypto/sha2.zig+1-1| ... | @@ -376,7 +376,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { | ... | @@ -376,7 +376,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { |
| 376 | } | 376 | } |
| 377 | 377 | ||
| 378 | pub const Error = error{}; | 378 | pub const Error = error{}; |
| 379 | pub const Writer = std.io.Writer(*Self, Error, write); | 379 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 380 | 380 | ||
| 381 | fn write(self: *Self, bytes: []const u8) Error!usize { | 381 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 382 | self.update(bytes); | 382 | self.update(bytes); |
lib/std/crypto/sha3.zig+5-5| ... | @@ -82,7 +82,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim | ... | @@ -82,7 +82,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | pub const Error = error{}; | 84 | pub const Error = error{}; |
| 85 | pub const Writer = std.io.Writer(*Self, Error, write); | 85 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 86 | 86 | ||
| 87 | fn write(self: *Self, bytes: []const u8) Error!usize { | 87 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 88 | self.update(bytes); | 88 | self.update(bytes); |
| ... | @@ -193,7 +193,7 @@ fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime | ... | @@ -193,7 +193,7 @@ fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | pub const Error = error{}; | 195 | pub const Error = error{}; |
| 196 | pub const Writer = std.io.Writer(*Self, Error, write); | 196 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 197 | 197 | ||
| 198 | fn write(self: *Self, bytes: []const u8) Error!usize { | 198 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 199 | self.update(bytes); | 199 | self.update(bytes); |
| ... | @@ -286,7 +286,7 @@ fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime | ... | @@ -286,7 +286,7 @@ fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | pub const Error = error{}; | 288 | pub const Error = error{}; |
| 289 | pub const Writer = std.io.Writer(*Self, Error, write); | 289 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 290 | 290 | ||
| 291 | fn write(self: *Self, bytes: []const u8) Error!usize { | 291 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 292 | self.update(bytes); | 292 | self.update(bytes); |
| ... | @@ -392,7 +392,7 @@ fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime r | ... | @@ -392,7 +392,7 @@ fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime r |
| 392 | } | 392 | } |
| 393 | 393 | ||
| 394 | pub const Error = error{}; | 394 | pub const Error = error{}; |
| 395 | pub const Writer = std.io.Writer(*Self, Error, write); | 395 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 396 | 396 | ||
| 397 | fn write(self: *Self, bytes: []const u8) Error!usize { | 397 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 398 | self.update(bytes); | 398 | self.update(bytes); |
| ... | @@ -484,7 +484,7 @@ fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, compt | ... | @@ -484,7 +484,7 @@ fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, compt |
| 484 | } | 484 | } |
| 485 | 485 | ||
| 486 | pub const Error = error{}; | 486 | pub const Error = error{}; |
| 487 | pub const Writer = std.io.Writer(*Self, Error, write); | 487 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 488 | 488 | ||
| 489 | fn write(self: *Self, bytes: []const u8) Error!usize { | 489 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 490 | self.update(bytes); | 490 | self.update(bytes); |
lib/std/crypto/siphash.zig+1-1| ... | @@ -240,7 +240,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) | ... | @@ -240,7 +240,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) |
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | pub const Error = error{}; | 242 | pub const Error = error{}; |
| 243 | pub const Writer = std.io.Writer(*Self, Error, write); | 243 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 244 | 244 | ||
| 245 | fn write(self: *Self, bytes: []const u8) Error!usize { | 245 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 246 | self.update(bytes); | 246 | self.update(bytes); |
lib/std/debug/Pdb.zig+1-1| ... | @@ -562,7 +562,7 @@ const MsfStream = struct { | ... | @@ -562,7 +562,7 @@ const MsfStream = struct { |
| 562 | return block * self.block_size + offset; | 562 | return block * self.block_size + offset; |
| 563 | } | 563 | } |
| 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 | return .{ .context = self }; | 566 | return .{ .context = self }; |
| 567 | } | 567 | } |
| 568 | }; | 568 | }; |
lib/std/fifo.zig+4-4| ... | @@ -38,8 +38,8 @@ pub fn LinearFifo( | ... | @@ -38,8 +38,8 @@ pub fn LinearFifo( |
| 38 | count: usize, | 38 | count: usize, |
| 39 | 39 | ||
| 40 | const Self = @This(); | 40 | const Self = @This(); |
| 41 | pub const Reader = std.io.Reader(*Self, error{}, readFn); | 41 | pub const Reader = std.io.GenericReader(*Self, error{}, readFn); |
| 42 | pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite); | 42 | pub const Writer = std.io.GenericWriter(*Self, error{OutOfMemory}, appendWrite); |
| 43 | 43 | ||
| 44 | // Type of Self argument for slice operations. | 44 | // Type of Self argument for slice operations. |
| 45 | // If buffer is inline (Static) then we need to ensure we haven't | 45 | // If buffer is inline (Static) then we need to ensure we haven't |
| ... | @@ -231,7 +231,7 @@ pub fn LinearFifo( | ... | @@ -231,7 +231,7 @@ pub fn LinearFifo( |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | /// Same as `read` except it returns an error union | 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 | fn readFn(self: *Self, dest: []u8) error{}!usize { | 235 | fn readFn(self: *Self, dest: []u8) error{}!usize { |
| 236 | return self.read(dest); | 236 | return self.read(dest); |
| 237 | } | 237 | } |
| ... | @@ -320,7 +320,7 @@ pub fn LinearFifo( | ... | @@ -320,7 +320,7 @@ pub fn LinearFifo( |
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | /// Same as `write` except it returns the number of bytes written, which is always the same | 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 | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { | 324 | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { |
| 325 | try self.write(bytes); | 325 | try self.write(bytes); |
| 326 | return bytes.len; | 326 | return bytes.len; |
lib/std/fs/File.zig+2-2| ... | @@ -1581,13 +1581,13 @@ fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix | ... | @@ -1581,13 +1581,13 @@ fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix |
| 1581 | } | 1581 | } |
| 1582 | } | 1582 | } |
| 1583 | 1583 | ||
| 1584 | pub const Reader = io.Reader(File, ReadError, read); | 1584 | pub const Reader = io.GenericReader(File, ReadError, read); |
| 1585 | 1585 | ||
| 1586 | pub fn reader(file: File) Reader { | 1586 | pub fn reader(file: File) Reader { |
| 1587 | return .{ .context = file }; | 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 | pub fn writer(file: File) Writer { | 1592 | pub fn writer(file: File) Writer { |
| 1593 | return .{ .context = file }; | 1593 | return .{ .context = file }; |
lib/std/http/Client.zig+5-5| ... | @@ -311,7 +311,7 @@ pub const Connection = struct { | ... | @@ -311,7 +311,7 @@ pub const Connection = struct { |
| 311 | EndOfStream, | 311 | EndOfStream, |
| 312 | }; | 312 | }; |
| 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 | pub fn reader(conn: *Connection) Reader { | 316 | pub fn reader(conn: *Connection) Reader { |
| 317 | return Reader{ .context = conn }; | 317 | return Reader{ .context = conn }; |
| ... | @@ -374,7 +374,7 @@ pub const Connection = struct { | ... | @@ -374,7 +374,7 @@ pub const Connection = struct { |
| 374 | UnexpectedWriteFailure, | 374 | UnexpectedWriteFailure, |
| 375 | }; | 375 | }; |
| 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 | pub fn writer(conn: *Connection) Writer { | 379 | pub fn writer(conn: *Connection) Writer { |
| 380 | return Writer{ .context = conn }; | 380 | return Writer{ .context = conn }; |
| ... | @@ -934,7 +934,7 @@ pub const Request = struct { | ... | @@ -934,7 +934,7 @@ pub const Request = struct { |
| 934 | 934 | ||
| 935 | const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError; | 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 | fn transferReader(req: *Request) TransferReader { | 939 | fn transferReader(req: *Request) TransferReader { |
| 940 | return .{ .context = req }; | 940 | return .{ .context = req }; |
| ... | @@ -1094,7 +1094,7 @@ pub const Request = struct { | ... | @@ -1094,7 +1094,7 @@ pub const Request = struct { |
| 1094 | pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || | 1094 | pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || |
| 1095 | error{ DecompressionFailure, InvalidTrailers }; | 1095 | error{ DecompressionFailure, InvalidTrailers }; |
| 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 | pub fn reader(req: *Request) Reader { | 1099 | pub fn reader(req: *Request) Reader { |
| 1100 | return .{ .context = req }; | 1100 | return .{ .context = req }; |
| ... | @@ -1134,7 +1134,7 @@ pub const Request = struct { | ... | @@ -1134,7 +1134,7 @@ pub const Request = struct { |
| 1134 | 1134 | ||
| 1135 | pub const WriteError = Connection.WriteError || error{ NotWriteable, MessageTooLong }; | 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 | pub fn writer(req: *Request) Writer { | 1139 | pub fn writer(req: *Request) Writer { |
| 1140 | return .{ .context = req }; | 1140 | return .{ .context = req }; |
lib/std/http/protocol.zig+2-2| ... | @@ -344,7 +344,7 @@ const MockBufferedConnection = struct { | ... | @@ -344,7 +344,7 @@ const MockBufferedConnection = struct { |
| 344 | } | 344 | } |
| 345 | 345 | ||
| 346 | pub const ReadError = std.io.FixedBufferStream([]const u8).ReadError || error{EndOfStream}; | 346 | pub const ReadError = std.io.FixedBufferStream([]const u8).ReadError || error{EndOfStream}; |
| 347 | pub const Reader = std.io.Reader(*MockBufferedConnection, ReadError, read); | 347 | pub const Reader = std.io.GenericReader(*MockBufferedConnection, ReadError, read); |
| 348 | 348 | ||
| 349 | pub fn reader(conn: *MockBufferedConnection) Reader { | 349 | pub fn reader(conn: *MockBufferedConnection) Reader { |
| 350 | return Reader{ .context = conn }; | 350 | return Reader{ .context = conn }; |
| ... | @@ -359,7 +359,7 @@ const MockBufferedConnection = struct { | ... | @@ -359,7 +359,7 @@ const MockBufferedConnection = struct { |
| 359 | } | 359 | } |
| 360 | 360 | ||
| 361 | pub const WriteError = std.io.FixedBufferStream([]const u8).WriteError; | 361 | pub const WriteError = std.io.FixedBufferStream([]const u8).WriteError; |
| 362 | pub const Writer = std.io.Writer(*MockBufferedConnection, WriteError, write); | 362 | pub const Writer = std.io.GenericWriter(*MockBufferedConnection, WriteError, write); |
| 363 | 363 | ||
| 364 | pub fn writer(conn: *MockBufferedConnection) Writer { | 364 | pub fn writer(conn: *MockBufferedConnection) Writer { |
| 365 | return Writer{ .context = conn }; | 365 | return Writer{ .context = conn }; |
lib/std/io.zig+71-11| ... | @@ -14,6 +14,69 @@ const File = std.fs.File; | ... | @@ -14,6 +14,69 @@ const File = std.fs.File; |
| 14 | const Allocator = std.mem.Allocator; | 14 | const Allocator = std.mem.Allocator; |
| 15 | const Alignment = std.mem.Alignment; | 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 | fn getStdOutHandle() posix.fd_t { | 80 | fn getStdOutHandle() posix.fd_t { |
| 18 | if (is_windows) { | 81 | if (is_windows) { |
| 19 | return windows.peb().ProcessParameters.hStdOutput; | 82 | return windows.peb().ProcessParameters.hStdOutput; |
| ... | @@ -62,6 +125,7 @@ pub fn getStdIn() File { | ... | @@ -62,6 +125,7 @@ pub fn getStdIn() File { |
| 62 | return .{ .handle = getStdInHandle() }; | 125 | return .{ .handle = getStdInHandle() }; |
| 63 | } | 126 | } |
| 64 | 127 | ||
| 128 | /// Deprecated in favor of `Reader`. | ||
| 65 | pub fn GenericReader( | 129 | pub fn GenericReader( |
| 66 | comptime Context: type, | 130 | comptime Context: type, |
| 67 | comptime ReadError: type, | 131 | comptime ReadError: type, |
| ... | @@ -289,6 +353,7 @@ pub fn GenericReader( | ... | @@ -289,6 +353,7 @@ pub fn GenericReader( |
| 289 | }; | 353 | }; |
| 290 | } | 354 | } |
| 291 | 355 | ||
| 356 | /// Deprecated in favor of `Writer`. | ||
| 292 | pub fn GenericWriter( | 357 | pub fn GenericWriter( |
| 293 | comptime Context: type, | 358 | comptime Context: type, |
| 294 | comptime WriteError: type, | 359 | comptime WriteError: type, |
| ... | @@ -350,15 +415,10 @@ pub fn GenericWriter( | ... | @@ -350,15 +415,10 @@ pub fn GenericWriter( |
| 350 | }; | 415 | }; |
| 351 | } | 416 | } |
| 352 | 417 | ||
| 353 | /// Deprecated; consider switching to `AnyReader` or use `GenericReader` | 418 | /// Deprecated in favor of `Reader`. |
| 354 | /// to use previous API. | 419 | pub const AnyReader = @import("io/DeprecatedReader.zig"); |
| 355 | pub const Reader = GenericReader; | 420 | /// Deprecated in favor of `Writer`. |
| 356 | /// Deprecated; consider switching to `AnyWriter` or use `GenericWriter` | 421 | pub const AnyWriter = @import("io/DeprecatedWriter.zig"); |
| 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"); | ||
| 362 | 422 | ||
| 363 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | 423 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 364 | 424 | ||
| ... | @@ -819,8 +879,8 @@ pub fn PollFiles(comptime StreamEnum: type) type { | ... | @@ -819,8 +879,8 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 819 | } | 879 | } |
| 820 | 880 | ||
| 821 | test { | 881 | test { |
| 822 | _ = AnyReader; | 882 | _ = Reader; |
| 823 | _ = AnyWriter; | 883 | _ = Writer; |
| 824 | _ = @import("io/bit_reader.zig"); | 884 | _ = @import("io/bit_reader.zig"); |
| 825 | _ = @import("io/bit_writer.zig"); | 885 | _ = @import("io/bit_writer.zig"); |
| 826 | _ = @import("io/buffered_atomic_file.zig"); | 886 | _ = @import("io/buffered_atomic_file.zig"); |
lib/std/io/DeprecatedReader.zig created+386| ... | @@ -0,0 +1,386 @@ | ||
| 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,386 +1,1471 @@ |
| 1 | context: *const anyopaque, | 1 | const Reader = @This(); |
| 2 | readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize, | ||
| 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. | 6 | const std = @import("../std.zig"); |
| 7 | /// If the number of bytes read is 0, it means end of stream. | 7 | const Writer = std.io.Writer; |
| 8 | /// End of stream is not an error condition. | 8 | const assert = std.debug.assert; |
| 9 | pub fn read(self: Self, buffer: []u8) anyerror!usize { | 9 | const testing = std.testing; |
| 10 | return self.readFn(self.context, buffer); | 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 | 108 | /// Constructs a `Reader` such that it will read from `buffer` and then end. |
| 14 | /// means the stream reached the end. Reaching the end of a stream is not an error | 109 | pub fn fixed(buffer: []const u8) Reader { |
| 15 | /// condition. | 110 | return .{ |
| 16 | pub fn readAll(self: Self, buffer: []u8) anyerror!usize { | 111 | .vtable = &.{ |
| 17 | return readAtLeast(self, buffer, buffer.len); | 112 | .stream = endingStream, |
| 113 | .discard = endingDiscard, | ||
| 114 | }, | ||
| 115 | // This cast is safe because all potential writes to it will instead | ||
| 116 | // return `error.EndOfStream`. | ||
| 117 | .buffer = @constCast(buffer), | ||
| 118 | .end = buffer.len, | ||
| 119 | .seek = 0, | ||
| 120 | }; | ||
| 18 | } | 121 | } |
| 19 | 122 | ||
| 20 | /// Returns the number of bytes read, calling the underlying read | 123 | pub fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { |
| 21 | /// function the minimal number of times until the buffer has at least | 124 | const buffer = limit.slice(r.buffer[r.seek..r.end]); |
| 22 | /// `len` bytes filled. If the number read is less than `len` it means | 125 | if (buffer.len > 0) { |
| 23 | /// the stream reached the end. Reaching the end of the stream is not | 126 | @branchHint(.likely); |
| 24 | /// an error condition. | 127 | const n = try w.write(buffer); |
| 25 | pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize { | 128 | r.seek += n; |
| 26 | assert(len <= buffer.len); | 129 | return n; |
| 27 | var index: usize = 0; | 130 | } |
| 28 | while (index < len) { | 131 | const before = w.count; |
| 29 | const amt = try self.read(buffer[index..]); | 132 | const n = try r.vtable.stream(r, w, limit); |
| 30 | if (amt == 0) break; | 133 | assert(n <= @intFromEnum(limit)); |
| 31 | index += amt; | 134 | assert(w.count == before + n); |
| 135 | return n; | ||
| 136 | } | ||
| 137 | |||
| 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; | 170 | return n; |
| 34 | } | 171 | } |
| 35 | 172 | ||
| 36 | /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. | 173 | /// "Pump" exactly `n` bytes from the reader to the writer. |
| 37 | pub fn readNoEof(self: Self, buf: []u8) anyerror!void { | 174 | pub fn streamExact(r: *Reader, w: *Writer, n: usize) StreamError!void { |
| 38 | const amt_read = try self.readAll(buf); | 175 | var remaining = n; |
| 39 | if (amt_read < buf.len) return error.EndOfStream; | 176 | while (remaining != 0) remaining -= try r.stream(w, .limited(remaining)); |
| 40 | } | 177 | } |
| 41 | 178 | ||
| 42 | /// Appends to the `std.ArrayList` contents by reading from the stream | 179 | /// "Pump" data from the reader to the writer, handling `error.EndOfStream` as |
| 43 | /// until end of stream is found. | 180 | /// a success case. |
| 44 | /// If the number of bytes appended would exceed `max_append_size`, | 181 | /// |
| 45 | /// `error.StreamTooLong` is returned | 182 | /// Returns total number of bytes written to `w`. |
| 46 | /// and the `std.ArrayList` has exactly `max_append_size` bytes appended. | 183 | pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize { |
| 47 | pub fn readAllArrayList( | 184 | var offset: usize = 0; |
| 48 | self: Self, | ||
| 49 | array_list: *std.ArrayList(u8), | ||
| 50 | max_append_size: usize, | ||
| 51 | ) anyerror!void { | ||
| 52 | return self.readAllArrayListAligned(null, array_list, max_append_size); | ||
| 53 | } | ||
| 54 | |||
| 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) { | 185 | while (true) { |
| 65 | array_list.expandToCapacity(); | 186 | offset += r.stream(w, .unlimited) catch |err| switch (err) { |
| 66 | const dest_slice = array_list.items[start_index..]; | 187 | error.EndOfStream => return offset, |
| 67 | const bytes_read = try self.readAll(dest_slice); | 188 | else => |e| return e, |
| 68 | start_index += bytes_read; | 189 | }; |
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Consumes the stream until the end, ignoring all the data, returning the | ||
| 194 | /// number of bytes discarded. | ||
| 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) { | 227 | /// Transfers all bytes from the current position to the end of the stream, up |
| 71 | array_list.shrinkAndFree(original_len + max_append_size); | 228 | /// to `limit`, appending them to `list`. |
| 229 | /// | ||
| 230 | /// If `limit` would be exceeded, `error.StreamTooLong` is returned instead. In | ||
| 231 | /// such case, the next byte that would be read will be the first one to exceed | ||
| 232 | /// `limit`, and all preceeding bytes have been appended to `list`. | ||
| 233 | /// | ||
| 234 | /// Asserts `buffer` has nonzero capacity. | ||
| 235 | /// | ||
| 236 | /// See also: | ||
| 237 | /// * `allocRemaining` | ||
| 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 | return error.StreamTooLong; | 269 | return error.StreamTooLong; |
| 73 | } | 270 | } |
| 271 | list.items.len += n; | ||
| 272 | remaining = remaining.subtract(n).?; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Writes bytes from the internally tracked stream position to `data`. | ||
| 277 | /// | ||
| 278 | /// Returns the number of bytes written, which will be at minimum `0` and | ||
| 279 | /// at most the sum of each data slice length. The number of bytes read, | ||
| 280 | /// including zero, does not indicate end of stream. | ||
| 281 | /// | ||
| 282 | /// The reader's internal logical seek position moves forward in accordance | ||
| 283 | /// with the number of bytes returned from this function. | ||
| 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) { | 288 | /// Equivalent to `readVec` but reads at most `limit` bytes. |
| 76 | array_list.shrinkAndFree(start_index); | 289 | /// |
| 77 | return; | 290 | /// This ultimately will lower to a call to `stream`, but it must ensure |
| 291 | /// that the buffer used has at least as much capacity, in case that function | ||
| 292 | /// depends on a minimum buffer capacity. It also ensures that if the `stream` | ||
| 293 | /// implementation calls `Writer.writableVector`, it will get this data slice | ||
| 294 | /// along with the buffer at the end. | ||
| 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. | 373 | pub fn readVecAll(r: *Reader, data: [][]u8) Error!void { |
| 81 | try array_list.ensureTotalCapacity(start_index + 1); | 374 | var index: usize = 0; |
| 375 | var truncate: usize = 0; | ||
| 376 | while (index < data.len) { | ||
| 377 | { | ||
| 378 | const untruncated = data[index]; | ||
| 379 | data[index] = untruncated[truncate..]; | ||
| 380 | defer data[index] = untruncated; | ||
| 381 | truncate += try r.readVec(data[index..]); | ||
| 382 | } | ||
| 383 | while (index < data.len and truncate >= data[index].len) { | ||
| 384 | truncate -= data[index].len; | ||
| 385 | index += 1; | ||
| 386 | } | ||
| 82 | } | 387 | } |
| 83 | } | 388 | } |
| 84 | 389 | ||
| 85 | /// Allocates enough memory to hold all the contents of the stream. If the allocated | 390 | /// Returns the next `len` bytes from the stream, filling the buffer as |
| 86 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | 391 | /// necessary. |
| 87 | /// Caller owns returned memory. | 392 | /// |
| 88 | /// If this function returns an error, the contents from the stream read so far are lost. | 393 | /// Invalidates previously returned values from `peek`. |
| 89 | pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 { | 394 | /// |
| 90 | var array_list = std.ArrayList(u8).init(allocator); | 395 | /// Asserts that the `Reader` was initialized with a buffer capacity at |
| 91 | defer array_list.deinit(); | 396 | /// least as big as `len`. |
| 92 | try self.readAllArrayList(&array_list, max_size); | 397 | /// |
| 93 | return try array_list.toOwnedSlice(); | 398 | /// If there are fewer than `len` bytes left in the stream, `error.EndOfStream` |
| 94 | } | 399 | /// is returned instead. |
| 95 | 400 | /// | |
| 96 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. | 401 | /// See also: |
| 97 | /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. | 402 | /// * `peek` |
| 98 | /// Does not include the delimiter in the result. | 403 | /// * `toss` |
| 99 | /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the | 404 | pub fn peek(r: *Reader, n: usize) Error![]u8 { |
| 100 | /// `std.ArrayList` is populated with `max_size` bytes from the stream. | 405 | try r.fill(n); |
| 101 | pub fn readUntilDelimiterArrayList( | 406 | return r.buffer[r.seek..][0..n]; |
| 102 | self: Self, | 407 | } |
| 103 | array_list: *std.ArrayList(u8), | 408 | |
| 104 | delimiter: u8, | 409 | /// Returns all the next buffered bytes, after filling the buffer to ensure it |
| 105 | max_size: usize, | 410 | /// contains at least `n` bytes. |
| 106 | ) anyerror!void { | 411 | /// |
| 107 | array_list.shrinkRetainingCapacity(0); | 412 | /// Invalidates previously returned values from `peek` and `peekGreedy`. |
| 108 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); | 413 | /// |
| 109 | } | 414 | /// Asserts that the `Reader` was initialized with a buffer capacity at |
| 110 | 415 | /// least as big as `n`. | |
| 111 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. | 416 | /// |
| 112 | /// Allocates enough memory to read until `delimiter`. If the allocated | 417 | /// If there are fewer than `n` bytes left in the stream, `error.EndOfStream` |
| 113 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | 418 | /// is returned instead. |
| 114 | /// Caller owns returned memory. | 419 | /// |
| 115 | /// If this function returns an error, the contents from the stream read so far are lost. | 420 | /// See also: |
| 116 | pub fn readUntilDelimiterAlloc( | 421 | /// * `peek` |
| 117 | self: Self, | 422 | /// * `toss` |
| 118 | allocator: mem.Allocator, | 423 | pub fn peekGreedy(r: *Reader, n: usize) Error![]u8 { |
| 119 | delimiter: u8, | 424 | try r.fill(n); |
| 120 | max_size: usize, | 425 | return r.buffer[r.seek..r.end]; |
| 121 | ) anyerror![]u8 { | 426 | } |
| 122 | var array_list = std.ArrayList(u8).init(allocator); | 427 | |
| 123 | defer array_list.deinit(); | 428 | /// Skips the next `n` bytes from the stream, advancing the seek position. This |
| 124 | try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); | 429 | /// is typically and safely used after `peek`. |
| 125 | return try array_list.toOwnedSlice(); | 430 | /// |
| 126 | } | 431 | /// Asserts that the number of bytes buffered is at least as many as `n`. |
| 127 | 432 | /// | |
| 128 | /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. | 433 | /// The "tossed" memory remains alive until a "peek" operation occurs. |
| 129 | /// Reads from the stream until specified byte is found. If the buffer is not | 434 | /// |
| 130 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | 435 | /// See also: |
| 131 | /// If end-of-stream is found, `error.EndOfStream` is returned. | 436 | /// * `peek`. |
| 132 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | 437 | /// * `discard`. |
| 133 | /// delimiter byte is written to the output buffer but is not included | 438 | pub fn toss(r: *Reader, n: usize) void { |
| 134 | /// in the returned slice. | 439 | r.seek += n; |
| 135 | pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 { | 440 | assert(r.seek <= r.end); |
| 136 | var fbs = std.io.fixedBufferStream(buf); | 441 | } |
| 137 | try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len); | 442 | |
| 138 | const output = fbs.getWritten(); | 443 | /// Equivalent to `toss(r.bufferedLen())`. |
| 139 | buf[output.len] = delimiter; // emulating old behaviour | 444 | pub fn tossAll(r: *Reader) void { |
| 140 | return output; | 445 | r.seek = 0; |
| 141 | } | 446 | r.end = 0; |
| 142 | 447 | } | |
| 143 | /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead. | 448 | |
| 144 | /// Allocates enough memory to read until `delimiter` or end-of-stream. | 449 | /// Equivalent to `peek` followed by `toss`. |
| 145 | /// If the allocated memory would be greater than `max_size`, returns | 450 | /// |
| 146 | /// `error.StreamTooLong`. If end-of-stream is found, returns the rest | 451 | /// The data returned is invalidated by the next call to `take`, `peek`, |
| 147 | /// of the stream. If this function is called again after that, returns | 452 | /// `fill`, and functions with those prefixes. |
| 148 | /// null. | 453 | pub fn take(r: *Reader, n: usize) Error![]u8 { |
| 149 | /// Caller owns returned memory. | 454 | const result = try r.peek(n); |
| 150 | /// If this function returns an error, the contents from the stream read so far are lost. | 455 | r.toss(n); |
| 151 | pub fn readUntilDelimiterOrEofAlloc( | 456 | return result; |
| 152 | self: Self, | 457 | } |
| 153 | allocator: mem.Allocator, | 458 | |
| 154 | delimiter: u8, | 459 | /// Returns the next `n` bytes from the stream as an array, filling the buffer |
| 155 | max_size: usize, | 460 | /// as necessary and advancing the seek position `n` bytes. |
| 156 | ) anyerror!?[]u8 { | 461 | /// |
| 157 | var array_list = std.ArrayList(u8).init(allocator); | 462 | /// Asserts that the `Reader` was initialized with a buffer capacity at |
| 158 | defer array_list.deinit(); | 463 | /// least as big as `n`. |
| 159 | self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) { | 464 | /// |
| 160 | error.EndOfStream => if (array_list.items.len == 0) { | 465 | /// If there are fewer than `n` bytes left in the stream, `error.EndOfStream` |
| 161 | return null; | 466 | /// is returned instead. |
| 467 | /// | ||
| 468 | /// See also: | ||
| 469 | /// * `take` | ||
| 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 | else => |e| return e, | 784 | else => |e| return e, |
| 164 | }; | 785 | }; |
| 165 | return try array_list.toOwnedSlice(); | 786 | r.toss(result.len); |
| 166 | } | 787 | return result[0 .. result.len - 1]; |
| 167 | 788 | } | |
| 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 | 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 | else => |e| return e, | 813 | else => |e| return e, |
| 184 | }; | 814 | }; |
| 185 | const output = fbs.getWritten(); | 815 | return result[0 .. result.len - 1]; |
| 186 | buf[output.len] = delimiter; // emulating old behaviour | ||
| 187 | return output; | ||
| 188 | } | 816 | } |
| 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 | /// Does not write the delimiter itself. | 832 | /// Does not write the delimiter itself. |
| 192 | /// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`, | 833 | /// |
| 193 | /// returns `error.StreamTooLong` and finishes appending. | 834 | /// Succeeds if stream ends before delimiter found. |
| 194 | /// If `optional_max_size` is null, appending is unbounded. | 835 | /// |
| 195 | pub fn streamUntilDelimiter( | 836 | /// Returns number of bytes streamed. The end is not signaled to the writer. |
| 196 | self: Self, | 837 | pub fn readDelimiterEnding( |
| 197 | writer: anytype, | 838 | r: *Reader, |
| 839 | w: *Writer, | ||
| 198 | delimiter: u8, | 840 | delimiter: u8, |
| 199 | optional_max_size: ?usize, | 841 | ) StreamRemainingError!usize { |
| 200 | ) anyerror!void { | 842 | const amount, const to = try r.readAny(w, delimiter, .unlimited); |
| 201 | if (optional_max_size) |max_size| { | 843 | return switch (to) { |
| 202 | for (0..max_size) |_| { | 844 | .delimiter, .end => amount, |
| 203 | const byte: u8 = try self.readByte(); | 845 | .limit => unreachable, |
| 204 | if (byte == delimiter) return; | 846 | }; |
| 205 | try writer.writeByte(byte); | 847 | } |
| 206 | } | 848 | |
| 207 | return error.StreamTooLong; | 849 | pub const StreamDelimiterLimitedError = StreamRemainingError || error{ |
| 208 | } else { | 850 | /// Stream ended before the delimiter was found. |
| 209 | while (true) { | 851 | EndOfStream, |
| 210 | const byte: u8 = try self.readByte(); | 852 | /// The delimiter was not found within the limit. |
| 211 | if (byte == delimiter) return; | 853 | StreamTooLong, |
| 212 | try writer.writeByte(byte); | 854 | }; |
| 213 | } | 855 | |
| 214 | // Can not throw `error.StreamTooLong` since there are no boundary. | 856 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. |
| 857 | /// Does not write the delimiter itself. | ||
| 858 | /// | ||
| 859 | /// Returns number of bytes streamed. | ||
| 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 | /// Reads from the stream until specified byte is found, discarding all data, | 900 | /// Reads from the stream until specified byte is found, discarding all data, |
| 219 | /// including the delimiter. | 901 | /// including the delimiter. |
| 220 | /// If end-of-stream is found, this function succeeds. | 902 | /// |
| 221 | pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void { | 903 | /// If end of stream is found, this function succeeds. |
| 222 | while (true) { | 904 | pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void { |
| 223 | const byte = self.readByte() catch |err| switch (err) { | 905 | _ = r; |
| 224 | error.EndOfStream => return, | 906 | _ = delimiter; |
| 225 | else => |e| return e, | 907 | @panic("TODO"); |
| 908 | } | ||
| 909 | |||
| 910 | /// Reads from the stream until specified byte is found, discarding all data, | ||
| 911 | /// excluding the delimiter. | ||
| 912 | /// | ||
| 913 | /// Succeeds if stream ends before delimiter found. | ||
| 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`. | 947 | /// Without advancing the seek position, does exactly one underlying read, filling the buffer as |
| 232 | pub fn readByte(self: Self) anyerror!u8 { | 948 | /// much as possible. This may result in zero bytes added to the buffer, which is not an end of |
| 233 | var result: [1]u8 = undefined; | 949 | /// stream condition. End of stream is communicated via returning `error.EndOfStream`. |
| 234 | const amt_read = try self.read(result[0..]); | 950 | /// |
| 235 | if (amt_read < 1) return error.EndOfStream; | 951 | /// Asserts buffer capacity is at least 1. |
| 236 | return result[0]; | 952 | pub fn fillMore(r: *Reader) Error!void { |
| 237 | } | 953 | rebaseCapacity(r, 1); |
| 238 | 954 | var writer: Writer = .{ | |
| 239 | /// Same as `readByte` except the returned byte is signed. | 955 | .buffer = r.buffer, |
| 240 | pub fn readByteSigned(self: Self) anyerror!i8 { | 956 | .end = r.end, |
| 241 | return @as(i8, @bitCast(try self.readByte())); | 957 | .vtable = &.{ .drain = Writer.fixedDrain }, |
| 242 | } | 958 | }; |
| 243 | 959 | r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { | |
| 244 | /// Reads exactly `num_bytes` bytes and returns as an array. | 960 | error.WriteFailed => unreachable, |
| 245 | /// `num_bytes` must be comptime-known | 961 | else => |e| return e, |
| 246 | pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 { | 962 | }; |
| 247 | var bytes: [num_bytes]u8 = undefined; | 963 | } |
| 248 | try self.readNoEof(&bytes); | 964 | |
| 249 | return bytes; | 965 | /// Returns the next byte from the stream or returns `error.EndOfStream`. |
| 250 | } | 966 | /// |
| 251 | 967 | /// Does not advance the seek position. | |
| 252 | /// Reads bytes until `bounded.len` is equal to `num_bytes`, | 968 | /// |
| 253 | /// or the stream ends. | 969 | /// Asserts the buffer capacity is nonzero. |
| 254 | /// | 970 | pub fn peekByte(r: *Reader) Error!u8 { |
| 255 | /// * it is assumed that `num_bytes` will not exceed `bounded.capacity()` | 971 | const buffer = r.buffer[0..r.end]; |
| 256 | pub fn readIntoBoundedBytes( | 972 | const seek = r.seek; |
| 257 | self: Self, | 973 | if (seek >= buffer.len) { |
| 258 | comptime num_bytes: usize, | 974 | @branchHint(.unlikely); |
| 259 | bounded: *std.BoundedArray(u8, num_bytes), | 975 | try fill(r, 1); |
| 260 | ) anyerror!void { | ||
| 261 | while (bounded.len < num_bytes) { | ||
| 262 | // get at most the number of bytes free in the bounded array | ||
| 263 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); | ||
| 264 | if (bytes_read == 0) return; | ||
| 265 | |||
| 266 | // bytes_read will never be larger than @TypeOf(bounded.len) | ||
| 267 | // due to `self.read` being bounded by `bounded.unusedCapacitySlice()` | ||
| 268 | bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read)); | ||
| 269 | } | 976 | } |
| 977 | return buffer[seek]; | ||
| 270 | } | 978 | } |
| 271 | 979 | ||
| 272 | /// Reads at most `num_bytes` and returns as a bounded array. | 980 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. |
| 273 | pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) { | 981 | /// |
| 274 | var result = std.BoundedArray(u8, num_bytes){}; | 982 | /// Asserts the buffer capacity is nonzero. |
| 275 | try self.readIntoBoundedBytes(num_bytes, &result); | 983 | pub fn takeByte(r: *Reader) Error!u8 { |
| 984 | const result = try peekByte(r); | ||
| 985 | r.seek += 1; | ||
| 276 | return result; | 986 | return result; |
| 277 | } | 987 | } |
| 278 | 988 | ||
| 279 | pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { | 989 | /// Same as `takeByte` except the returned byte is signed. |
| 280 | const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8)); | 990 | pub fn takeByteSigned(r: *Reader) Error!i8 { |
| 281 | return mem.readInt(T, &bytes, endian); | 991 | return @bitCast(try r.takeByte()); |
| 282 | } | 992 | } |
| 283 | 993 | ||
| 284 | pub fn readVarInt( | 994 | /// Asserts the buffer was initialized with a capacity at least `@bitSizeOf(T) / 8`. |
| 285 | self: Self, | 995 | pub inline fn takeInt(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { |
| 286 | comptime ReturnType: type, | 996 | const n = @divExact(@typeInfo(T).int.bits, 8); |
| 287 | endian: std.builtin.Endian, | 997 | return std.mem.readInt(T, try r.takeArray(n), endian); |
| 288 | size: usize, | 998 | } |
| 289 | ) anyerror!ReturnType { | ||
| 290 | assert(size <= @sizeOf(ReturnType)); | ||
| 291 | var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; | ||
| 292 | const bytes = bytes_buf[0..size]; | ||
| 293 | try self.readNoEof(bytes); | ||
| 294 | return mem.readVarInt(ReturnType, bytes, endian); | ||
| 295 | } | ||
| 296 | |||
| 297 | /// Optional parameters for `skipBytes` | ||
| 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 | 999 | ||
| 308 | while (remaining > 0) { | 1000 | /// Asserts the buffer was initialized with a capacity at least `n`. |
| 309 | const amt = @min(remaining, options.buf_size); | 1001 | pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n: usize) Error!Int { |
| 310 | try self.readNoEof(buf[0..amt]); | 1002 | assert(n <= @sizeOf(Int)); |
| 311 | remaining -= amt; | 1003 | return std.mem.readVarInt(Int, try r.take(n), endian); |
| 312 | } | ||
| 313 | } | 1004 | } |
| 314 | 1005 | ||
| 315 | /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice | 1006 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 316 | pub fn isBytes(self: Self, slice: []const u8) anyerror!bool { | 1007 | /// |
| 317 | var i: usize = 0; | 1008 | /// Advances the seek position. |
| 318 | var matches = true; | 1009 | /// |
| 319 | while (i < slice.len) : (i += 1) { | 1010 | /// See also: |
| 320 | if (slice[i] != try self.readByte()) { | 1011 | /// * `peekStruct` |
| 321 | matches = false; | 1012 | pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T { |
| 322 | } | 1013 | // Only extern and packed structs have defined in-memory layout. |
| 323 | } | 1014 | comptime assert(@typeInfo(T).@"struct".layout != .auto); |
| 324 | return matches; | 1015 | return @ptrCast(try r.takeArray(@sizeOf(T))); |
| 325 | } | 1016 | } |
| 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 | // Only extern and packed structs have defined in-memory layout. | 1025 | // Only extern and packed structs have defined in-memory layout. |
| 329 | comptime assert(@typeInfo(T).@"struct".layout != .auto); | 1026 | comptime assert(@typeInfo(T).@"struct".layout != .auto); |
| 330 | var res: [1]T = undefined; | 1027 | return @ptrCast(try r.peekArray(@sizeOf(T))); |
| 331 | try self.readNoEof(mem.sliceAsBytes(res[0..])); | ||
| 332 | return res[0]; | ||
| 333 | } | 1028 | } |
| 334 | 1029 | ||
| 335 | pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { | 1030 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 336 | var res = try self.readStruct(T); | 1031 | /// |
| 337 | if (native_endian != endian) { | 1032 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` |
| 338 | mem.byteSwapAllFields(T, &res); | 1033 | /// when `endian` is comptime-known and matches the host endianness. |
| 339 | } | 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 | return res; | 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 | 1050 | pub const TakeEnumError = Error || error{InvalidEnumTag}; |
| 344 | /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`. | 1051 | |
| 345 | /// TODO optimization taking advantage of most fields being in order | 1052 | /// Reads an integer with the same size as the given enum's tag type. If the |
| 346 | pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum { | 1053 | /// integer matches an enum tag, casts the integer to the enum tag and returns |
| 347 | const E = error{ | 1054 | /// it. Otherwise, returns `error.InvalidEnumTag`. |
| 348 | /// An integer was read, but it did not match any of the tags in the supplied enum. | 1055 | /// |
| 349 | InvalidValue, | 1056 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`. |
| 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"; | 1074 | } |
| 352 | const tag = try self.readInt(type_info.tag_type, endian); | ||
| 353 | 1075 | ||
| 354 | inline for (std.meta.fields(Enum)) |field| { | 1076 | pub const TakeLeb128Error = Error || error{Overflow}; |
| 355 | if (tag == field.value) { | 1077 | |
| 356 | return @field(Enum, field.name); | 1078 | /// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit. |
| 357 | } | 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. | 1136 | fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result { |
| 364 | /// Returns the number of bytes discarded. | 1137 | const result_info = @typeInfo(Result).int; |
| 365 | pub fn discard(self: Self) anyerror!u64 { | 1138 | comptime assert(result_info.bits % 7 == 0); |
| 366 | var trash: [4096]u8 = undefined; | 1139 | var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits; |
| 367 | var index: u64 = 0; | 1140 | const UnsignedResult = @Type(.{ .int = .{ |
| 1141 | .signedness = .unsigned, | ||
| 1142 | .bits = result_info.bits, | ||
| 1143 | } }); | ||
| 1144 | var result: UnsignedResult = 0; | ||
| 1145 | var fits = true; | ||
| 368 | while (true) { | 1146 | while (true) { |
| 369 | const n = try self.read(&trash); | 1147 | const buffer: []const packed struct(u8) { bits: u7, more: bool } = @ptrCast(try r.peekGreedy(1)); |
| 370 | if (n == 0) return index; | 1148 | for (buffer, 1..) |byte, len| { |
| 371 | index += n; | 1149 | if (remaining_bits > 0) { |
| 1150 | result = @shlExact(@as(UnsignedResult, byte.bits), result_info.bits - 7) | | ||
| 1151 | if (result_info.bits > 7) @shrExact(result, 7) else 0; | ||
| 1152 | remaining_bits -= 7; | ||
| 1153 | } else if (fits) fits = switch (result_info.signedness) { | ||
| 1154 | .signed => @as(i7, @bitCast(byte.bits)) == | ||
| 1155 | @as(i7, @truncate(@as(Result, @bitCast(result)) >> (result_info.bits - 1))), | ||
| 1156 | .unsigned => byte.bits == 0, | ||
| 1157 | }; | ||
| 1158 | if (byte.more) continue; | ||
| 1159 | r.toss(len); | ||
| 1160 | return if (fits) @as(Result, @bitCast(result)) >> remaining_bits else error.Overflow; | ||
| 1161 | } | ||
| 1162 | r.toss(buffer.len); | ||
| 372 | } | 1163 | } |
| 373 | } | 1164 | } |
| 374 | 1165 | ||
| 375 | const std = @import("../std.zig"); | 1166 | /// Left-aligns data such that `r.seek` becomes zero. |
| 376 | const Self = @This(); | 1167 | pub fn rebase(r: *Reader) void { |
| 377 | const math = std.math; | 1168 | if (r.seek == 0) return; |
| 378 | const assert = std.debug.assert; | 1169 | const data = r.buffer[r.seek..r.end]; |
| 379 | const mem = std.mem; | 1170 | @memmove(r.buffer[0..data.len], data); |
| 380 | const testing = std.testing; | 1171 | r.seek = 0; |
| 381 | const native_endian = @import("builtin").target.cpu.arch.endian(); | 1172 | r.end = data.len; |
| 382 | const Alignment = std.mem.Alignment; | 1173 | } |
| 1174 | |||
| 1175 | /// Ensures `capacity` more data can be buffered without rebasing, by rebasing | ||
| 1176 | /// if necessary. | ||
| 1177 | /// | ||
| 1178 | /// Asserts `capacity` is within the buffer capacity. | ||
| 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 { | 1260 | test takeSentinel { |
| 385 | _ = @import("Reader/test.zig"); | 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,83 +1,2206 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 2 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 3 | |||
| 4 | const Writer = @This(); | ||
| 1 | const std = @import("../std.zig"); | 5 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 3 | const mem = std.mem; | 7 | const Limit = std.io.Limit; |
| 4 | const native_endian = @import("builtin").target.cpu.arch.endian(); | 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, | 88 | pub const FileAllError = error{ |
| 7 | writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize, | 89 | /// Detailed diagnostics are found on the `File.Reader` struct. |
| 90 | ReadFailed, | ||
| 91 | /// See the `Writer` implementation for detailed diagnostics. | ||
| 92 | WriteFailed, | ||
| 93 | }; | ||
| 8 | 94 | ||
| 9 | const Self = @This(); | 95 | pub const FileReadingError = error{ |
| 10 | pub const Error = anyerror; | 96 | /// Detailed diagnostics are found on the `File.Reader` struct. |
| 97 | ReadFailed, | ||
| 98 | /// See the `Writer` implementation for detailed diagnostics. | ||
| 99 | WriteFailed, | ||
| 100 | /// Reached the end of the file being read. | ||
| 101 | EndOfStream, | ||
| 102 | }; | ||
| 11 | 103 | ||
| 12 | pub fn write(self: Self, bytes: []const u8) anyerror!usize { | 104 | pub const FileError = error{ |
| 13 | return self.writeFn(self.context, bytes); | 105 | /// Detailed diagnostics are found on the `File.Reader` struct. |
| 106 | ReadFailed, | ||
| 107 | /// See the `Writer` implementation for detailed diagnostics. | ||
| 108 | WriteFailed, | ||
| 109 | /// Reached the end of the file being read. | ||
| 110 | EndOfStream, | ||
| 111 | /// Indicates the caller should do its own file reading; the callee cannot | ||
| 112 | /// offer a more efficient implementation. | ||
| 113 | Unimplemented, | ||
| 114 | }; | ||
| 115 | |||
| 116 | /// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless | ||
| 117 | /// modified externally, `count` will always equal `end`. | ||
| 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 { | 125 | pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) { |
| 17 | var index: usize = 0; | 126 | return .{ .out = w, .hasher = hasher }; |
| 18 | while (index != bytes.len) { | 127 | } |
| 19 | index += try self.write(bytes[index..]); | 128 | |
| 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 { | 210 | /// Equivalent to `writeSplat` but writes at most `limit` bytes. |
| 24 | return std.fmt.format(self, format, args); | 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 { | 224 | /// Returns how many bytes were consumed from `header` and `data`. |
| 28 | const array = [1]u8{byte}; | 225 | pub fn writeSplatHeader( |
| 29 | return self.writeAll(&array); | 226 | w: *Writer, |
| 227 | header: []const u8, | ||
| 228 | data: []const []const u8, | ||
| 229 | splat: usize, | ||
| 230 | ) Error!usize { | ||
| 231 | const new_end = w.end + header.len; | ||
| 232 | if (new_end <= w.buffer.len) { | ||
| 233 | @memcpy(w.buffer[w.end..][0..header.len], header); | ||
| 234 | w.end = new_end; | ||
| 235 | w.count += header.len; | ||
| 236 | return header.len + try writeSplat(w, data, splat); | ||
| 237 | } | ||
| 238 | var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size. | ||
| 239 | var i: usize = 1; | ||
| 240 | vecs[0] = header; | ||
| 241 | for (data) |buf| { | ||
| 242 | if (buf.len == 0) continue; | ||
| 243 | vecs[i] = buf; | ||
| 244 | i += 1; | ||
| 245 | if (vecs.len - i == 0) break; | ||
| 246 | } | ||
| 247 | const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1; | ||
| 248 | const n = try w.vtable.drain(w, vecs[0..i], new_splat); | ||
| 249 | w.count += n; | ||
| 250 | return n; | ||
| 30 | } | 251 | } |
| 31 | 252 | ||
| 32 | pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void { | 253 | /// Equivalent to `writeSplatHeader` but writes at most `limit` bytes. |
| 33 | var bytes: [256]u8 = undefined; | 254 | pub fn writeSplatHeaderLimit( |
| 34 | @memset(bytes[0..], byte); | 255 | w: *Writer, |
| 256 | header: []const u8, | ||
| 257 | data: []const []const u8, | ||
| 258 | splat: usize, | ||
| 259 | limit: Limit, | ||
| 260 | ) Error!usize { | ||
| 261 | _ = w; | ||
| 262 | _ = header; | ||
| 263 | _ = data; | ||
| 264 | _ = splat; | ||
| 265 | _ = limit; | ||
| 266 | @panic("TODO"); | ||
| 267 | } | ||
| 35 | 268 | ||
| 36 | var remaining: usize = n; | 269 | /// Drains all remaining buffered data. |
| 37 | while (remaining > 0) { | 270 | pub fn flush(w: *Writer) Error!void { |
| 38 | const to_write = @min(remaining, bytes.len); | 271 | return w.vtable.flush(w); |
| 39 | try self.writeAll(bytes[0..to_write]); | 272 | } |
| 40 | remaining -= to_write; | 273 | |
| 274 | /// Repeatedly calls `VTable.drain` until `end` is zero. | ||
| 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 | var i: usize = 0; | 402 | var i: usize = 0; |
| 46 | while (i < n) : (i += 1) { | 403 | var remaining = limit; |
| 47 | try self.writeAll(bytes); | 404 | while (it.next()) |full_buffer| { |
| 405 | if (!remaining.nonzero()) break; | ||
| 406 | if (buffer.len - i == 0) break; | ||
| 407 | const buf = remaining.slice(full_buffer); | ||
| 408 | if (buf.len == 0) continue; | ||
| 409 | buffer[i] = .{ .base = buf.ptr, .len = buf.len }; | ||
| 410 | i += 1; | ||
| 411 | remaining = remaining.subtract(buf.len).?; | ||
| 412 | } | ||
| 413 | return buffer[0..i]; | ||
| 414 | } | ||
| 415 | |||
| 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 | var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined; | 613 | var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined; |
| 53 | mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian); | 614 | std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian); |
| 54 | return self.writeAll(&bytes); | 615 | return w.writeAll(&bytes); |
| 55 | } | 616 | } |
| 56 | 617 | ||
| 57 | pub fn writeStruct(self: Self, value: anytype) anyerror!void { | 618 | pub fn writeStruct(w: *Writer, value: anytype) Error!void { |
| 58 | // Only extern and packed structs have defined in-memory layout. | 619 | // Only extern and packed structs have defined in-memory layout. |
| 59 | comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto); | 620 | comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto); |
| 60 | return self.writeAll(mem.asBytes(&value)); | 621 | return w.writeAll(std.mem.asBytes(&value)); |
| 61 | } | 622 | } |
| 62 | 623 | ||
| 63 | pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void { | 624 | /// The function is inline to avoid the dead code in case `endian` is |
| 64 | // TODO: make sure this value is not a reference type | 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 | if (native_endian == endian) { | 628 | if (native_endian == endian) { |
| 66 | return self.writeStruct(value); | 629 | return w.writeStruct(value); |
| 67 | } else { | 630 | } else { |
| 68 | var copy = value; | 631 | var copy = value; |
| 69 | mem.byteSwapAllFields(@TypeOf(value), &copy); | 632 | std.mem.byteSwapAllFields(@TypeOf(value), &copy); |
| 70 | return self.writeStruct(copy); | 633 | return w.writeStruct(copy); |
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 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 { | 735 | pub fn alignBuffer( |
| 75 | // TODO: figure out how to adjust std lib abstractions so that this ends up | 736 | w: *Writer, |
| 76 | // doing sendfile or maybe even copy_file_range under the right conditions. | 737 | buffer: []const u8, |
| 77 | var buf: [4000]u8 = undefined; | 738 | width: usize, |
| 739 | alignment: std.fmt.Alignment, | ||
| 740 | fill: u8, | ||
| 741 | ) Error!void { | ||
| 742 | const padding = if (buffer.len < width) width - buffer.len else 0; | ||
| 743 | if (padding == 0) { | ||
| 744 | @branchHint(.likely); | ||
| 745 | return w.writeAll(buffer); | ||
| 746 | } | ||
| 747 | switch (alignment) { | ||
| 748 | .left => { | ||
| 749 | try w.writeAll(buffer); | ||
| 750 | try w.splatByteAll(fill, padding); | ||
| 751 | }, | ||
| 752 | .center => { | ||
| 753 | const left_padding = padding / 2; | ||
| 754 | const right_padding = (padding + 1) / 2; | ||
| 755 | try w.splatByteAll(fill, left_padding); | ||
| 756 | try w.writeAll(buffer); | ||
| 757 | try w.splatByteAll(fill, right_padding); | ||
| 758 | }, | ||
| 759 | .right => { | ||
| 760 | try w.splatByteAll(fill, padding); | ||
| 761 | try w.writeAll(buffer); | ||
| 762 | }, | ||
| 763 | } | ||
| 764 | } | ||
| 765 | |||
| 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 | while (true) { | 1523 | while (true) { |
| 79 | const n = try file.readAll(&buf); | 1524 | const buffer: []packed struct(u8) { bits: u7, more: bool } = @ptrCast(try w.writableSliceGreedy(1)); |
| 80 | try self.writeAll(buf[0..n]); | 1525 | for (buffer, 1..) |*byte, len| { |
| 81 | if (n < buf.len) return; | 1526 | const more = switch (value_info.signedness) { |
| 1527 | .signed => remaining >> 6 != remaining >> (value_info.bits - 1), | ||
| 1528 | .unsigned => remaining > std.math.maxInt(u7), | ||
| 1529 | }; | ||
| 1530 | byte.* = if (@inComptime()) @typeInfo(@TypeOf(buffer)).pointer.child{ | ||
| 1531 | .bits = @bitCast(@as(@Type(.{ .int = .{ | ||
| 1532 | .signedness = value_info.signedness, | ||
| 1533 | .bits = 7, | ||
| 1534 | } }), @truncate(remaining))), | ||
| 1535 | .more = more, | ||
| 1536 | } else .{ | ||
| 1537 | .bits = @bitCast(@as(@Type(.{ .int = .{ | ||
| 1538 | .signedness = value_info.signedness, | ||
| 1539 | .bits = 7, | ||
| 1540 | } }), @truncate(remaining))), | ||
| 1541 | .more = more, | ||
| 1542 | }; | ||
| 1543 | if (value_info.bits > 7) remaining >>= 7; | ||
| 1544 | if (!more) return w.advance(len); | ||
| 1545 | } | ||
| 1546 | w.advance(buffer.len); | ||
| 1547 | } | ||
| 1548 | } | ||
| 1549 | |||
| 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,7 +11,7 @@ pub const BufferedAtomicFile = struct { |
| 11 | 11 | ||
| 12 | pub const buffer_size = 4096; | 12 | pub const buffer_size = 4096; |
| 13 | pub const BufferedWriter = std.io.BufferedWriter(buffer_size, File.Writer); | 13 | pub const BufferedWriter = std.io.BufferedWriter(buffer_size, File.Writer); |
| 14 | pub const Writer = std.io.Writer(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write); | 14 | pub const Writer = std.io.GenericWriter(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write); |
| 15 | 15 | ||
| 16 | /// TODO when https://github.com/ziglang/zig/issues/2761 is solved | 16 | /// TODO when https://github.com/ziglang/zig/issues/2761 is solved |
| 17 | /// this API will not need an allocator | 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,7 +12,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty |
| 12 | end: usize = 0, | 12 | end: usize = 0, |
| 13 | 13 | ||
| 14 | pub const Error = ReaderType.Error; | 14 | pub const Error = ReaderType.Error; |
| 15 | pub const Reader = io.Reader(*Self, Error, read); | 15 | pub const Reader = io.GenericReader(*Self, Error, read); |
| 16 | 16 | ||
| 17 | const Self = @This(); | 17 | const Self = @This(); |
| 18 | 18 | ||
| ... | @@ -61,7 +61,7 @@ test "OneByte" { | ... | @@ -61,7 +61,7 @@ test "OneByte" { |
| 61 | 61 | ||
| 62 | const Error = error{NoError}; | 62 | const Error = error{NoError}; |
| 63 | const Self = @This(); | 63 | const Self = @This(); |
| 64 | const Reader = io.Reader(*Self, Error, read); | 64 | const Reader = io.GenericReader(*Self, Error, read); |
| 65 | 65 | ||
| 66 | fn init(str: []const u8) Self { | 66 | fn init(str: []const u8) Self { |
| 67 | return Self{ | 67 | return Self{ |
| ... | @@ -105,7 +105,7 @@ test "Block" { | ... | @@ -105,7 +105,7 @@ test "Block" { |
| 105 | 105 | ||
| 106 | const Error = error{NoError}; | 106 | const Error = error{NoError}; |
| 107 | const Self = @This(); | 107 | const Self = @This(); |
| 108 | const Reader = io.Reader(*Self, Error, read); | 108 | const Reader = io.GenericReader(*Self, Error, read); |
| 109 | 109 | ||
| 110 | fn init(block: []const u8, reads_allowed: usize) Self { | 110 | fn init(block: []const u8, reads_allowed: usize) Self { |
| 111 | return Self{ | 111 | return Self{ |
lib/std/io/buffered_writer.zig+1-1| ... | @@ -10,7 +10,7 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty | ... | @@ -10,7 +10,7 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty |
| 10 | end: usize = 0, | 10 | end: usize = 0, |
| 11 | 11 | ||
| 12 | pub const Error = WriterType.Error; | 12 | pub const Error = WriterType.Error; |
| 13 | pub const Writer = io.Writer(*Self, Error, write); | 13 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 14 | 14 | ||
| 15 | const Self = @This(); | 15 | const Self = @This(); |
| 16 | 16 |
lib/std/io/c_writer.zig+1-1| ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const testing = std.testing; | 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 | pub fn cWriter(c_file: *std.c.FILE) CWriter { | 8 | pub fn cWriter(c_file: *std.c.FILE) CWriter { |
| 9 | return .{ .context = c_file }; | 9 | return .{ .context = c_file }; |
lib/std/io/change_detection_stream.zig+1-1| ... | @@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type { | ... | @@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type { |
| 8 | return struct { | 8 | return struct { |
| 9 | const Self = @This(); | 9 | const Self = @This(); |
| 10 | pub const Error = WriterType.Error; | 10 | pub const Error = WriterType.Error; |
| 11 | pub const Writer = io.Writer(*Self, Error, write); | 11 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 12 | 12 | ||
| 13 | anything_changed: bool, | 13 | anything_changed: bool, |
| 14 | underlying_writer: WriterType, | 14 | underlying_writer: WriterType, |
lib/std/io/counting_reader.zig+1-1| ... | @@ -9,7 +9,7 @@ pub fn CountingReader(comptime ReaderType: anytype) type { | ... | @@ -9,7 +9,7 @@ pub fn CountingReader(comptime ReaderType: anytype) type { |
| 9 | bytes_read: u64 = 0, | 9 | bytes_read: u64 = 0, |
| 10 | 10 | ||
| 11 | pub const Error = ReaderType.Error; | 11 | pub const Error = ReaderType.Error; |
| 12 | pub const Reader = io.Reader(*@This(), Error, read); | 12 | pub const Reader = io.GenericReader(*@This(), Error, read); |
| 13 | 13 | ||
| 14 | pub fn read(self: *@This(), buf: []u8) Error!usize { | 14 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 15 | const amt = try self.child_reader.read(buf); | 15 | const amt = try self.child_reader.read(buf); |
lib/std/io/counting_writer.zig+1-1| ... | @@ -9,7 +9,7 @@ pub fn CountingWriter(comptime WriterType: type) type { | ... | @@ -9,7 +9,7 @@ pub fn CountingWriter(comptime WriterType: type) type { |
| 9 | child_stream: WriterType, | 9 | child_stream: WriterType, |
| 10 | 10 | ||
| 11 | pub const Error = WriterType.Error; | 11 | pub const Error = WriterType.Error; |
| 12 | pub const Writer = io.Writer(*Self, Error, write); | 12 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 13 | 13 | ||
| 14 | const Self = @This(); | 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,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type { |
| 8 | return struct { | 8 | return struct { |
| 9 | const Self = @This(); | 9 | const Self = @This(); |
| 10 | pub const Error = UnderlyingWriter.Error; | 10 | pub const Error = UnderlyingWriter.Error; |
| 11 | pub const Writer = io.Writer(*Self, Error, write); | 11 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 12 | 12 | ||
| 13 | underlying_writer: UnderlyingWriter, | 13 | underlying_writer: UnderlyingWriter, |
| 14 | byte_found: bool, | 14 | byte_found: bool, |
lib/std/io/fixed_buffer_stream.zig+4-4| ... | @@ -4,8 +4,8 @@ const testing = std.testing; | ... | @@ -4,8 +4,8 @@ const testing = std.testing; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | 6 | ||
| 7 | /// This turns a byte buffer into an `io.Writer`, `io.Reader`, or `io.SeekableStream`. | 7 | /// This turns a byte buffer into an `io.GenericWriter`, `io.GenericReader`, or `io.SeekableStream`. |
| 8 | /// If the supplied byte buffer is const, then `io.Writer` is not available. | 8 | /// If the supplied byte buffer is const, then `io.GenericWriter` is not available. |
| 9 | pub fn FixedBufferStream(comptime Buffer: type) type { | 9 | pub fn FixedBufferStream(comptime Buffer: type) type { |
| 10 | return struct { | 10 | return struct { |
| 11 | /// `Buffer` is either a `[]u8` or `[]const u8`. | 11 | /// `Buffer` is either a `[]u8` or `[]const u8`. |
| ... | @@ -17,8 +17,8 @@ pub fn FixedBufferStream(comptime Buffer: type) type { | ... | @@ -17,8 +17,8 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 17 | pub const SeekError = error{}; | 17 | pub const SeekError = error{}; |
| 18 | pub const GetSeekPosError = error{}; | 18 | pub const GetSeekPosError = error{}; |
| 19 | 19 | ||
| 20 | pub const Reader = io.Reader(*Self, ReadError, read); | 20 | pub const Reader = io.GenericReader(*Self, ReadError, read); |
| 21 | pub const Writer = io.Writer(*Self, WriteError, write); | 21 | pub const Writer = io.GenericWriter(*Self, WriteError, write); |
| 22 | 22 | ||
| 23 | pub const SeekableStream = io.SeekableStream( | 23 | pub const SeekableStream = io.SeekableStream( |
| 24 | *Self, | 24 | *Self, |
lib/std/io/limited_reader.zig+1-1| ... | @@ -9,7 +9,7 @@ pub fn LimitedReader(comptime ReaderType: type) type { | ... | @@ -9,7 +9,7 @@ pub fn LimitedReader(comptime ReaderType: type) type { |
| 9 | bytes_left: u64, | 9 | bytes_left: u64, |
| 10 | 10 | ||
| 11 | pub const Error = ReaderType.Error; | 11 | pub const Error = ReaderType.Error; |
| 12 | pub const Reader = io.Reader(*Self, Error, read); | 12 | pub const Reader = io.GenericReader(*Self, Error, read); |
| 13 | 13 | ||
| 14 | const Self = @This(); | 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,7 +15,7 @@ pub fn MultiWriter(comptime Writers: type) type { |
| 15 | streams: Writers, | 15 | streams: Writers, |
| 16 | 16 | ||
| 17 | pub const Error = ErrSet; | 17 | pub const Error = ErrSet; |
| 18 | pub const Writer = io.Writer(*Self, Error, write); | 18 | pub const Writer = io.GenericWriter(*Self, Error, write); |
| 19 | 19 | ||
| 20 | pub fn writer(self: *Self) Writer { | 20 | pub fn writer(self: *Self) Writer { |
| 21 | return .{ .context = self }; | 21 | return .{ .context = self }; |
lib/std/io/stream_source.zig+4-4| ... | @@ -2,9 +2,9 @@ const std = @import("../std.zig"); | ... | @@ -2,9 +2,9 @@ const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const io = std.io; | 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 | /// well as files. | 6 | /// well as files. |
| 7 | /// For memory sources, if the supplied byte buffer is const, then `io.Writer` is not available. | 7 | /// For memory sources, if the supplied byte buffer is const, then `io.GenericWriter` is not available. |
| 8 | /// The error set of the stream functions is the error set of the corresponding file functions. | 8 | /// The error set of the stream functions is the error set of the corresponding file functions. |
| 9 | pub const StreamSource = union(enum) { | 9 | pub const StreamSource = union(enum) { |
| 10 | // TODO: expose UEFI files to std.os in a way that allows this to be true | 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,8 +26,8 @@ pub const StreamSource = union(enum) { |
| 26 | pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{}); | 26 | pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{}); |
| 27 | pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{}); | 27 | pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{}); |
| 28 | 28 | ||
| 29 | pub const Reader = io.Reader(*StreamSource, ReadError, read); | 29 | pub const Reader = io.GenericReader(*StreamSource, ReadError, read); |
| 30 | pub const Writer = io.Writer(*StreamSource, WriteError, write); | 30 | pub const Writer = io.GenericWriter(*StreamSource, WriteError, write); |
| 31 | pub const SeekableStream = io.SeekableStream( | 31 | pub const SeekableStream = io.SeekableStream( |
| 32 | *StreamSource, | 32 | *StreamSource, |
| 33 | SeekError, | 33 | SeekError, |
lib/std/json.zig+2-2| ... | @@ -1,12 +1,12 @@ | ... | @@ -1,12 +1,12 @@ |
| 1 | //! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259 | 1 | //! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259 |
| 2 | //! | 2 | //! |
| 3 | //! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs, | 3 | //! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs, |
| 4 | //! The `Reader` API connects a `std.io.Reader` to a `Scanner`. | 4 | //! The `Reader` API connects a `std.io.GenericReader` to a `Scanner`. |
| 5 | //! | 5 | //! |
| 6 | //! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type. | 6 | //! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type. |
| 7 | //! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection. | 7 | //! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection. |
| 8 | //! | 8 | //! |
| 9 | //! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.Writer`. | 9 | //! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.GenericWriter`. |
| 10 | //! The high-level `stringify` serializes a Zig or `Value` type into JSON. | 10 | //! The high-level `stringify` serializes a Zig or `Value` type into JSON. |
| 11 | 11 | ||
| 12 | const builtin = @import("builtin"); | 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,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always }; |
| 219 | /// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`. | 219 | /// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`. |
| 220 | pub const default_max_value_len = 4 * 1024 * 1024; | 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 | /// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader. | 223 | /// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader. |
| 224 | pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type { | 224 | pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type { |
| 225 | return struct { | 225 | return struct { |
lib/std/json/stringify.zig+2-2| ... | @@ -38,7 +38,7 @@ pub const StringifyOptions = struct { | ... | @@ -38,7 +38,7 @@ pub const StringifyOptions = struct { |
| 38 | emit_nonportable_numbers_as_strings: bool = false, | 38 | emit_nonportable_numbers_as_strings: bool = false, |
| 39 | }; | 39 | }; |
| 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 | /// See `WriteStream` for how the given value is serialized into JSON. | 42 | /// See `WriteStream` for how the given value is serialized into JSON. |
| 43 | /// The maximum nesting depth of the output JSON document is 256. | 43 | /// The maximum nesting depth of the output JSON document is 256. |
| 44 | /// See also `stringifyMaxDepth` and `stringifyArbitraryDepth`. | 44 | /// See also `stringifyMaxDepth` and `stringifyArbitraryDepth`. |
| ... | @@ -81,7 +81,7 @@ pub fn stringifyArbitraryDepth( | ... | @@ -81,7 +81,7 @@ pub fn stringifyArbitraryDepth( |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | /// Calls `stringifyArbitraryDepth` and stores the result in dynamically allocated memory | 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 | /// Caller owns returned memory. | 86 | /// Caller owns returned memory. |
| 87 | pub fn stringifyAlloc( | 87 | pub fn stringifyAlloc( |
lib/std/json/stringify_test.zig+1-1| ... | @@ -307,7 +307,7 @@ test "stringify tuple" { | ... | @@ -307,7 +307,7 @@ test "stringify tuple" { |
| 307 | fn testStringify(expected: []const u8, value: anytype, options: StringifyOptions) !void { | 307 | fn testStringify(expected: []const u8, value: anytype, options: StringifyOptions) !void { |
| 308 | const ValidationWriter = struct { | 308 | const ValidationWriter = struct { |
| 309 | const Self = @This(); | 309 | const Self = @This(); |
| 310 | pub const Writer = std.io.Writer(*Self, Error, write); | 310 | pub const Writer = std.io.GenericWriter(*Self, Error, write); |
| 311 | pub const Error = error{ | 311 | pub const Error = error{ |
| 312 | TooMuchData, | 312 | TooMuchData, |
| 313 | DifferentData, | 313 | DifferentData, |
lib/std/net.zig+2-2| ... | @@ -1845,8 +1845,8 @@ pub const Stream = struct { | ... | @@ -1845,8 +1845,8 @@ pub const Stream = struct { |
| 1845 | pub const ReadError = posix.ReadError; | 1845 | pub const ReadError = posix.ReadError; |
| 1846 | pub const WriteError = posix.WriteError; | 1846 | pub const WriteError = posix.WriteError; |
| 1847 | 1847 | ||
| 1848 | pub const Reader = io.Reader(Stream, ReadError, read); | 1848 | pub const Reader = io.GenericReader(Stream, ReadError, read); |
| 1849 | pub const Writer = io.Writer(Stream, WriteError, write); | 1849 | pub const Writer = io.GenericWriter(Stream, WriteError, write); |
| 1850 | 1850 | ||
| 1851 | pub fn reader(self: Stream) Reader { | 1851 | pub fn reader(self: Stream) Reader { |
| 1852 | return .{ .context = self }; | 1852 | return .{ .context = self }; |
lib/std/os/uefi/protocol/file.zig+2-2| ... | @@ -88,8 +88,8 @@ pub const File = extern struct { | ... | @@ -88,8 +88,8 @@ pub const File = extern struct { |
| 88 | getPosition, | 88 | getPosition, |
| 89 | getEndPos, | 89 | getEndPos, |
| 90 | ); | 90 | ); |
| 91 | pub const Reader = io.Reader(*File, ReadError, read); | 91 | pub const Reader = io.GenericReader(*File, ReadError, read); |
| 92 | pub const Writer = io.Writer(*File, WriteError, write); | 92 | pub const Writer = io.GenericWriter(*File, WriteError, write); |
| 93 | 93 | ||
| 94 | pub fn seekableStream(self: *File) SeekableStream { | 94 | pub fn seekableStream(self: *File) SeekableStream { |
| 95 | return .{ .context = self }; | 95 | return .{ .context = self }; |
lib/std/tar.zig+1-1| ... | @@ -348,7 +348,7 @@ pub fn Iterator(comptime ReaderType: type) type { | ... | @@ -348,7 +348,7 @@ pub fn Iterator(comptime ReaderType: type) type { |
| 348 | unread_bytes: *u64, | 348 | unread_bytes: *u64, |
| 349 | parent_reader: ReaderType, | 349 | parent_reader: ReaderType, |
| 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 | pub fn reader(self: File) Reader { | 353 | pub fn reader(self: File) Reader { |
| 354 | return .{ .context = self }; | 354 | return .{ .context = self }; |
lib/std/zig/llvm/Builder.zig+1-1| ... | @@ -9520,7 +9520,7 @@ fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) ty | ... | @@ -9520,7 +9520,7 @@ fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) ty |
| 9520 | backing_writer: BackingWriter, | 9520 | backing_writer: BackingWriter, |
| 9521 | 9521 | ||
| 9522 | pub const Error = BackingWriter.Error || ExtraErrors; | 9522 | pub const Error = BackingWriter.Error || ExtraErrors; |
| 9523 | pub const Writer = std.io.Writer(*const Self, Error, write); | 9523 | pub const Writer = std.io.GenericWriter(*const Self, Error, write); |
| 9524 | 9524 | ||
| 9525 | const Self = @This(); | 9525 | const Self = @This(); |
| 9526 | 9526 |
lib/std/zig/render.zig+1-1| ... | @@ -3245,7 +3245,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type { | ... | @@ -3245,7 +3245,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type { |
| 3245 | return struct { | 3245 | return struct { |
| 3246 | const Self = @This(); | 3246 | const Self = @This(); |
| 3247 | pub const WriteError = UnderlyingWriter.Error; | 3247 | pub const WriteError = UnderlyingWriter.Error; |
| 3248 | pub const Writer = std.io.Writer(*Self, WriteError, write); | 3248 | pub const Writer = std.io.GenericWriter(*Self, WriteError, write); |
| 3249 | 3249 | ||
| 3250 | pub const IndentType = enum { | 3250 | pub const IndentType = enum { |
| 3251 | normal, | 3251 | normal, |
lib/std/zig/string_literal.zig+1-1| ... | @@ -322,7 +322,7 @@ test parseCharLiteral { | ... | @@ -322,7 +322,7 @@ test parseCharLiteral { |
| 322 | ); | 322 | ); |
| 323 | } | 323 | } |
| 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 | /// Asserts `bytes` has '"' at beginning and end. | 326 | /// Asserts `bytes` has '"' at beginning and end. |
| 327 | pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result { | 327 | pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result { |
| 328 | assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"'); | 328 | assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"'); |
lib/std/zip.zig+2-2| ... | @@ -106,7 +106,7 @@ pub const EndRecord = extern struct { | ... | @@ -106,7 +106,7 @@ pub const EndRecord = extern struct { |
| 106 | /// Find and return the end record for the given seekable zip stream. | 106 | /// Find and return the end record for the given seekable zip stream. |
| 107 | /// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and | 107 | /// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and |
| 108 | /// its context must also have a `.reader()` method that returns an instance of | 108 | /// its context must also have a `.reader()` method that returns an instance of |
| 109 | /// `std.io.Reader`. | 109 | /// `std.io.GenericReader`. |
| 110 | pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord { | 110 | pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord { |
| 111 | var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined; | 111 | var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined; |
| 112 | const record_len_max = @min(stream_len, buf.len); | 112 | const record_len_max = @min(stream_len, buf.len); |
| ... | @@ -617,7 +617,7 @@ pub const ExtractOptions = struct { | ... | @@ -617,7 +617,7 @@ pub const ExtractOptions = struct { |
| 617 | /// Extract the zipped files inside `seekable_stream` to the given `dest` directory. | 617 | /// Extract the zipped files inside `seekable_stream` to the given `dest` directory. |
| 618 | /// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and | 618 | /// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and |
| 619 | /// its context must also have a `.reader()` method that returns an instance of | 619 | /// its context must also have a `.reader()` method that returns an instance of |
| 620 | /// `std.io.Reader`. | 620 | /// `std.io.GenericReader`. |
| 621 | pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptions) !void { | 621 | pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptions) !void { |
| 622 | const SeekableStream = @TypeOf(seekable_stream); | 622 | const SeekableStream = @TypeOf(seekable_stream); |
| 623 | var iter = try Iterator(SeekableStream).init(seekable_stream); | 623 | var iter = try Iterator(SeekableStream).init(seekable_stream); |
src/Package/Fetch/git.zig+1-1| ... | @@ -1026,7 +1026,7 @@ pub const Session = struct { | ... | @@ -1026,7 +1026,7 @@ pub const Session = struct { |
| 1026 | ProtocolError, | 1026 | ProtocolError, |
| 1027 | UnexpectedPacket, | 1027 | UnexpectedPacket, |
| 1028 | }; | 1028 | }; |
| 1029 | pub const Reader = std.io.Reader(*FetchStream, ReadError, read); | 1029 | pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read); |
| 1030 | 1030 | ||
| 1031 | const StreamCode = enum(u8) { | 1031 | const StreamCode = enum(u8) { |
| 1032 | pack_data = 1, | 1032 | pack_data = 1, |