migrate some std lib

This commit is contained in:
Andrew Kelley 2025-02-15 16:26:19 -08:00
parent c2fc6b0b6c
commit 5356f3a307
11 changed files with 1108 additions and 1227 deletions

View File

@ -2766,8 +2766,9 @@ fn dumpBadDirnameHelp(
comptime msg: []const u8,
args: anytype,
) anyerror!void {
var w = debug.lockStdErr2();
var buffered_writer = debug.lockStdErr2();
defer debug.unlockStdErr();
const w = &buffered_writer;
const stderr = io.getStdErr();
try w.print(msg, args);
@ -2784,7 +2785,7 @@ fn dumpBadDirnameHelp(
if (asking_step) |as| {
tty_config.setColor(w, .red) catch {};
try stderr.writer().print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
tty_config.setColor(w, .reset) catch {};
as.dump(stderr);
@ -2802,7 +2803,8 @@ pub fn dumpBadGetPathHelp(
src_builder: *Build,
asking_step: ?*Step,
) anyerror!void {
var w = stderr.unbufferedWriter();
var buffered_writer = stderr.unbufferedWriter();
const w = &buffered_writer;
try w.print(
\\getPath() was called on a GeneratedFile that wasn't built yet.
\\ source package path: {s}
@ -2821,7 +2823,7 @@ pub fn dumpBadGetPathHelp(
s.dump(stderr);
if (asking_step) |as| {
tty_config.setColor(w, .red) catch {};
try stderr.writer().print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
tty_config.setColor(w, .reset) catch {};
as.dump(stderr);

View File

@ -1061,14 +1061,17 @@ pub const Manifest = struct {
}
fn addDepFileMaybePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void {
const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
defer self.cache.gpa.free(dep_file_contents);
const gpa = self.cache.gpa;
const dep_file_contents = try dir.readFileAlloc(gpa, dep_file_basename, manifest_file_size_max);
defer gpa.free(dep_file_contents);
var error_buf = std.ArrayList(u8).init(self.cache.gpa);
defer error_buf.deinit();
var error_buf: std.ArrayListUnmanaged(u8) = .empty;
defer error_buf.deinit(gpa);
var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
defer resolve_buf.deinit(gpa);
var it: DepTokenizer = .{ .bytes = dep_file_contents };
while (it.next()) |token| {
switch (token) {
// We don't care about targets, we only want the prereqs
@ -1078,16 +1081,14 @@ pub const Manifest = struct {
_ = try self.addFile(file_path, null);
} else try self.addFilePost(file_path),
.prereq_must_resolve => {
var resolve_buf = std.ArrayList(u8).init(self.cache.gpa);
defer resolve_buf.deinit();
try token.resolve(resolve_buf.writer());
resolve_buf.clearRetainingCapacity();
try token.resolve(gpa, &resolve_buf);
if (self.manifest_file == null) {
_ = try self.addFile(resolve_buf.items, null);
} else try self.addFilePost(resolve_buf.items);
},
else => |err| {
try err.printError(error_buf.writer());
try err.printError(gpa, &error_buf);
log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
return error.InvalidDepFile;
},
@ -1125,13 +1126,13 @@ pub const Manifest = struct {
if (self.manifest_dirty) {
self.manifest_dirty = false;
var contents = std.ArrayList(u8).init(self.cache.gpa);
defer contents.deinit();
const gpa = self.cache.gpa;
var contents: std.ArrayListUnmanaged(u8) = .empty;
defer contents.deinit(gpa);
const writer = contents.writer();
try writer.writeAll(manifest_header ++ "\n");
try contents.appendSlice(gpa, manifest_header ++ "\n");
for (self.files.keys()) |file| {
try writer.print("{d} {d} {d} {x} {d} {s}\n", .{
try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{
file.stat.size,
file.stat.inode,
file.stat.mtime,

View File

@ -7,6 +7,7 @@ state: State = .lhs,
const std = @import("std");
const testing = std.testing;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
pub fn next(self: *Tokenizer) ?Token {
var start = self.index;
@ -362,7 +363,7 @@ pub const Token = union(enum) {
};
/// Resolve escapes in target or prereq. Only valid with .target_must_resolve or .prereq_must_resolve.
pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
pub fn resolve(self: Token, gpa: Allocator, list: *std.ArrayListUnmanaged(u8)) error{OutOfMemory}!void {
switch (self) {
.target_must_resolve => |bytes| {
var state: enum { start, escape, dollar } = .start;
@ -372,27 +373,27 @@ pub const Token = union(enum) {
switch (c) {
'\\' => state = .escape,
'$' => state = .dollar,
else => try writer.writeByte(c),
else => try list.append(gpa, c),
}
},
.escape => {
switch (c) {
' ', '#', '\\' => {},
'$' => {
try writer.writeByte('\\');
try list.append(gpa, '\\');
state = .dollar;
continue;
},
else => try writer.writeByte('\\'),
else => try list.append(gpa, '\\'),
}
try writer.writeByte(c);
try list.append(gpa, c);
state = .start;
},
.dollar => {
try writer.writeByte('$');
try list.append(gpa, '$');
switch (c) {
'$' => {},
else => try writer.writeByte(c),
else => try list.append(gpa, c),
}
state = .start;
},
@ -406,19 +407,19 @@ pub const Token = union(enum) {
.start => {
switch (c) {
'\\' => state = .escape,
else => try writer.writeByte(c),
else => try list.append(gpa, c),
}
},
.escape => {
switch (c) {
' ' => {},
'\\' => {
try writer.writeByte(c);
try list.append(gpa, c);
continue;
},
else => try writer.writeByte('\\'),
else => try list.append(gpa, '\\'),
}
try writer.writeByte(c);
try list.append(gpa, c);
state = .start;
},
}
@ -428,20 +429,20 @@ pub const Token = union(enum) {
}
}
pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
pub fn printError(self: Token, gpa: Allocator, list: *std.ArrayListUnmanaged(u8)) error{OutOfMemory}!void {
switch (self) {
.target, .target_must_resolve, .prereq, .prereq_must_resolve => unreachable, // not an error
.incomplete_quoted_prerequisite,
.incomplete_target,
=> |index_and_bytes| {
try writer.print("{s} '", .{self.errStr()});
try list.print("{s} '", .{self.errStr()});
if (self == .incomplete_target) {
const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
try tmp.resolve(writer);
try tmp.resolve(gpa, list);
} else {
try printCharValues(writer, index_and_bytes.bytes);
try printCharValues(gpa, list, index_and_bytes.bytes);
}
try writer.print("' at position {d}", .{index_and_bytes.index});
try list.print(gpa, "' at position {d}", .{index_and_bytes.index});
},
.invalid_target,
.bad_target_escape,
@ -450,9 +451,9 @@ pub const Token = union(enum) {
.incomplete_escape,
.expected_colon,
=> |index_and_char| {
try writer.writeAll("illegal char ");
try printUnderstandableChar(writer, index_and_char.char);
try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() });
try list.appendSlice("illegal char ");
try printUnderstandableChar(gpa, list, index_and_char.char);
try list.print(gpa, " at position {d}: {s}", .{ index_and_char.index, self.errStr() });
},
}
}
@ -1026,41 +1027,41 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
defer arena_allocator.deinit();
var it: Tokenizer = .{ .bytes = input };
var buffer = std.ArrayList(u8).init(arena);
var resolve_buf = std.ArrayList(u8).init(arena);
var buffer: std.ArrayListUnmanaged(u8) = .empty;
var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
var i: usize = 0;
while (it.next()) |token| {
if (i != 0) try buffer.appendSlice("\n");
if (i != 0) try buffer.appendSlice(arena, "\n");
switch (token) {
.target, .prereq => |bytes| {
try buffer.appendSlice(@tagName(token));
try buffer.appendSlice(" = {");
try buffer.appendSlice(arena, @tagName(token));
try buffer.appendSlice(arena, " = {");
for (bytes) |b| {
try buffer.append(printable_char_tab[b]);
try buffer.append(arena, printable_char_tab[b]);
}
try buffer.appendSlice("}");
try buffer.appendSlice(arena, "}");
},
.target_must_resolve => {
try buffer.appendSlice("target = {");
try token.resolve(resolve_buf.writer());
try buffer.appendSlice(arena, "target = {");
try token.resolve(arena, &resolve_buf);
for (resolve_buf.items) |b| {
try buffer.append(printable_char_tab[b]);
try buffer.append(arena, printable_char_tab[b]);
}
resolve_buf.items.len = 0;
try buffer.appendSlice("}");
try buffer.appendSlice(arena, "}");
},
.prereq_must_resolve => {
try buffer.appendSlice("prereq = {");
try token.resolve(resolve_buf.writer());
try buffer.appendSlice(arena, "prereq = {");
try token.resolve(arena, &resolve_buf);
for (resolve_buf.items) |b| {
try buffer.append(printable_char_tab[b]);
try buffer.append(arena, printable_char_tab[b]);
}
resolve_buf.items.len = 0;
try buffer.appendSlice("}");
try buffer.appendSlice(arena, "}");
},
else => {
try buffer.appendSlice("ERROR: ");
try token.printError(buffer.writer());
try buffer.appendSlice(arena, "ERROR: ");
try token.printError(arena, &buffer);
break;
},
}
@ -1072,121 +1073,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
return;
}
const out = std.io.getStdErr().writer();
try out.writeAll("\n");
try printSection(out, "<<<< input", input);
try printSection(out, "==== expect", expect);
try printSection(out, ">>>> got", buffer.items);
try printRuler(out);
try testing.expect(false);
}
fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
try printLabel(out, label, bytes);
try hexDump(out, bytes);
try printRuler(out);
try out.writeAll(bytes);
try out.writeAll("\n");
}
fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
var buf: [80]u8 = undefined;
const text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len });
try out.writeAll(text);
var i: usize = text.len;
const end = 79;
while (i < end) : (i += 1) {
try out.writeAll(&[_]u8{label[0]});
}
try out.writeAll("\n");
}
fn printRuler(out: anytype) !void {
var i: usize = 0;
const end = 79;
while (i < end) : (i += 1) {
try out.writeAll("-");
}
try out.writeAll("\n");
}
fn hexDump(out: anytype, bytes: []const u8) !void {
const n16 = bytes.len >> 4;
var line: usize = 0;
var offset: usize = 0;
while (line < n16) : (line += 1) {
try hexDump16(out, offset, bytes[offset..][0..16]);
offset += 16;
}
const n = bytes.len & 0x0f;
if (n > 0) {
try printDecValue(out, offset, 8);
try out.writeAll(":");
try out.writeAll(" ");
const end1 = @min(offset + n, offset + 8);
for (bytes[offset..end1]) |b| {
try out.writeAll(" ");
try printHexValue(out, b, 2);
}
const end2 = offset + n;
if (end2 > end1) {
try out.writeAll(" ");
for (bytes[end1..end2]) |b| {
try out.writeAll(" ");
try printHexValue(out, b, 2);
}
}
const short = 16 - n;
var i: usize = 0;
while (i < short) : (i += 1) {
try out.writeAll(" ");
}
if (end2 > end1) {
try out.writeAll(" |");
} else {
try out.writeAll(" |");
}
try printCharValues(out, bytes[offset..end2]);
try out.writeAll("|\n");
offset += n;
}
try printDecValue(out, offset, 8);
try out.writeAll(":");
try out.writeAll("\n");
}
fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
try printDecValue(out, offset, 8);
try out.writeAll(":");
try out.writeAll(" ");
for (bytes[0..8]) |b| {
try out.writeAll(" ");
try printHexValue(out, b, 2);
}
try out.writeAll(" ");
for (bytes[8..16]) |b| {
try out.writeAll(" ");
try printHexValue(out, b, 2);
}
try out.writeAll(" |");
try printCharValues(out, bytes);
try out.writeAll("|\n");
}
fn printDecValue(out: anytype, value: u64, width: u8) !void {
var buffer: [20]u8 = undefined;
const len = std.fmt.formatIntBuf(buffer[0..], value, 10, .lower, .{ .width = width, .fill = '0' });
try out.writeAll(buffer[0..len]);
}
fn printHexValue(out: anytype, value: u64, width: u8) !void {
var buffer: [16]u8 = undefined;
const len = std.fmt.formatIntBuf(buffer[0..], value, 16, .lower, .{ .width = width, .fill = '0' });
try out.writeAll(buffer[0..len]);
try testing.expectEqualStrings(expect, buffer.items);
}
fn printCharValues(out: anytype, bytes: []const u8) !void {

View File

@ -976,37 +976,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
@memcpy(self.items[old_len..][0..items.len], items);
}
pub const WriterContext = struct {
self: *Self,
allocator: Allocator,
};
pub const Writer = if (T != u8)
@compileError("The Writer interface is only defined for ArrayList(u8) " ++
"but the given type is ArrayList(" ++ @typeName(T) ++ ")")
else
std.io.Writer(WriterContext, Allocator.Error, appendWrite);
/// Initializes a Writer which will append to the list.
pub fn writer(self: *Self, gpa: Allocator) Writer {
return .{ .context = .{ .self = self, .allocator = gpa } };
}
/// Same as `append` except it returns the number of bytes written,
/// which is always the same as `m.len`. The purpose of this function
/// existing is to match `std.io.Writer` API.
/// Invalidates element pointers if additional memory is needed.
fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize {
try context.self.appendSlice(context.allocator, m);
return m.len;
}
pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
comptime assert(T == u8);
try self.ensureUnusedCapacity(gpa, fmt.len);
var alw: std.io.ArrayListWriter = undefined;
const bw = alw.fromOwned(gpa, self);
defer self.* = alw.toOwned();
var aw: std.io.AllocatingWriter = undefined;
const bw = aw.fromArrayList(gpa, self);
defer self.* = aw.toArrayList();
bw.print(fmt, args) catch return error.OutOfMemory;
}

View File

@ -301,7 +301,7 @@ pub const AnyWriter = Writer;
pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
pub const BufferedWriter = @import("io/BufferedWriter.zig");
pub const ArrayListWriter = @import("io/ArrayListWriter.zig");
pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader;
pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader;
@ -784,7 +784,7 @@ test {
_ = Writer;
_ = CountingWriter;
_ = FixedBufferStream;
_ = ArrayListWriter;
_ = AllocatingWriter;
_ = @import("io/bit_reader.zig");
_ = @import("io/bit_writer.zig");
_ = @import("io/buffered_atomic_file.zig");

View File

@ -1,73 +1,119 @@
//! The straightforward way to use `std.ArrayList` as the underlying writer
//! when using `std.io.BufferedWriter` is to populate the `std.io.Writer`
//! interface and then use an empty buffer. However, this means that every use
//! of `std.io.BufferedWriter` will go through the vtable, including for
//! TODO rename to AllocatingWriter.
//! While it is possible to use `std.ArrayList` as the underlying writer when
//! using `std.io.BufferedWriter` by populating the `std.io.Writer` interface
//! and then using an empty buffer, it means that every use of
//! `std.io.BufferedWriter` will go through the vtable, including for
//! functions such as `writeByte`. This API instead maintains
//! `std.io.BufferedWriter` state such that it writes to the unused capacity of
//! the array list, filling it up completely before making a call through the
//! an array list, filling it up completely before making a call through the
//! vtable, causing a resize. Consequently, the same, optimized, non-generic
//! machine code that uses `std.io.BufferedReader`, such as formatted printing,
//! is also used when the underlying writer is backed by `std.ArrayList`.
//! takes the hot paths when using this API.
const std = @import("../std.zig");
const ArrayListWriter = @This();
const AllocatingWriter = @This();
const assert = std.debug.assert;
items: []u8,
/// This is missing the data stored in `buffered_writer`. See `getWritten` for
/// returning a slice that includes both.
written: []u8,
allocator: std.mem.Allocator,
buffered_writer: std.io.BufferedWriter,
/// Replaces `array_list` with empty, taking ownership of the memory.
pub fn fromOwned(
alw: *ArrayListWriter,
allocator: std.mem.Allocator,
array_list: *std.ArrayListUnmanaged(u8),
) *std.io.BufferedWriter {
alw.* = .{
.allocated_slice = array_list.items,
const vtable: std.io.Writer.VTable = .{
.writev = writev,
.writeFile = writeFile,
};
/// Sets the `AllocatingWriter` to an empty state.
pub fn init(aw: *AllocatingWriter, allocator: std.mem.Allocator) *std.io.BufferedWriter {
aw.* = .{
.written = &.{},
.allocator = allocator,
.buffered_writer = .{
.unbuffered_writer = .{
.context = alw,
.vtable = &.{
.writev = writev,
.writeFile = writeFile,
},
.context = aw,
.vtable = &vtable,
},
.buffer = &.{},
},
};
return &aw.buffered_writer;
}
/// Replaces `array_list` with empty, taking ownership of the memory.
pub fn fromArrayList(
aw: *AllocatingWriter,
allocator: std.mem.Allocator,
array_list: *std.ArrayListUnmanaged(u8),
) *std.io.BufferedWriter {
aw.* = .{
.written = array_list.items,
.allocator = allocator,
.buffered_writer = .{
.unbuffered_writer = .{
.context = aw,
.vtable = &vtable,
},
.buffer = array_list.unusedCapacitySlice(),
},
};
array_list.* = .empty;
return &alw.buffered_writer;
return &aw.buffered_writer;
}
/// Returns the memory back that was borrowed with `fromOwned`.
pub fn toOwned(alw: *ArrayListWriter) std.ArrayListUnmanaged(u8) {
const end = alw.buffered_writer.end;
/// Returns an array list that takes ownership of the allocated memory.
/// Resets the `AllocatingWriter` to an empty state.
pub fn toArrayList(aw: *AllocatingWriter) std.ArrayListUnmanaged(u8) {
const bw = &aw.buffered_writer;
const written = aw.written;
const result: std.ArrayListUnmanaged(u8) = .{
.items = alw.items.ptr[0 .. alw.items.len + end],
.capacity = alw.buffered_writer.buffer.len - end,
.items = written.ptr[0 .. written.len + bw.end],
.capacity = written.len + bw.buffer.len,
};
alw.* = undefined;
aw.written = &.{};
bw.buffer = &.{};
bw.end = 0;
return result;
}
fn setArrayList(aw: *AllocatingWriter, list: std.ArrayListUnmanaged(u8)) void {
aw.written = list.items;
aw.buffered_writer.buffer = list.unusedCapacitySlice();
}
pub fn getWritten(aw: *AllocatingWriter) []u8 {
const bw = &aw.buffered_writer;
const end = aw.buffered_writer.end;
const result = aw.written.ptr[0 .. aw.written.len + end];
bw.buffer = bw.buffer[end..];
bw.end = 0;
return result;
}
pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
const bw = &aw.buffered_writer;
bw.buffer = aw.written.ptr[0 .. aw.written.len + bw.buffer.len];
bw.end = 0;
aw.written.len = 0;
}
fn writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
const alw: *ArrayListWriter = @alignCast(@ptrCast(context));
const start_len = alw.items.len;
const bw = &alw.buffered_writer;
assert(data[0].ptr == alw.items.ptr + start_len);
const bw_end = data[0].len;
const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
const start_len = aw.written.len;
const bw = &aw.buffered_writer;
assert(data[0].ptr == aw.written.ptr + start_len);
var list: std.ArrayListUnmanaged(u8) = .{
.items = alw.items.ptr[0 .. start_len + bw_end],
.capacity = bw.buffer.len - bw_end,
.items = aw.written.ptr[0 .. start_len + data[0].len],
.capacity = start_len + bw.buffer.len,
};
defer setArrayList(aw, list);
const rest = data[1..];
var new_capacity: usize = list.capacity;
for (rest) |bytes| new_capacity += bytes.len;
try list.ensureTotalCapacity(alw.allocator, new_capacity + 1);
try list.ensureTotalCapacity(aw.allocator, new_capacity + 1);
for (rest) |bytes| list.appendSliceAssumeCapacity(bytes);
alw.items = list.items;
aw.written = list.items;
bw.buffer = list.unusedCapacitySlice();
return list.items.len - start_len;
}
@ -80,16 +126,16 @@ fn writeFile(
headers_and_trailers_full: []const []const u8,
headers_len_full: usize,
) anyerror!usize {
const alw: *ArrayListWriter = @alignCast(@ptrCast(context));
const list = alw.array_list;
const bw = &alw.buffered_writer;
const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
const gpa = aw.allocator;
var list = aw.toArrayList();
defer setArrayList(aw, list);
const start_len = list.items.len;
const headers_and_trailers, const headers_len = if (headers_len_full >= 1) b: {
assert(headers_and_trailers_full[0].ptr == list.items.ptr + start_len);
list.items.len += headers_and_trailers_full[0].len;
break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
} else .{ headers_and_trailers_full, headers_len_full };
const gpa = alw.allocator;
const trailers = headers_and_trailers[headers_len..];
if (len == .entire_file) {
var new_capacity: usize = list.capacity + std.atomic.cache_line;
@ -103,11 +149,9 @@ fn writeFile(
for (trailers) |bytes| new_capacity += bytes.len;
try list.ensureTotalCapacity(gpa, new_capacity);
for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
bw.buffer = list.unusedCapacitySlice();
return list.items.len - start_len;
}
list.items.len += n;
bw.buffer = list.unusedCapacitySlice();
return list.items.len - start_len;
}
var new_capacity: usize = list.capacity + len.int();
@ -118,10 +162,8 @@ fn writeFile(
const n = try file.pread(dest, offset);
list.items.len += n;
if (n < dest.len) {
bw.buffer = list.unusedCapacitySlice();
return list.items.len - start_len;
}
for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
bw.buffer = list.unusedCapacitySlice();
return list.items.len - start_len;
}

View File

@ -19,6 +19,9 @@ end: usize = 0,
/// vectors through the underlying write calls as possible.
pub const max_buffers_len = 8;
/// Although `BufferedWriter` can easily satisfy the `Writer` interface, it's
/// generally more practical to pass a `BufferedWriter` instance itself around,
/// since it will result in fewer calls across vtable boundaries.
pub fn writer(bw: *BufferedWriter) Writer {
return .{
.context = bw,
@ -212,6 +215,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
const new_end = end + n;
if (new_end <= buffer.len) {
@branchHint(.likely);
@memset(buffer[end..][0..n], byte);
bw.end = new_end;
return n;
@ -226,6 +230,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
bw.end = remainder.len;
return 0;
}
assert(bw.buffer.ptr == buffer.ptr); // TODO this is not a valid assertion
@memset(buffer[0..n], byte);
bw.end = n;
return n;

View File

@ -71,12 +71,7 @@ pub const Config = union(enum) {
reset_attributes: u16,
};
pub fn setColor(
conf: Config,
writer: anytype,
color: Color,
) (@typeInfo(@TypeOf(writer.writeAll(""))).error_union.error_set ||
windows.SetConsoleTextAttributeError)!void {
pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) anyerror!void {
nosuspend switch (conf) {
.no_color => return,
.escape_codes => {
@ -101,7 +96,7 @@ pub const Config = union(enum) {
.dim => "\x1b[2m",
.reset => "\x1b[0m",
};
try writer.writeAll(color_string);
try bw.writeAll(color_string);
},
.windows_api => |ctx| if (native_os == .windows) {
const attributes = switch (color) {

View File

@ -1004,7 +1004,8 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
fn writeIntFd(fd: i32, value: ErrInt) !void {
const file: File = .{ .handle = fd };
file.writer().writeInt(u64, @intCast(value), .little) catch return error.SystemResources;
var bw = file.unbufferedWriter();
bw.writeInt(u64, @intCast(value), .little) catch return error.SystemResources;
}
fn readIntFd(fd: i32) !ErrInt {

View File

@ -475,37 +475,37 @@ pub fn stringEscape(
bytes: []const u8,
comptime f: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
bw: *std.io.BufferedWriter,
) !void {
_ = options;
for (bytes) |byte| switch (byte) {
'\n' => try writer.writeAll("\\n"),
'\r' => try writer.writeAll("\\r"),
'\t' => try writer.writeAll("\\t"),
'\\' => try writer.writeAll("\\\\"),
'\n' => try bw.writeAll("\\n"),
'\r' => try bw.writeAll("\\r"),
'\t' => try bw.writeAll("\\t"),
'\\' => try bw.writeAll("\\\\"),
'"' => {
if (f.len == 1 and f[0] == '\'') {
try writer.writeByte('"');
try bw.writeByte('"');
} else if (f.len == 0) {
try writer.writeAll("\\\"");
try bw.writeAll("\\\"");
} else {
@compileError("expected {} or {'}, found {" ++ f ++ "}");
}
},
'\'' => {
if (f.len == 1 and f[0] == '\'') {
try writer.writeAll("\\'");
try bw.writeAll("\\'");
} else if (f.len == 0) {
try writer.writeByte('\'');
try bw.writeByte('\'');
} else {
@compileError("expected {} or {'}, found {" ++ f ++ "}");
}
},
' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte),
// Use hex escapes for rest any unprintable characters.
else => {
try writer.writeAll("\\x");
try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer);
try bw.writeAll("\\x");
try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' });
},
};
}

File diff suppressed because it is too large Load Diff