std.fmt: breaking API changes

added adapter to AnyWriter and GenericWriter to help bridge the gap
between old and new API

make std.testing.expectFmt work at compile-time

std.fmt no longer has a dependency on std.unicode. Formatted printing
was never properly unicode-aware. Now it no longer pretends to be.

Breakage/deprecations:
* std.fs.File.reader -> std.fs.File.deprecatedReader
* std.fs.File.writer -> std.fs.File.deprecatedWriter
* std.io.GenericReader -> std.io.Reader
* std.io.GenericWriter -> std.io.Writer
* std.io.AnyReader -> std.io.Reader
* std.io.AnyWriter -> std.io.Writer
* std.fmt.format -> std.fmt.deprecatedFormat
* std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape
* std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape
* std.fmt.fmtSliceHexLower -> {x}
* std.fmt.fmtSliceHexUpper -> {X}
* std.fmt.fmtIntSizeDec -> {B}
* std.fmt.fmtIntSizeBin -> {Bi}
* std.fmt.fmtDuration -> {D}
* std.fmt.fmtDurationSigned -> {D}
* {} -> {f} when there is a format method
* format method signature
  - anytype -> *std.io.Writer
  - inferred error set -> error{WriteFailed}
  - options -> (deleted)
* std.fmt.Formatted
  - now takes context type explicitly
  - no fmt string
This commit is contained in:
Andrew Kelley 2025-06-27 20:05:22 -07:00
parent 0b3f0124dc
commit 0e37ff0d59
161 changed files with 4385 additions and 5847 deletions

View File

@ -436,7 +436,6 @@ set(ZIG_STAGE2_SOURCES
lib/std/elf.zig lib/std/elf.zig
lib/std/fifo.zig lib/std/fifo.zig
lib/std/fmt.zig lib/std/fmt.zig
lib/std/fmt/format_float.zig
lib/std/fmt/parse_float.zig lib/std/fmt/parse_float.zig
lib/std/fs.zig lib/std/fs.zig
lib/std/fs/AtomicFile.zig lib/std/fs/AtomicFile.zig

View File

@ -279,7 +279,7 @@ pub fn build(b: *std.Build) !void {
const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor); const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
if (zig_version.order(ancestor_ver) != .gt) { if (zig_version.order(ancestor_ver) != .gt) {
std.debug.print("Zig version '{}' must be greater than tagged ancestor '{}'\n", .{ zig_version, ancestor_ver }); std.debug.print("Zig version '{f}' must be greater than tagged ancestor '{f}'\n", .{ zig_version, ancestor_ver });
std.process.exit(1); std.process.exit(1);
} }
@ -1449,7 +1449,7 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
} }
var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| { var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
std.debug.panic("unable to open '{}doc/langref' directory: {s}", .{ std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
b.build_root, @errorName(err), b.build_root, @errorName(err),
}); });
}; };
@ -1470,7 +1470,7 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
// in a temporary directory // in a temporary directory
"--cache-root", b.cache_root.path orelse ".", "--cache-root", b.cache_root.path orelse ".",
}); });
cmd.addArgs(&.{ "--zig-lib-dir", b.fmt("{}", .{b.graph.zig_lib_directory}) }); cmd.addArgs(&.{ "--zig-lib-dir", b.fmt("{f}", .{b.graph.zig_lib_directory}) });
cmd.addArgs(&.{"-i"}); cmd.addArgs(&.{"-i"});
cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name}))); cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));

View File

@ -1432,7 +1432,7 @@ fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u
defer buf.deinit(); defer buf.deinit();
const max = limit orelse std.math.maxInt(u32); const max = limit orelse std.math.maxInt(u32);
file.reader().readAllArrayList(&buf, max) catch |e| switch (e) { file.deprecatedReader().readAllArrayList(&buf, max) catch |e| switch (e) {
error.StreamTooLong => if (limit == null) return e, error.StreamTooLong => if (limit == null) return e,
else => return e, else => return e,
}; };

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const mem = std.mem; const mem = std.mem;
const Source = @import("Source.zig"); const Source = @import("Source.zig");
@ -443,18 +444,13 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
printRt(m, prop.msg, .{"{s}"}, .{&str}); printRt(m, prop.msg, .{"{s}"}, .{&str});
} else { } else {
var buf: [3]u8 = undefined; var buf: [3]u8 = undefined;
const str = std.fmt.bufPrint(&buf, "x{x}", .{std.fmt.fmtSliceHexLower(&.{msg.extra.invalid_escape.char})}) catch unreachable; const str = std.fmt.bufPrint(&buf, "x{x}", .{&.{msg.extra.invalid_escape.char}}) catch unreachable;
printRt(m, prop.msg, .{"{s}"}, .{str}); printRt(m, prop.msg, .{"{s}"}, .{str});
} }
}, },
.normalized => { .normalized => {
const f = struct { const f = struct {
pub fn f( pub fn f(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
bytes: []const u8,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
var it: std.unicode.Utf8Iterator = .{ var it: std.unicode.Utf8Iterator = .{
.bytes = bytes, .bytes = bytes,
.i = 0, .i = 0,
@ -464,22 +460,16 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
try writer.writeByte(@intCast(codepoint)); try writer.writeByte(@intCast(codepoint));
} else if (codepoint < 0xFFFF) { } else if (codepoint < 0xFFFF) {
try writer.writeAll("\\u"); try writer.writeAll("\\u");
try std.fmt.formatInt(codepoint, 16, .upper, .{ try writer.printIntOptions(codepoint, 16, .upper, .{ .fill = '0', .width = 4 });
.fill = '0',
.width = 4,
}, writer);
} else { } else {
try writer.writeAll("\\U"); try writer.writeAll("\\U");
try std.fmt.formatInt(codepoint, 16, .upper, .{ try writer.printIntOptions(codepoint, 16, .upper, .{ .fill = '0', .width = 8 });
.fill = '0',
.width = 8,
}, writer);
} }
} }
} }
}.f; }.f;
printRt(m, prop.msg, .{"{s}"}, .{ printRt(m, prop.msg, .{"{f}"}, .{
std.fmt.Formatter(f){ .data = msg.extra.normalized }, std.fmt.Formatter([]const u8, f){ .data = msg.extra.normalized },
}); });
}, },
.none, .offset => m.write(prop.msg), .none, .offset => m.write(prop.msg),
@ -541,7 +531,7 @@ const MsgWriter = struct {
fn init(config: std.io.tty.Config) MsgWriter { fn init(config: std.io.tty.Config) MsgWriter {
std.debug.lockStdErr(); std.debug.lockStdErr();
return .{ return .{
.w = std.io.bufferedWriter(std.fs.File.stderr().writer()), .w = std.io.bufferedWriter(std.fs.File.stderr().deprecatedWriter()),
.config = config, .config = config,
}; };
} }

View File

@ -591,7 +591,7 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8, comptime fast_
var macro_buf = std.ArrayList(u8).init(d.comp.gpa); var macro_buf = std.ArrayList(u8).init(d.comp.gpa);
defer macro_buf.deinit(); defer macro_buf.deinit();
const std_out = std.fs.File.stdout().writer(); const std_out = std.fs.File.stdout().deprecatedWriter();
if (try parseArgs(d, std_out, macro_buf.writer(), args)) return; if (try parseArgs(d, std_out, macro_buf.writer(), args)) return;
const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile); const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile);
@ -689,7 +689,7 @@ fn processSource(
std.fs.File.stdout(); std.fs.File.stdout();
defer if (d.output_name != null) file.close(); defer if (d.output_name != null) file.close();
var buf_w = std.io.bufferedWriter(file.writer()); var buf_w = std.io.bufferedWriter(file.deprecatedWriter());
pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er| pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er|
return d.fatal("unable to write result: {s}", .{errorDescription(er)}); return d.fatal("unable to write result: {s}", .{errorDescription(er)});
@ -705,7 +705,7 @@ fn processSource(
if (d.verbose_ast) { if (d.verbose_ast) {
const stdout = std.fs.File.stdout(); const stdout = std.fs.File.stdout();
var buf_writer = std.io.bufferedWriter(stdout.writer()); var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter());
tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {}; tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {};
buf_writer.flush() catch {}; buf_writer.flush() catch {};
} }
@ -735,7 +735,7 @@ fn processSource(
if (d.verbose_ir) { if (d.verbose_ir) {
const stdout = std.fs.File.stdout(); const stdout = std.fs.File.stdout();
var buf_writer = std.io.bufferedWriter(stdout.writer()); var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter());
ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {}; ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {};
buf_writer.flush() catch {}; buf_writer.flush() catch {};
} }
@ -806,10 +806,10 @@ fn processSource(
} }
fn dumpLinkerArgs(items: []const []const u8) !void { fn dumpLinkerArgs(items: []const []const u8) !void {
const stdout = std.fs.File.stdout().writer(); const stdout = std.fs.File.stdout().deprecatedWriter();
for (items, 0..) |item, i| { for (items, 0..) |item, i| {
if (i > 0) try stdout.writeByte(' '); if (i > 0) try stdout.writeByte(' ');
try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)}); try stdout.print("\"{f}\"", .{std.zig.fmtString(item)});
} }
try stdout.writeByte('\n'); try stdout.writeByte('\n');
} }

View File

@ -500,8 +500,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_
const w = p.strings.writer(); const w = p.strings.writer();
const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes; const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes;
try w.print("call to '{s}' declared with attribute error: {}", .{ try w.print("call to '{s}' declared with attribute error: {f}", .{
p.tokSlice(@"error".__name_tok), std.zig.fmtEscapes(msg_str), p.tokSlice(@"error".__name_tok), std.zig.fmtString(msg_str),
}); });
const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
try p.errStr(.error_attribute, usage_tok, str); try p.errStr(.error_attribute, usage_tok, str);
@ -512,8 +512,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_
const w = p.strings.writer(); const w = p.strings.writer();
const msg_str = p.comp.interner.get(warning.msg.ref()).bytes; const msg_str = p.comp.interner.get(warning.msg.ref()).bytes;
try w.print("call to '{s}' declared with attribute warning: {}", .{ try w.print("call to '{s}' declared with attribute warning: {f}", .{
p.tokSlice(warning.__name_tok), std.zig.fmtEscapes(msg_str), p.tokSlice(warning.__name_tok), std.zig.fmtString(msg_str),
}); });
const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
try p.errStr(.warning_attribute, usage_tok, str); try p.errStr(.warning_attribute, usage_tok, str);
@ -542,7 +542,7 @@ fn errDeprecated(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, msg: ?Valu
try w.writeAll(reason); try w.writeAll(reason);
if (msg) |m| { if (msg) |m| {
const str = p.comp.interner.get(m.ref()).bytes; const str = p.comp.interner.get(m.ref()).bytes;
try w.print(": {}", .{std.zig.fmtEscapes(str)}); try w.print(": {f}", .{std.zig.fmtString(str)});
} }
const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
return p.errStr(tag, tok_i, str); return p.errStr(tag, tok_i, str);

View File

@ -811,7 +811,7 @@ fn verboseLog(pp: *Preprocessor, raw: RawToken, comptime fmt: []const u8, args:
const source = pp.comp.getSource(raw.source); const source = pp.comp.getSource(raw.source);
const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start }); const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start });
const stderr = std.fs.File.stderr().writer(); const stderr = std.fs.File.stderr().deprecatedWriter();
var buf_writer = std.io.bufferedWriter(stderr); var buf_writer = std.io.bufferedWriter(stderr);
const writer = buf_writer.writer(); const writer = buf_writer.writer();
defer buf_writer.flush() catch {}; defer buf_writer.flush() catch {};
@ -3262,7 +3262,8 @@ fn printLinemarker(
// containing the same bytes as the input regardless of encoding. // containing the same bytes as the input regardless of encoding.
else => { else => {
try w.writeAll("\\x"); try w.writeAll("\\x");
try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, w); // TODO try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' });
try w.print("{x:0>2}", .{byte});
}, },
}; };
try w.writeByte('"'); try w.writeByte('"');

View File

@ -982,7 +982,7 @@ pub fn printString(bytes: []const u8, ty: Type, comp: *const Compilation, w: any
const without_null = bytes[0 .. bytes.len - @intFromEnum(size)]; const without_null = bytes[0 .. bytes.len - @intFromEnum(size)];
try w.writeByte('"'); try w.writeByte('"');
switch (size) { switch (size) {
.@"1" => try w.print("{}", .{std.zig.fmtEscapes(without_null)}), .@"1" => try w.print("{f}", .{std.zig.fmtString(without_null)}),
.@"2" => { .@"2" => {
var items: [2]u16 = undefined; var items: [2]u16 = undefined;
var i: usize = 0; var i: usize = 0;

View File

@ -171,7 +171,7 @@ pub fn addRelocation(elf: *Elf, name: []const u8, section_kind: Object.Section,
/// strtab /// strtab
/// section headers /// section headers
pub fn finish(elf: *Elf, file: std.fs.File) !void { pub fn finish(elf: *Elf, file: std.fs.File) !void {
var buf_writer = std.io.bufferedWriter(file.writer()); var buf_writer = std.io.bufferedWriter(file.deprecatedWriter());
const w = buf_writer.writer(); const w = buf_writer.writer();
var num_sections: std.elf.Elf64_Half = additional_sections; var num_sections: std.elf.Elf64_Half = additional_sections;

View File

@ -849,7 +849,7 @@ const Context = struct {
fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
if (std.zig.primitives.isPrimitive(bytes)) if (std.zig.primitives.isPrimitive(bytes))
return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes}); return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
return c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(bytes)}); return c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(bytes, .{ .allow_primitive = true })});
} }
fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange { fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
@ -1201,7 +1201,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
const compile_error_tok = try c.addToken(.builtin, "@compileError"); const compile_error_tok = try c.addToken(.builtin, "@compileError");
_ = try c.addToken(.l_paren, "("); _ = try c.addToken(.l_paren, "(");
const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(payload.mangled)}); const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
const err_msg = try c.addNode(.{ const err_msg = try c.addNode(.{
.tag = .string_literal, .tag = .string_literal,
.main_token = err_msg_tok, .main_token = err_msg_tok,
@ -2116,7 +2116,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
defer c.gpa.free(members); defer c.gpa.free(members);
for (payload.fields, 0..) |field, i| { for (payload.fields, 0..) |field, i| {
const name_tok = try c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(field.name)}); const name_tok = try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true })});
_ = try c.addToken(.colon, ":"); _ = try c.addToken(.colon, ":");
const type_expr = try renderNode(c, field.type); const type_expr = try renderNode(c, field.type);
@ -2205,7 +2205,7 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI
.main_token = try c.addToken(.period, "."), .main_token = try c.addToken(.period, "."),
.data = .{ .node_and_token = .{ .data = .{ .node_and_token = .{
lhs, lhs,
try c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(field_name)}), try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true })}),
} }, } },
}); });
} }
@ -2681,7 +2681,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
_ = try c.addToken(.l_paren, "("); _ = try c.addToken(.l_paren, "(");
const res = try c.addNode(.{ const res = try c.addNode(.{
.tag = .string_literal, .tag = .string_literal,
.main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}), .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
.data = undefined, .data = undefined,
}); });
_ = try c.addToken(.r_paren, ")"); _ = try c.addToken(.r_paren, ")");
@ -2765,7 +2765,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
_ = try c.addToken(.l_paren, "("); _ = try c.addToken(.l_paren, "(");
const res = try c.addNode(.{ const res = try c.addNode(.{
.tag = .string_literal, .tag = .string_literal,
.main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}), .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
.data = undefined, .data = undefined,
}); });
_ = try c.addToken(.r_paren, ")"); _ = try c.addToken(.r_paren, ")");

View File

@ -365,7 +365,7 @@ pub fn main() !void {
.data = buffer.items, .data = buffer.items,
.flags = .{ .exclusive = true }, .flags = .{ .exclusive = true },
}) catch |err| { }) catch |err| {
fatal("unable to write configuration results to '{}{s}': {s}", .{ fatal("unable to write configuration results to '{f}{s}': {s}", .{
local_cache_directory, tmp_sub_path, @errorName(err), local_cache_directory, tmp_sub_path, @errorName(err),
}); });
}; };
@ -378,7 +378,7 @@ pub fn main() !void {
validateSystemLibraryOptions(builder); validateSystemLibraryOptions(builder);
const stdout_writer = std.fs.File.stdout().writer(); const stdout_writer = std.fs.File.stdout().deprecatedWriter();
if (help_menu) if (help_menu)
return usage(builder, stdout_writer); return usage(builder, stdout_writer);
@ -704,14 +704,14 @@ fn runStepNames(
ttyconf.setColor(stderr, .cyan) catch {}; ttyconf.setColor(stderr, .cyan) catch {};
stderr.writeAll("Build Summary:") catch {}; stderr.writeAll("Build Summary:") catch {};
ttyconf.setColor(stderr, .reset) catch {}; ttyconf.setColor(stderr, .reset) catch {};
stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; stderr.deprecatedWriter().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; if (skipped_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{skipped_count}) catch {};
if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; if (failure_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{failure_count}) catch {};
if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; if (test_count > 0) stderr.deprecatedWriter().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; if (test_skip_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{test_skip_count}) catch {};
if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; if (test_fail_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{test_fail_count}) catch {};
if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; if (test_leak_count > 0) stderr.deprecatedWriter().print("; {d} leaked", .{test_leak_count}) catch {};
stderr.writeAll("\n") catch {}; stderr.writeAll("\n") catch {};
@ -820,10 +820,10 @@ fn printStepStatus(
try stderr.writeAll(" cached"); try stderr.writeAll(" cached");
} else if (s.test_results.test_count > 0) { } else if (s.test_results.test_count > 0) {
const pass_count = s.test_results.passCount(); const pass_count = s.test_results.passCount();
try stderr.writer().print(" {d} passed", .{pass_count}); try stderr.deprecatedWriter().print(" {d} passed", .{pass_count});
if (s.test_results.skip_count > 0) { if (s.test_results.skip_count > 0) {
try ttyconf.setColor(stderr, .yellow); try ttyconf.setColor(stderr, .yellow);
try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); try stderr.deprecatedWriter().print(" {d} skipped", .{s.test_results.skip_count});
} }
} else { } else {
try stderr.writeAll(" success"); try stderr.writeAll(" success");
@ -832,15 +832,15 @@ fn printStepStatus(
if (s.result_duration_ns) |ns| { if (s.result_duration_ns) |ns| {
try ttyconf.setColor(stderr, .dim); try ttyconf.setColor(stderr, .dim);
if (ns >= std.time.ns_per_min) { if (ns >= std.time.ns_per_min) {
try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); try stderr.deprecatedWriter().print(" {d}m", .{ns / std.time.ns_per_min});
} else if (ns >= std.time.ns_per_s) { } else if (ns >= std.time.ns_per_s) {
try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); try stderr.deprecatedWriter().print(" {d}s", .{ns / std.time.ns_per_s});
} else if (ns >= std.time.ns_per_ms) { } else if (ns >= std.time.ns_per_ms) {
try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); try stderr.deprecatedWriter().print(" {d}ms", .{ns / std.time.ns_per_ms});
} else if (ns >= std.time.ns_per_us) { } else if (ns >= std.time.ns_per_us) {
try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); try stderr.deprecatedWriter().print(" {d}us", .{ns / std.time.ns_per_us});
} else { } else {
try stderr.writer().print(" {d}ns", .{ns}); try stderr.deprecatedWriter().print(" {d}ns", .{ns});
} }
try ttyconf.setColor(stderr, .reset); try ttyconf.setColor(stderr, .reset);
} }
@ -848,13 +848,13 @@ fn printStepStatus(
const rss = s.result_peak_rss; const rss = s.result_peak_rss;
try ttyconf.setColor(stderr, .dim); try ttyconf.setColor(stderr, .dim);
if (rss >= 1000_000_000) { if (rss >= 1000_000_000) {
try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); try stderr.deprecatedWriter().print(" MaxRSS:{d}G", .{rss / 1000_000_000});
} else if (rss >= 1000_000) { } else if (rss >= 1000_000) {
try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); try stderr.deprecatedWriter().print(" MaxRSS:{d}M", .{rss / 1000_000});
} else if (rss >= 1000) { } else if (rss >= 1000) {
try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); try stderr.deprecatedWriter().print(" MaxRSS:{d}K", .{rss / 1000});
} else { } else {
try stderr.writer().print(" MaxRSS:{d}B", .{rss}); try stderr.deprecatedWriter().print(" MaxRSS:{d}B", .{rss});
} }
try ttyconf.setColor(stderr, .reset); try ttyconf.setColor(stderr, .reset);
} }
@ -866,7 +866,7 @@ fn printStepStatus(
if (skip == .skipped_oom) { if (skip == .skipped_oom) {
try stderr.writeAll(" (not enough memory)"); try stderr.writeAll(" (not enough memory)");
try ttyconf.setColor(stderr, .dim); try ttyconf.setColor(stderr, .dim);
try stderr.writer().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); try stderr.deprecatedWriter().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss });
try ttyconf.setColor(stderr, .yellow); try ttyconf.setColor(stderr, .yellow);
} }
try stderr.writeAll("\n"); try stderr.writeAll("\n");
@ -883,18 +883,18 @@ fn printStepFailure(
) !void { ) !void {
if (s.result_error_bundle.errorMessageCount() > 0) { if (s.result_error_bundle.errorMessageCount() > 0) {
try ttyconf.setColor(stderr, .red); try ttyconf.setColor(stderr, .red);
try stderr.writer().print(" {d} errors\n", .{ try stderr.deprecatedWriter().print(" {d} errors\n", .{
s.result_error_bundle.errorMessageCount(), s.result_error_bundle.errorMessageCount(),
}); });
try ttyconf.setColor(stderr, .reset); try ttyconf.setColor(stderr, .reset);
} else if (!s.test_results.isSuccess()) { } else if (!s.test_results.isSuccess()) {
try stderr.writer().print(" {d}/{d} passed", .{ try stderr.deprecatedWriter().print(" {d}/{d} passed", .{
s.test_results.passCount(), s.test_results.test_count, s.test_results.passCount(), s.test_results.test_count,
}); });
if (s.test_results.fail_count > 0) { if (s.test_results.fail_count > 0) {
try stderr.writeAll(", "); try stderr.writeAll(", ");
try ttyconf.setColor(stderr, .red); try ttyconf.setColor(stderr, .red);
try stderr.writer().print("{d} failed", .{ try stderr.deprecatedWriter().print("{d} failed", .{
s.test_results.fail_count, s.test_results.fail_count,
}); });
try ttyconf.setColor(stderr, .reset); try ttyconf.setColor(stderr, .reset);
@ -902,7 +902,7 @@ fn printStepFailure(
if (s.test_results.skip_count > 0) { if (s.test_results.skip_count > 0) {
try stderr.writeAll(", "); try stderr.writeAll(", ");
try ttyconf.setColor(stderr, .yellow); try ttyconf.setColor(stderr, .yellow);
try stderr.writer().print("{d} skipped", .{ try stderr.deprecatedWriter().print("{d} skipped", .{
s.test_results.skip_count, s.test_results.skip_count,
}); });
try ttyconf.setColor(stderr, .reset); try ttyconf.setColor(stderr, .reset);
@ -910,7 +910,7 @@ fn printStepFailure(
if (s.test_results.leak_count > 0) { if (s.test_results.leak_count > 0) {
try stderr.writeAll(", "); try stderr.writeAll(", ");
try ttyconf.setColor(stderr, .red); try ttyconf.setColor(stderr, .red);
try stderr.writer().print("{d} leaked", .{ try stderr.deprecatedWriter().print("{d} leaked", .{
s.test_results.leak_count, s.test_results.leak_count,
}); });
try ttyconf.setColor(stderr, .reset); try ttyconf.setColor(stderr, .reset);
@ -992,7 +992,7 @@ fn printTreeStep(
if (s.dependencies.items.len == 0) { if (s.dependencies.items.len == 0) {
try stderr.writeAll(" (reused)\n"); try stderr.writeAll(" (reused)\n");
} else { } else {
try stderr.writer().print(" (+{d} more reused dependencies)\n", .{ try stderr.deprecatedWriter().print(" (+{d} more reused dependencies)\n", .{
s.dependencies.items.len, s.dependencies.items.len,
}); });
} }
@ -1209,7 +1209,7 @@ pub fn printErrorMessages(
var indent: usize = 0; var indent: usize = 0;
while (step_stack.pop()) |s| : (indent += 1) { while (step_stack.pop()) |s| : (indent += 1) {
if (indent > 0) { if (indent > 0) {
try stderr.writer().writeByteNTimes(' ', (indent - 1) * 3); try stderr.deprecatedWriter().writeByteNTimes(' ', (indent - 1) * 3);
try printChildNodePrefix(stderr, ttyconf); try printChildNodePrefix(stderr, ttyconf);
} }
@ -1231,7 +1231,7 @@ pub fn printErrorMessages(
} }
if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) { if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) {
try failing_step.result_error_bundle.renderToWriter(options, stderr.writer()); try failing_step.result_error_bundle.renderToWriter(options, stderr.deprecatedWriter());
} }
for (failing_step.result_error_msgs.items) |msg| { for (failing_step.result_error_msgs.items) |msg| {

View File

@ -40,7 +40,7 @@ pub fn main() !void {
const arg = args[i]; const arg = args[i];
if (mem.startsWith(u8, arg, "-")) { if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
const stdout = std.fs.File.stdout().writer(); const stdout = std.fs.File.stdout().deprecatedWriter();
try stdout.writeAll(usage_libc); try stdout.writeAll(usage_libc);
return std.process.cleanExit(); return std.process.cleanExit();
} else if (mem.eql(u8, arg, "-target")) { } else if (mem.eql(u8, arg, "-target")) {
@ -97,7 +97,7 @@ pub fn main() !void {
fatal("no include dirs detected for target {s}", .{zig_target}); fatal("no include dirs detected for target {s}", .{zig_target});
} }
var bw = std.io.bufferedWriter(std.fs.File.stdout().writer()); var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
var writer = bw.writer(); var writer = bw.writer();
for (libc_dirs.libc_include_dir_list) |include_dir| { for (libc_dirs.libc_include_dir_list) |include_dir| {
try writer.writeAll(include_dir); try writer.writeAll(include_dir);
@ -125,7 +125,7 @@ pub fn main() !void {
}; };
defer libc.deinit(gpa); defer libc.deinit(gpa);
var bw = std.io.bufferedWriter(std.fs.File.stdout().writer()); var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
try libc.render(bw.writer()); try libc.render(bw.writer());
try bw.flush(); try bw.flush();
} }

View File

@ -635,11 +635,11 @@ const HexWriter = struct {
const payload_bytes = self.getPayloadBytes(); const payload_bytes = self.getPayloadBytes();
assert(payload_bytes.len <= MAX_PAYLOAD_LEN); assert(payload_bytes.len <= MAX_PAYLOAD_LEN);
const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3X}{4X:0>2}" ++ linesep, .{
@as(u8, @intCast(payload_bytes.len)), @as(u8, @intCast(payload_bytes.len)),
self.address, self.address,
@intFromEnum(self.payload), @intFromEnum(self.payload),
std.fmt.fmtSliceHexUpper(payload_bytes), payload_bytes,
self.checksum(), self.checksum(),
}); });
try file.writeAll(line); try file.writeAll(line);
@ -1495,7 +1495,7 @@ const ElfFileHelper = struct {
if (size < prefix.len) return null; if (size < prefix.len) return null;
try in_file.seekTo(offset); try in_file.seekTo(offset);
var section_reader = std.io.limitedReader(in_file.reader(), size); var section_reader = std.io.limitedReader(in_file.deprecatedReader(), size);
// allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed. // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.
const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size)); const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size));

View File

@ -68,7 +68,7 @@ pub fn main() !void {
const arg = args[i]; const arg = args[i];
if (mem.startsWith(u8, arg, "-")) { if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
const stdout = std.fs.File.stdout().writer(); const stdout = std.fs.File.stdout().deprecatedWriter();
try stdout.writeAll(usage); try stdout.writeAll(usage);
return std.process.cleanExit(); return std.process.cleanExit();
} else if (mem.eql(u8, arg, "--")) { } else if (mem.eql(u8, arg, "--")) {

View File

@ -127,7 +127,7 @@ pub const Diagnostics = struct {
pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void { pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {
std.debug.lockStdErr(); std.debug.lockStdErr();
defer std.debug.unlockStdErr(); defer std.debug.unlockStdErr();
const stderr = std.fs.File.stderr().writer(); const stderr = std.fs.File.stderr().deprecatedWriter();
self.renderToWriter(args, stderr, config) catch return; self.renderToWriter(args, stderr, config) catch return;
} }

View File

@ -570,7 +570,7 @@ pub const Compiler = struct {
switch (predefined_type) { switch (predefined_type) {
.GROUP_ICON, .GROUP_CURSOR => { .GROUP_ICON, .GROUP_CURSOR => {
// Check for animated icon first // Check for animated icon first
if (ani.isAnimatedIcon(file.reader())) { if (ani.isAnimatedIcon(file.deprecatedReader())) {
// Animated icons are just put into the resource unmodified, // Animated icons are just put into the resource unmodified,
// and the resource type changes to ANIICON/ANICURSOR // and the resource type changes to ANIICON/ANICURSOR
@ -586,14 +586,14 @@ pub const Compiler = struct {
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
try file.seekTo(0); try file.seekTo(0);
try writeResourceData(writer, file.reader(), header.data_size); try writeResourceData(writer, file.deprecatedReader(), header.data_size);
return; return;
} }
// isAnimatedIcon moved the file cursor so reset to the start // isAnimatedIcon moved the file cursor so reset to the start
try file.seekTo(0); try file.seekTo(0);
const icon_dir = ico.read(self.allocator, file.reader(), try file.getEndPos()) catch |err| switch (err) { const icon_dir = ico.read(self.allocator, file.deprecatedReader(), try file.getEndPos()) catch |err| switch (err) {
error.OutOfMemory => |e| return e, error.OutOfMemory => |e| return e,
else => |e| { else => |e| {
return self.iconReadError( return self.iconReadError(
@ -672,7 +672,7 @@ pub const Compiler = struct {
} }
try file.seekTo(entry.data_offset_from_start_of_file); try file.seekTo(entry.data_offset_from_start_of_file);
var header_bytes = file.reader().readBytesNoEof(16) catch { var header_bytes = file.deprecatedReader().readBytesNoEof(16) catch {
return self.iconReadError( return self.iconReadError(
error.UnexpectedEOF, error.UnexpectedEOF,
filename_utf8, filename_utf8,
@ -803,7 +803,7 @@ pub const Compiler = struct {
} }
try file.seekTo(entry.data_offset_from_start_of_file); try file.seekTo(entry.data_offset_from_start_of_file);
try writeResourceDataNoPadding(writer, file.reader(), entry.data_size_in_bytes); try writeResourceDataNoPadding(writer, file.deprecatedReader(), entry.data_size_in_bytes);
try writeDataPadding(writer, full_data_size); try writeDataPadding(writer, full_data_size);
if (self.state.icon_id == std.math.maxInt(u16)) { if (self.state.icon_id == std.math.maxInt(u16)) {
@ -859,7 +859,7 @@ pub const Compiler = struct {
header.applyMemoryFlags(node.common_resource_attributes, self.source); header.applyMemoryFlags(node.common_resource_attributes, self.source);
const file_size = try file.getEndPos(); const file_size = try file.getEndPos();
const bitmap_info = bmp.read(file.reader(), file_size) catch |err| { const bitmap_info = bmp.read(file.deprecatedReader(), file_size) catch |err| {
const filename_string_index = try self.diagnostics.putString(filename_utf8); const filename_string_index = try self.diagnostics.putString(filename_utf8);
return self.addErrorDetailsAndFail(.{ return self.addErrorDetailsAndFail(.{
.err = .bmp_read_error, .err = .bmp_read_error,
@ -922,7 +922,7 @@ pub const Compiler = struct {
header.data_size = bmp_bytes_to_write; header.data_size = bmp_bytes_to_write;
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
try file.seekTo(bmp.file_header_len); try file.seekTo(bmp.file_header_len);
const file_reader = file.reader(); const file_reader = file.deprecatedReader();
try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size); try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size);
if (bitmap_info.getBitmasksByteLen() > 0) { if (bitmap_info.getBitmasksByteLen() > 0) {
try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen()); try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen());
@ -968,7 +968,7 @@ pub const Compiler = struct {
header.data_size = @intCast(file_size); header.data_size = @intCast(file_size);
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
var header_slurping_reader = headerSlurpingReader(148, file.reader()); var header_slurping_reader = headerSlurpingReader(148, file.deprecatedReader());
try writeResourceData(writer, header_slurping_reader.reader(), header.data_size); try writeResourceData(writer, header_slurping_reader.reader(), header.data_size);
try self.state.font_dir.add(self.arena, FontDir.Font{ try self.state.font_dir.add(self.arena, FontDir.Font{
@ -1002,7 +1002,7 @@ pub const Compiler = struct {
// We now know that the data size will fit in a u32 // We now know that the data size will fit in a u32
header.data_size = @intCast(data_size); header.data_size = @intCast(data_size);
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
try writeResourceData(writer, file.reader(), header.data_size); try writeResourceData(writer, file.deprecatedReader(), header.data_size);
} }
fn iconReadError( fn iconReadError(

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert;
const Token = @import("lex.zig").Token; const Token = @import("lex.zig").Token;
const SourceMappings = @import("source_mapping.zig").SourceMappings; const SourceMappings = @import("source_mapping.zig").SourceMappings;
const utils = @import("utils.zig"); const utils = @import("utils.zig");
@ -63,7 +64,7 @@ pub const Diagnostics = struct {
pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void { pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void {
std.debug.lockStdErr(); std.debug.lockStdErr();
defer std.debug.unlockStdErr(); defer std.debug.unlockStdErr();
const stderr = std.fs.File.stderr().writer(); const stderr = std.fs.File.stderr().deprecatedWriter();
for (self.errors.items) |err_details| { for (self.errors.items) |err_details| {
renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return; renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;
} }
@ -409,15 +410,7 @@ pub const ErrorDetails = struct {
failed_to_open_cwd, failed_to_open_cwd,
}; };
fn formatToken( fn formatToken(ctx: TokenFormatContext, writer: *std.io.Writer) std.io.Writer.Error!void {
ctx: TokenFormatContext,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;
switch (ctx.token.id) { switch (ctx.token.id) {
.eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()), .eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()),
else => {}, else => {},
@ -441,7 +434,7 @@ pub const ErrorDetails = struct {
code_page: SupportedCodePage, code_page: SupportedCodePage,
}; };
fn fmtToken(self: ErrorDetails, source: []const u8) std.fmt.Formatter(formatToken) { fn fmtToken(self: ErrorDetails, source: []const u8) std.fmt.Formatter(TokenFormatContext, formatToken) {
return .{ .data = .{ return .{ .data = .{
.token = self.token, .token = self.token,
.code_page = self.code_page, .code_page = self.code_page,
@ -466,10 +459,14 @@ pub const ErrorDetails = struct {
.hint => return, .hint => return,
}, },
.illegal_byte => { .illegal_byte => {
return writer.print("character '{s}' is not allowed", .{std.fmt.fmtSliceEscapeUpper(self.token.slice(source))}); return writer.print("character '{f}' is not allowed", .{
std.ascii.hexEscape(self.token.slice(source), .upper),
});
}, },
.illegal_byte_outside_string_literals => { .illegal_byte_outside_string_literals => {
return writer.print("character '{s}' is not allowed outside of string literals", .{std.fmt.fmtSliceEscapeUpper(self.token.slice(source))}); return writer.print("character '{f}' is not allowed outside of string literals", .{
std.ascii.hexEscape(self.token.slice(source), .upper),
});
}, },
.illegal_codepoint_outside_string_literals => { .illegal_codepoint_outside_string_literals => {
// This is somewhat hacky, but we know that: // This is somewhat hacky, but we know that:
@ -1106,7 +1103,7 @@ const CorrespondingLines = struct {
.code_page = err_details.code_page, .code_page = err_details.code_page,
}; };
corresponding_lines.buffered_reader = BufferedReaderType{ corresponding_lines.buffered_reader = BufferedReaderType{
.unbuffered_reader = corresponding_lines.file.reader(), .unbuffered_reader = corresponding_lines.file.deprecatedReader(),
}; };
errdefer corresponding_lines.deinit(); errdefer corresponding_lines.deinit();

View File

@ -237,7 +237,9 @@ pub const Lexer = struct {
} }
pub fn dump(self: *Self, token: *const Token) void { pub fn dump(self: *Self, token: *const Token) void {
std.debug.print("{s}:{d}: {s}\n", .{ @tagName(token.id), token.line_number, std.fmt.fmtSliceEscapeLower(token.slice(self.buffer)) }); std.debug.print("{s}:{d}: {f}\n", .{
@tagName(token.id), token.line_number, std.ascii.hexEscape(token.slice(self.buffer), .lower),
});
} }
pub const LexMethod = enum { pub const LexMethod = enum {

View File

@ -29,7 +29,7 @@ pub fn main() !void {
defer std.process.argsFree(allocator, args); defer std.process.argsFree(allocator, args);
if (args.len < 2) { if (args.len < 2) {
try renderErrorMessage(stderr.writer(), stderr_config, .err, "expected zig lib dir as first argument", .{}); try renderErrorMessage(stderr.deprecatedWriter(), stderr_config, .err, "expected zig lib dir as first argument", .{});
std.process.exit(1); std.process.exit(1);
} }
const zig_lib_dir = args[1]; const zig_lib_dir = args[1];
@ -82,14 +82,14 @@ pub fn main() !void {
if (options.print_help_and_exit) { if (options.print_help_and_exit) {
const stdout = std.fs.File.stdout(); const stdout = std.fs.File.stdout();
try cli.writeUsage(stdout.writer(), "zig rc"); try cli.writeUsage(stdout.deprecatedWriter(), "zig rc");
return; return;
} }
// Don't allow verbose when integrating with Zig via stdout // Don't allow verbose when integrating with Zig via stdout
options.verbose = false; options.verbose = false;
const stdout_writer = std.fs.File.stdout().writer(); const stdout_writer = std.fs.File.stdout().deprecatedWriter();
if (options.verbose) { if (options.verbose) {
try options.dumpVerbose(stdout_writer); try options.dumpVerbose(stdout_writer);
try stdout_writer.writeByte('\n'); try stdout_writer.writeByte('\n');
@ -290,7 +290,7 @@ pub fn main() !void {
}; };
defer depfile.close(); defer depfile.close();
const depfile_writer = depfile.writer(); const depfile_writer = depfile.deprecatedWriter();
var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer); var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer);
switch (options.depfile_fmt) { switch (options.depfile_fmt) {
.json => { .json => {
@ -645,7 +645,7 @@ const ErrorHandler = union(enum) {
}, },
.tty => { .tty => {
// extra newline to separate this line from the aro errors // extra newline to separate this line from the aro errors
try renderErrorMessage(std.fs.File.stderr().writer(), self.tty, .err, "{s}\n", .{fail_msg}); try renderErrorMessage(std.fs.File.stderr().deprecatedWriter(), self.tty, .err, "{s}\n", .{fail_msg});
aro.Diagnostics.render(comp, self.tty); aro.Diagnostics.render(comp, self.tty);
}, },
} }
@ -690,7 +690,7 @@ const ErrorHandler = union(enum) {
try server.serveErrorBundle(error_bundle); try server.serveErrorBundle(error_bundle);
}, },
.tty => { .tty => {
try renderErrorMessage(std.fs.File.stderr().writer(), self.tty, msg_type, format, args); try renderErrorMessage(std.fs.File.stderr().deprecatedWriter(), self.tty, msg_type, format, args);
}, },
} }
} }

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert;
const rc = @import("rc.zig"); const rc = @import("rc.zig");
const ResourceType = rc.ResourceType; const ResourceType = rc.ResourceType;
const CommonResourceAttributes = rc.CommonResourceAttributes; const CommonResourceAttributes = rc.CommonResourceAttributes;
@ -163,14 +164,8 @@ pub const Language = packed struct(u16) {
return @bitCast(self); return @bitCast(self);
} }
pub fn format( pub fn format(language: Language, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
language: Language, comptime assert(fmt.len == 0);
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
_ = fmt;
_ = options;
const language_id = language.asInt(); const language_id = language.asInt();
const language_name = language_name: { const language_name = language_name: {
if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| { if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {
@ -181,7 +176,7 @@ pub const Language = packed struct(u16) {
} }
break :language_name "<UNKNOWN>"; break :language_name "<UNKNOWN>";
}; };
try out_stream.print("{s} (0x{X})", .{ language_name, language_id }); try w.print("{s} (0x{X})", .{ language_name, language_id });
} }
}; };
@ -445,47 +440,34 @@ pub const NameOrOrdinal = union(enum) {
} }
} }
pub fn format( pub fn format(self: NameOrOrdinal, w: *std.io.Writer, comptime fmt: []const u8) !void {
self: NameOrOrdinal, comptime assert(fmt.len == 0);
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
_ = fmt;
_ = options;
switch (self) { switch (self) {
.name => |name| { .name => |name| {
try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)}); try w.print("{s}", .{std.unicode.fmtUtf16Le(name)});
}, },
.ordinal => |ordinal| { .ordinal => |ordinal| {
try out_stream.print("{d}", .{ordinal}); try w.print("{d}", .{ordinal});
}, },
} }
} }
fn formatResourceType( fn formatResourceType(self: NameOrOrdinal, w: *std.io.Writer) std.io.Writer.Error!void {
self: NameOrOrdinal,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
_ = fmt;
_ = options;
switch (self) { switch (self) {
.name => |name| { .name => |name| {
try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)}); try w.print("{s}", .{std.unicode.fmtUtf16Le(name)});
}, },
.ordinal => |ordinal| { .ordinal => |ordinal| {
if (std.enums.tagName(RT, @enumFromInt(ordinal))) |predefined_type_name| { if (std.enums.tagName(RT, @enumFromInt(ordinal))) |predefined_type_name| {
try out_stream.print("{s}", .{predefined_type_name}); try w.print("{s}", .{predefined_type_name});
} else { } else {
try out_stream.print("{d}", .{ordinal}); try w.print("{d}", .{ordinal});
} }
}, },
} }
} }
pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(formatResourceType) { pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(NameOrOrdinal, formatResourceType) {
return .{ .data = type_value }; return .{ .data = type_value };
} }
}; };

View File

@ -328,7 +328,7 @@ pub fn mainSimple() anyerror!void {
passed += 1; passed += 1;
} }
if (enable_print and print_summary) { if (enable_print and print_summary) {
stderr.writer().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {}; stderr.deprecatedWriter().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {};
} }
if (failed != 0) std.process.exit(1); if (failed != 0) std.process.exit(1);
} }

View File

@ -160,7 +160,7 @@ fn mainImpl() !void {
var doc = try parser.endInput(); var doc = try parser.endInput();
defer doc.deinit(gpa); defer doc.deinit(gpa);
var stdout_buf = std.io.bufferedWriter(std.fs.File.stdout().writer()); var stdout_buf = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
try doc.render(stdout_buf.writer()); try doc.render(stdout_buf.writer());
try stdout_buf.flush(); try stdout_buf.flush();
} }

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const Document = @import("Document.zig"); const Document = @import("Document.zig");
const Node = Document.Node; const Node = Document.Node;
const assert = std.debug.assert;
/// A Markdown document renderer. /// A Markdown document renderer.
/// ///
@ -229,18 +230,11 @@ pub fn renderInlineNodeText(
} }
} }
pub fn fmtHtml(bytes: []const u8) std.fmt.Formatter(formatHtml) { pub fn fmtHtml(bytes: []const u8) std.fmt.Formatter([]const u8, formatHtml) {
return .{ .data = bytes }; return .{ .data = bytes };
} }
fn formatHtml( fn formatHtml(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
bytes: []const u8,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;
for (bytes) |b| { for (bytes) |b| {
switch (b) { switch (b) {
'<' => try writer.writeAll("&lt;"), '<' => try writer.writeAll("&lt;"),

View File

@ -5,7 +5,7 @@ pub fn bufferedPrint() !void {
// Stdout is for the actual output of your application, for example if you // Stdout is for the actual output of your application, for example if you
// are implementing gzip, then only the compressed bytes should be sent to // are implementing gzip, then only the compressed bytes should be sent to
// stdout, not any debugging messages. // stdout, not any debugging messages.
const stdout_file = std.fs.File.stdout().writer(); const stdout_file = std.fs.File.stdout().deprecatedWriter();
// Buffering can improve performance significantly in print-heavy programs. // Buffering can improve performance significantly in print-heavy programs.
var bw = std.io.bufferedWriter(stdout_file); var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer(); const stdout = bw.writer();

View File

@ -1745,7 +1745,7 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8
return true; return true;
}, },
.lazy_path, .lazy_path_list => { .lazy_path, .lazy_path_list => {
log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{}", .{ name, std.zig.fmtId(@tagName(gop.value_ptr.value)) }); log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{f}", .{ name, std.zig.fmtId(@tagName(gop.value_ptr.value)) });
return true; return true;
}, },
} }
@ -2059,7 +2059,7 @@ pub fn runAllowFail(
try Step.handleVerbose2(b, null, child.env_map, argv); try Step.handleVerbose2(b, null, child.env_map, argv);
try child.spawn(); try child.spawn();
const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_output_size) catch { const stdout = child.stdout.?.deprecatedReader().readAllAlloc(b.allocator, max_output_size) catch {
return error.ReadFailure; return error.ReadFailure;
}; };
errdefer b.allocator.free(stdout); errdefer b.allocator.free(stdout);
@ -2770,7 +2770,7 @@ fn dumpBadDirnameHelp(
defer debug.unlockStdErr(); defer debug.unlockStdErr();
const stderr: fs.File = .stderr(); const stderr: fs.File = .stderr();
const w = stderr.writer(); const w = stderr.deprecatedWriter();
try w.print(msg, args); try w.print(msg, args);
const tty_config = std.io.tty.detectConfig(stderr); const tty_config = std.io.tty.detectConfig(stderr);
@ -2785,7 +2785,7 @@ fn dumpBadDirnameHelp(
if (asking_step) |as| { if (asking_step) |as| {
tty_config.setColor(w, .red) catch {}; 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 stderr.deprecatedWriter().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 {}; tty_config.setColor(w, .reset) catch {};
as.dump(stderr); as.dump(stderr);
@ -2803,7 +2803,7 @@ pub fn dumpBadGetPathHelp(
src_builder: *Build, src_builder: *Build,
asking_step: ?*Step, asking_step: ?*Step,
) anyerror!void { ) anyerror!void {
const w = stderr.writer(); const w = stderr.deprecatedWriter();
try w.print( try w.print(
\\getPath() was called on a GeneratedFile that wasn't built yet. \\getPath() was called on a GeneratedFile that wasn't built yet.
\\ source package path: {s} \\ source package path: {s}
@ -2822,7 +2822,7 @@ pub fn dumpBadGetPathHelp(
s.dump(stderr); s.dump(stderr);
if (asking_step) |as| { if (asking_step) |as| {
tty_config.setColor(w, .red) catch {}; 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 stderr.deprecatedWriter().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 {}; tty_config.setColor(w, .reset) catch {};
as.dump(stderr); as.dump(stderr);

View File

@ -68,7 +68,7 @@ const PrefixedPath = struct {
fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath {
const gpa = cache.gpa; const gpa = cache.gpa;
const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path}); const resolved_path = try fs.path.resolve(gpa, &.{file_path});
errdefer gpa.free(resolved_path); errdefer gpa.free(resolved_path);
return findPrefixResolved(cache, resolved_path); return findPrefixResolved(cache, resolved_path);
} }
@ -132,7 +132,7 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
/// Initial state with random bytes, that can be copied. /// Initial state with random bytes, that can be copied.
/// Refresh this with new random bytes when the manifest /// Refresh this with new random bytes when the manifest
/// format is modified in a non-backwards-compatible way. /// format is modified in a non-backwards-compatible way.
pub const hasher_init: Hasher = Hasher.init(&[_]u8{ pub const hasher_init: Hasher = Hasher.init(&.{
0x33, 0x52, 0xa2, 0x84, 0x33, 0x52, 0xa2, 0x84,
0xcf, 0x17, 0x56, 0x57, 0xcf, 0x17, 0x56, 0x57,
0x01, 0xbb, 0xcd, 0xe4, 0x01, 0xbb, 0xcd, 0xe4,
@ -286,11 +286,8 @@ pub const HashHelper = struct {
pub fn binToHex(bin_digest: BinDigest) HexDigest { pub fn binToHex(bin_digest: BinDigest) HexDigest {
var out_digest: HexDigest = undefined; var out_digest: HexDigest = undefined;
_ = fmt.bufPrint( var w: std.io.Writer = .fixed(&out_digest);
&out_digest, w.printHex(&bin_digest, .lower) catch unreachable;
"{s}",
.{fmt.fmtSliceHexLower(&bin_digest)},
) catch unreachable;
return out_digest; return out_digest;
} }
@ -337,7 +334,6 @@ pub const Manifest = struct {
manifest_create: fs.File.OpenError, manifest_create: fs.File.OpenError,
manifest_read: fs.File.ReadError, manifest_read: fs.File.ReadError,
manifest_lock: fs.File.LockError, manifest_lock: fs.File.LockError,
manifest_seek: fs.File.SeekError,
file_open: FileOp, file_open: FileOp,
file_stat: FileOp, file_stat: FileOp,
file_read: FileOp, file_read: FileOp,
@ -611,12 +607,6 @@ pub const Manifest = struct {
var file = self.files.pop().?; var file = self.files.pop().?;
file.key.deinit(self.cache.gpa); file.key.deinit(self.cache.gpa);
} }
// Also, seek the file back to the start.
self.manifest_file.?.seekTo(0) catch |err| {
self.diagnostic = .{ .manifest_seek = err };
return error.CacheCheckFailed;
};
switch (try self.hitWithCurrentLock()) { switch (try self.hitWithCurrentLock()) {
.hit => break :hit, .hit => break :hit,
.miss => |m| break :digests m.file_digests_populated, .miss => |m| break :digests m.file_digests_populated,
@ -661,9 +651,8 @@ pub const Manifest = struct {
return true; return true;
} }
/// Assumes that `self.hash.hasher` has been updated only with the original digest, that /// Assumes that `self.hash.hasher` has been updated only with the original digest and that
/// `self.files` contains only the original input files, and that `self.manifest_file.?` is /// `self.files` contains only the original input files.
/// seeked to the start of the file.
fn hitWithCurrentLock(self: *Manifest) HitError!union(enum) { fn hitWithCurrentLock(self: *Manifest) HitError!union(enum) {
hit, hit,
miss: struct { miss: struct {
@ -672,12 +661,13 @@ pub const Manifest = struct {
} { } {
const gpa = self.cache.gpa; const gpa = self.cache.gpa;
const input_file_count = self.files.entries.len; const input_file_count = self.files.entries.len;
var manifest_reader = self.manifest_file.?.reader(&.{}); // Reads positionally from zero.
const file_contents = self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max) catch |err| switch (err) { const limit: std.io.Limit = .limited(manifest_file_size_max);
const file_contents = manifest_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
error.StreamTooLong => return error.OutOfMemory, error.StreamTooLong => return error.OutOfMemory,
else => |e| { error.ReadFailed => {
self.diagnostic = .{ .manifest_read = e }; self.diagnostic = .{ .manifest_read = manifest_reader.err.? };
return error.CacheCheckFailed; return error.CacheCheckFailed;
}, },
}; };
@ -1063,14 +1053,17 @@ pub const Manifest = struct {
} }
fn addDepFileMaybePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void { 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); const gpa = self.cache.gpa;
defer self.cache.gpa.free(dep_file_contents); 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); var error_buf: std.ArrayListUnmanaged(u8) = .empty;
defer error_buf.deinit(); defer error_buf.deinit(gpa);
var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
defer resolve_buf.deinit(gpa);
var it: DepTokenizer = .{ .bytes = dep_file_contents }; var it: DepTokenizer = .{ .bytes = dep_file_contents };
while (it.next()) |token| { while (it.next()) |token| {
switch (token) { switch (token) {
// We don't care about targets, we only want the prereqs // We don't care about targets, we only want the prereqs
@ -1080,16 +1073,14 @@ pub const Manifest = struct {
_ = try self.addFile(file_path, null); _ = try self.addFile(file_path, null);
} else try self.addFilePost(file_path), } else try self.addFilePost(file_path),
.prereq_must_resolve => { .prereq_must_resolve => {
var resolve_buf = std.ArrayList(u8).init(self.cache.gpa); resolve_buf.clearRetainingCapacity();
defer resolve_buf.deinit(); try token.resolve(gpa, &resolve_buf);
try token.resolve(resolve_buf.writer());
if (self.manifest_file == null) { if (self.manifest_file == null) {
_ = try self.addFile(resolve_buf.items, null); _ = try self.addFile(resolve_buf.items, null);
} else try self.addFilePost(resolve_buf.items); } else try self.addFilePost(resolve_buf.items);
}, },
else => |err| { 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 }); log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
return error.InvalidDepFile; return error.InvalidDepFile;
}, },
@ -1127,24 +1118,25 @@ pub const Manifest = struct {
if (self.manifest_dirty) { if (self.manifest_dirty) {
self.manifest_dirty = false; self.manifest_dirty = false;
var contents = std.ArrayList(u8).init(self.cache.gpa); const gpa = self.cache.gpa;
defer contents.deinit(); var contents: std.ArrayListUnmanaged(u8) = .empty;
defer contents.deinit(gpa);
const writer = contents.writer(); try contents.appendSlice(gpa, manifest_header ++ "\n");
try writer.writeAll(manifest_header ++ "\n");
for (self.files.keys()) |file| { for (self.files.keys()) |file| {
try writer.print("{d} {d} {d} {} {d} {s}\n", .{ try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{
file.stat.size, file.stat.size,
file.stat.inode, file.stat.inode,
file.stat.mtime, file.stat.mtime,
fmt.fmtSliceHexLower(&file.bin_digest), &file.bin_digest,
file.prefixed_path.prefix, file.prefixed_path.prefix,
file.prefixed_path.sub_path, file.prefixed_path.sub_path,
}); });
} }
try manifest_file.setEndPos(contents.items.len); try manifest_file.setEndPos(contents.items.len);
try manifest_file.pwriteAll(contents.items, 0); var pos: usize = 0;
while (pos < contents.items.len) pos += try manifest_file.pwrite(contents.items[pos..], pos);
} }
if (self.want_shared_lock) { if (self.want_shared_lock) {

View File

@ -7,6 +7,7 @@ state: State = .lhs,
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const assert = std.debug.assert; const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
pub fn next(self: *Tokenizer) ?Token { pub fn next(self: *Tokenizer) ?Token {
var start = self.index; 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. /// 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) { switch (self) {
.target_must_resolve => |bytes| { .target_must_resolve => |bytes| {
var state: enum { start, escape, dollar } = .start; var state: enum { start, escape, dollar } = .start;
@ -372,27 +373,27 @@ pub const Token = union(enum) {
switch (c) { switch (c) {
'\\' => state = .escape, '\\' => state = .escape,
'$' => state = .dollar, '$' => state = .dollar,
else => try writer.writeByte(c), else => try list.append(gpa, c),
} }
}, },
.escape => { .escape => {
switch (c) { switch (c) {
' ', '#', '\\' => {}, ' ', '#', '\\' => {},
'$' => { '$' => {
try writer.writeByte('\\'); try list.append(gpa, '\\');
state = .dollar; state = .dollar;
continue; continue;
}, },
else => try writer.writeByte('\\'), else => try list.append(gpa, '\\'),
} }
try writer.writeByte(c); try list.append(gpa, c);
state = .start; state = .start;
}, },
.dollar => { .dollar => {
try writer.writeByte('$'); try list.append(gpa, '$');
switch (c) { switch (c) {
'$' => {}, '$' => {},
else => try writer.writeByte(c), else => try list.append(gpa, c),
} }
state = .start; state = .start;
}, },
@ -406,19 +407,19 @@ pub const Token = union(enum) {
.start => { .start => {
switch (c) { switch (c) {
'\\' => state = .escape, '\\' => state = .escape,
else => try writer.writeByte(c), else => try list.append(gpa, c),
} }
}, },
.escape => { .escape => {
switch (c) { switch (c) {
' ' => {}, ' ' => {},
'\\' => { '\\' => {
try writer.writeByte(c); try list.append(gpa, c);
continue; continue;
}, },
else => try writer.writeByte('\\'), else => try list.append(gpa, '\\'),
} }
try writer.writeByte(c); try list.append(gpa, c);
state = .start; 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) { switch (self) {
.target, .target_must_resolve, .prereq, .prereq_must_resolve => unreachable, // not an error .target, .target_must_resolve, .prereq, .prereq_must_resolve => unreachable, // not an error
.incomplete_quoted_prerequisite, .incomplete_quoted_prerequisite,
.incomplete_target, .incomplete_target,
=> |index_and_bytes| { => |index_and_bytes| {
try writer.print("{s} '", .{self.errStr()}); try list.print(gpa, "{s} '", .{self.errStr()});
if (self == .incomplete_target) { if (self == .incomplete_target) {
const tmp = Token{ .target_must_resolve = index_and_bytes.bytes }; const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
try tmp.resolve(writer); try tmp.resolve(gpa, list);
} else { } 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, .invalid_target,
.bad_target_escape, .bad_target_escape,
@ -450,9 +451,9 @@ pub const Token = union(enum) {
.incomplete_escape, .incomplete_escape,
.expected_colon, .expected_colon,
=> |index_and_char| { => |index_and_char| {
try writer.writeAll("illegal char "); try list.appendSlice(gpa, "illegal char ");
try printUnderstandableChar(writer, index_and_char.char); try printUnderstandableChar(gpa, list, index_and_char.char);
try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() }); 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(); defer arena_allocator.deinit();
var it: Tokenizer = .{ .bytes = input }; var it: Tokenizer = .{ .bytes = input };
var buffer = std.ArrayList(u8).init(arena); var buffer: std.ArrayListUnmanaged(u8) = .empty;
var resolve_buf = std.ArrayList(u8).init(arena); var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
var i: usize = 0; var i: usize = 0;
while (it.next()) |token| { while (it.next()) |token| {
if (i != 0) try buffer.appendSlice("\n"); if (i != 0) try buffer.appendSlice(arena, "\n");
switch (token) { switch (token) {
.target, .prereq => |bytes| { .target, .prereq => |bytes| {
try buffer.appendSlice(@tagName(token)); try buffer.appendSlice(arena, @tagName(token));
try buffer.appendSlice(" = {"); try buffer.appendSlice(arena, " = {");
for (bytes) |b| { 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 => { .target_must_resolve => {
try buffer.appendSlice("target = {"); try buffer.appendSlice(arena, "target = {");
try token.resolve(resolve_buf.writer()); try token.resolve(arena, &resolve_buf);
for (resolve_buf.items) |b| { 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; resolve_buf.items.len = 0;
try buffer.appendSlice("}"); try buffer.appendSlice(arena, "}");
}, },
.prereq_must_resolve => { .prereq_must_resolve => {
try buffer.appendSlice("prereq = {"); try buffer.appendSlice(arena, "prereq = {");
try token.resolve(resolve_buf.writer()); try token.resolve(arena, &resolve_buf);
for (resolve_buf.items) |b| { 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; resolve_buf.items.len = 0;
try buffer.appendSlice("}"); try buffer.appendSlice(arena, "}");
}, },
else => { else => {
try buffer.appendSlice("ERROR: "); try buffer.appendSlice(arena, "ERROR: ");
try token.printError(buffer.writer()); try token.printError(arena, &buffer);
break; break;
}, },
} }
@ -1072,134 +1073,18 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
return; return;
} }
const out = std.fs.File.stderr().writer(); try testing.expectEqualStrings(expect, buffer.items);
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 { fn printCharValues(gpa: Allocator, list: *std.ArrayListUnmanaged(u8), bytes: []const u8) !void {
try printLabel(out, label, bytes); for (bytes) |b| try list.append(gpa, printable_char_tab[b]);
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 { fn printUnderstandableChar(gpa: Allocator, list: *std.ArrayListUnmanaged(u8), char: 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]);
}
fn printCharValues(out: anytype, bytes: []const u8) !void {
for (bytes) |b| {
try out.writeAll(&[_]u8{printable_char_tab[b]});
}
}
fn printUnderstandableChar(out: anytype, char: u8) !void {
if (std.ascii.isPrint(char)) { if (std.ascii.isPrint(char)) {
try out.print("'{c}'", .{char}); try list.print(gpa, "'{c}'", .{char});
} else { } else {
try out.print("\\x{X:0>2}", .{char}); try list.print(gpa, "\\x{X:0>2}", .{char});
} }
} }

View File

@ -1,5 +1,6 @@
const Directory = @This(); const Directory = @This();
const std = @import("../../std.zig"); const std = @import("../../std.zig");
const assert = std.debug.assert;
const fs = std.fs; const fs = std.fs;
const fmt = std.fmt; const fmt = std.fmt;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@ -55,14 +56,8 @@ pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
self.* = undefined; self.* = undefined;
} }
pub fn format( pub fn format(self: Directory, writer: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
self: Directory, comptime assert(f.len == 0);
comptime fmt_string: []const u8,
options: fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
if (fmt_string.len != 0) fmt.invalidFmtError(fmt_string, self);
if (self.path) |p| { if (self.path) |p| {
try writer.writeAll(p); try writer.writeAll(p);
try writer.writeAll(fs.path.sep_str); try writer.writeAll(fs.path.sep_str);

View File

@ -1,3 +1,10 @@
const Path = @This();
const std = @import("../../std.zig");
const assert = std.debug.assert;
const fs = std.fs;
const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache;
root_dir: Cache.Directory, root_dir: Cache.Directory,
/// The path, relative to the root dir, that this `Path` represents. /// The path, relative to the root dir, that this `Path` represents.
/// Empty string means the root_dir is the path. /// Empty string means the root_dir is the path.
@ -133,38 +140,32 @@ pub fn makePath(p: Path, sub_path: []const u8) !void {
} }
pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 { pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 {
return std.fmt.allocPrint(allocator, "{}", .{p}); return std.fmt.allocPrint(allocator, "{f}", .{p});
} }
pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 { pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 {
return std.fmt.allocPrintZ(allocator, "{}", .{p}); return std.fmt.allocPrintSentinel(allocator, "{f}", .{p}, 0);
} }
pub fn format( pub fn format(self: Path, writer: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
self: Path, if (f.len == 1) {
comptime fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
if (fmt_string.len == 1) {
// Quote-escape the string. // Quote-escape the string.
const stringEscape = std.zig.stringEscape; const zigEscape = switch (f[0]) {
const f = switch (fmt_string[0]) { 'q' => std.zig.stringEscape,
'q' => "", '\'' => std.zig.charEscape,
'\'' => "\'", else => @compileError("unsupported format string: " ++ f),
else => @compileError("unsupported format string: " ++ fmt_string),
}; };
if (self.root_dir.path) |p| { if (self.root_dir.path) |p| {
try stringEscape(p, f, options, writer); try zigEscape(p, writer);
if (self.sub_path.len > 0) try stringEscape(fs.path.sep_str, f, options, writer); if (self.sub_path.len > 0) try zigEscape(fs.path.sep_str, writer);
} }
if (self.sub_path.len > 0) { if (self.sub_path.len > 0) {
try stringEscape(self.sub_path, f, options, writer); try zigEscape(self.sub_path, writer);
} }
return; return;
} }
if (fmt_string.len > 0) if (f.len > 0)
std.fmt.invalidFmtError(fmt_string, self); std.fmt.invalidFmtError(f, self);
if (std.fs.path.isAbsolute(self.sub_path)) { if (std.fs.path.isAbsolute(self.sub_path)) {
try writer.writeAll(self.sub_path); try writer.writeAll(self.sub_path);
return; return;
@ -223,9 +224,3 @@ pub const TableAdapter = struct {
return a.eql(b); return a.eql(b);
} }
}; };
const Path = @This();
const std = @import("../../std.zig");
const fs = std.fs;
const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache;

View File

@ -170,7 +170,7 @@ fn serveFile(
// We load the file with every request so that the user can make changes to the file // We load the file with every request so that the user can make changes to the file
// and refresh the HTML page without restarting this server. // and refresh the HTML page without restarting this server.
const file_contents = ws.zig_lib_directory.handle.readFileAlloc(gpa, name, 10 * 1024 * 1024) catch |err| { const file_contents = ws.zig_lib_directory.handle.readFileAlloc(gpa, name, 10 * 1024 * 1024) catch |err| {
log.err("failed to read '{}{s}': {s}", .{ ws.zig_lib_directory, name, @errorName(err) }); log.err("failed to read '{f}{s}': {s}", .{ ws.zig_lib_directory, name, @errorName(err) });
return error.AlreadyReported; return error.AlreadyReported;
}; };
defer gpa.free(file_contents); defer gpa.free(file_contents);
@ -251,10 +251,10 @@ fn buildWasmBinary(
"-fsingle-threaded", // "-fsingle-threaded", //
"--dep", "Walk", // "--dep", "Walk", //
"--dep", "html_render", // "--dep", "html_render", //
try std.fmt.allocPrint(arena, "-Mroot={}", .{main_src_path}), // try std.fmt.allocPrint(arena, "-Mroot={f}", .{main_src_path}), //
try std.fmt.allocPrint(arena, "-MWalk={}", .{walk_src_path}), // try std.fmt.allocPrint(arena, "-MWalk={f}", .{walk_src_path}), //
"--dep", "Walk", // "--dep", "Walk", //
try std.fmt.allocPrint(arena, "-Mhtml_render={}", .{html_render_src_path}), // try std.fmt.allocPrint(arena, "-Mhtml_render={f}", .{html_render_src_path}), //
"--listen=-", "--listen=-",
}); });
@ -526,7 +526,7 @@ fn serveSourcesTar(ws: *WebServer, request: *std.http.Server.Request) !void {
for (deduped_paths) |joined_path| { for (deduped_paths) |joined_path| {
var file = joined_path.root_dir.handle.openFile(joined_path.sub_path, .{}) catch |err| { var file = joined_path.root_dir.handle.openFile(joined_path.sub_path, .{}) catch |err| {
log.err("failed to open {}: {s}", .{ joined_path, @errorName(err) }); log.err("failed to open {f}: {s}", .{ joined_path, @errorName(err) });
continue; continue;
}; };
defer file.close(); defer file.close();
@ -604,7 +604,7 @@ fn prepareTables(
const rebuilt_exe_path = run_step.rebuilt_executable.?; const rebuilt_exe_path = run_step.rebuilt_executable.?;
var debug_info = std.debug.Info.load(gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| { var debug_info = std.debug.Info.load(gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| {
log.err("step '{s}': failed to load debug information for '{}': {s}", .{ log.err("step '{s}': failed to load debug information for '{f}': {s}", .{
run_step.step.name, rebuilt_exe_path, @errorName(err), run_step.step.name, rebuilt_exe_path, @errorName(err),
}); });
return error.AlreadyReported; return error.AlreadyReported;
@ -616,7 +616,7 @@ fn prepareTables(
.sub_path = "v/" ++ std.fmt.hex(coverage_id), .sub_path = "v/" ++ std.fmt.hex(coverage_id),
}; };
var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| { var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
log.err("step '{s}': failed to load coverage file '{}': {s}", .{ log.err("step '{s}': failed to load coverage file '{f}': {s}", .{
run_step.step.name, coverage_file_path, @errorName(err), run_step.step.name, coverage_file_path, @errorName(err),
}); });
return error.AlreadyReported; return error.AlreadyReported;
@ -624,7 +624,7 @@ fn prepareTables(
defer coverage_file.close(); defer coverage_file.close();
const file_size = coverage_file.getEndPos() catch |err| { const file_size = coverage_file.getEndPos() catch |err| {
log.err("unable to check len of coverage file '{}': {s}", .{ coverage_file_path, @errorName(err) }); log.err("unable to check len of coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });
return error.AlreadyReported; return error.AlreadyReported;
}; };
@ -636,7 +636,7 @@ fn prepareTables(
coverage_file.handle, coverage_file.handle,
0, 0,
) catch |err| { ) catch |err| {
log.err("failed to map coverage file '{}': {s}", .{ coverage_file_path, @errorName(err) }); log.err("failed to map coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });
return error.AlreadyReported; return error.AlreadyReported;
}; };
gop.value_ptr.mapped_memory = mapped_memory; gop.value_ptr.mapped_memory = mapped_memory;

View File

@ -186,7 +186,7 @@ pub const IncludeDir = union(enum) {
.embed_path => |lazy_path| { .embed_path => |lazy_path| {
// Special case: this is a single arg. // Special case: this is a single arg.
const resolved = lazy_path.getPath3(b, asking_step); const resolved = lazy_path.getPath3(b, asking_step);
const arg = b.fmt("--embed-dir={}", .{resolved}); const arg = b.fmt("--embed-dir={f}", .{resolved});
return zig_args.append(arg); return zig_args.append(arg);
}, },
}; };

View File

@ -287,7 +287,8 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
/// For debugging purposes, prints identifying information about this Step. /// For debugging purposes, prints identifying information about this Step.
pub fn dump(step: *Step, file: std.fs.File) void { pub fn dump(step: *Step, file: std.fs.File) void {
const w = file.writer(); var fw = file.writer(&.{});
const w = &fw.interface;
const tty_config = std.io.tty.detectConfig(file); const tty_config = std.io.tty.detectConfig(file);
const debug_info = std.debug.getSelfDebugInfo() catch |err| { const debug_info = std.debug.getSelfDebugInfo() catch |err| {
w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
@ -482,9 +483,9 @@ pub fn evalZigProcess(
pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus { pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus {
const b = s.owner; const b = s.owner;
const src_path = src_lazy_path.getPath3(b, s); const src_path = src_lazy_path.getPath3(b, s);
try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{}", .{src_path}), dest_path }); try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| { return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| {
return s.fail("unable to update file from '{}' to '{s}': {s}", .{ return s.fail("unable to update file from '{f}' to '{s}': {s}", .{
src_path, dest_path, @errorName(err), src_path, dest_path, @errorName(err),
}); });
}; };
@ -821,7 +822,7 @@ fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: Build.Cac
switch (err) { switch (err) {
error.CacheCheckFailed => switch (man.diagnostic) { error.CacheCheckFailed => switch (man.diagnostic) {
.none => unreachable, .none => unreachable,
.manifest_create, .manifest_read, .manifest_lock, .manifest_seek => |e| return s.fail("failed to check cache: {s} {s}", .{ .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail("failed to check cache: {s} {s}", .{
@tagName(man.diagnostic), @errorName(e), @tagName(man.diagnostic), @errorName(e),
}), }),
.file_open, .file_stat, .file_read, .file_hash => |op| { .file_open, .file_stat, .file_read, .file_hash => |op| {

File diff suppressed because it is too large Load Diff

View File

@ -1542,7 +1542,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) { if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) {
if (compile.version) |version| { if (compile.version) |version| {
try zig_args.append("--version"); try zig_args.append("--version");
try zig_args.append(b.fmt("{}", .{version})); try zig_args.append(b.fmt("{f}", .{version}));
} }
if (compile.rootModuleTarget().os.tag.isDarwin()) { if (compile.rootModuleTarget().os.tag.isDarwin()) {
@ -1696,9 +1696,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
if (compile.build_id orelse b.build_id) |build_id| { if (compile.build_id orelse b.build_id) |build_id| {
try zig_args.append(switch (build_id) { try zig_args.append(switch (build_id) {
.hexstring => |hs| b.fmt("--build-id=0x{s}", .{ .hexstring => |hs| b.fmt("--build-id=0x{x}", .{hs.toSlice()}),
std.fmt.fmtSliceHexLower(hs.toSlice()),
}),
.none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}), .none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}),
}); });
} }
@ -1706,7 +1704,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir| const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir|
dir.getPath2(b, step) dir.getPath2(b, step)
else if (b.graph.zig_lib_directory.path) |_| else if (b.graph.zig_lib_directory.path) |_|
b.fmt("{}", .{b.graph.zig_lib_directory}) b.fmt("{f}", .{b.graph.zig_lib_directory})
else else
null; null;
@ -1746,8 +1744,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
} }
if (compile.error_limit) |err_limit| try zig_args.appendSlice(&.{ if (compile.error_limit) |err_limit| try zig_args.appendSlice(&.{
"--error-limit", "--error-limit", b.fmt("{d}", .{err_limit}),
b.fmt("{}", .{err_limit}),
}); });
try addFlag(&zig_args, "incremental", b.graph.incremental); try addFlag(&zig_args, "incremental", b.graph.incremental);
@ -1793,11 +1790,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
var args_hash: [Sha256.digest_length]u8 = undefined; var args_hash: [Sha256.digest_length]u8 = undefined;
Sha256.hash(args, &args_hash, .{}); Sha256.hash(args, &args_hash, .{});
var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
_ = try std.fmt.bufPrint( _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});
&args_hex_hash,
"{s}",
.{std.fmt.fmtSliceHexLower(&args_hash)},
);
const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
try b.cache_root.handle.writeFile(.{ .sub_path = args_file, .data = args }); try b.cache_root.handle.writeFile(.{ .sub_path = args_file, .data = args });
@ -1836,7 +1829,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
// Update generated files // Update generated files
if (maybe_output_dir) |output_dir| { if (maybe_output_dir) |output_dir| {
if (compile.emit_directory) |lp| { if (compile.emit_directory) |lp| {
lp.path = b.fmt("{}", .{output_dir}); lp.path = b.fmt("{f}", .{output_dir});
} }
// zig fmt: off // zig fmt: off

View File

@ -2,6 +2,7 @@ const std = @import("std");
const ConfigHeader = @This(); const ConfigHeader = @This();
const Step = std.Build.Step; const Step = std.Build.Step;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Writer = std.io.Writer;
pub const Style = union(enum) { pub const Style = union(enum) {
/// A configure format supported by autotools that uses `#undef foo` to /// A configure format supported by autotools that uses `#undef foo` to
@ -87,7 +88,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path }); owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path });
config_header.* = .{ config_header.* = .{
.step = Step.init(.{ .step = .init(.{
.id = base_id, .id = base_id,
.name = name, .name = name,
.owner = owner, .owner = owner,
@ -95,7 +96,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
.first_ret_addr = options.first_ret_addr orelse @returnAddress(), .first_ret_addr = options.first_ret_addr orelse @returnAddress(),
}), }),
.style = options.style, .style = options.style,
.values = std.StringArrayHashMap(Value).init(owner.allocator), .values = .init(owner.allocator),
.max_bytes = options.max_bytes, .max_bytes = options.max_bytes,
.include_path = include_path, .include_path = include_path,
@ -195,8 +196,9 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
man.hash.addBytes(config_header.include_path); man.hash.addBytes(config_header.include_path);
man.hash.addOptionalBytes(config_header.include_guard_override); man.hash.addOptionalBytes(config_header.include_guard_override);
var output = std.ArrayList(u8).init(gpa); var aw: std.io.Writer.Allocating = .init(gpa);
defer output.deinit(); defer aw.deinit();
const bw = &aw.interface;
const header_text = "This file was generated by ConfigHeader using the Zig Build System."; const header_text = "This file was generated by ConfigHeader using the Zig Build System.";
const c_generated_line = "/* " ++ header_text ++ " */\n"; const c_generated_line = "/* " ++ header_text ++ " */\n";
@ -204,7 +206,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
switch (config_header.style) { switch (config_header.style) {
.autoconf_undef, .autoconf, .autoconf_at => |file_source| { .autoconf_undef, .autoconf, .autoconf_at => |file_source| {
try output.appendSlice(c_generated_line); try bw.writeAll(c_generated_line);
const src_path = file_source.getPath2(b, step); const src_path = file_source.getPath2(b, step);
const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| { const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| {
return step.fail("unable to read autoconf input file '{s}': {s}", .{ return step.fail("unable to read autoconf input file '{s}': {s}", .{
@ -212,32 +214,33 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
}); });
}; };
switch (config_header.style) { switch (config_header.style) {
.autoconf_undef, .autoconf => try render_autoconf_undef(step, contents, &output, config_header.values, src_path), .autoconf_undef, .autoconf => try render_autoconf_undef(step, contents, bw, config_header.values, src_path),
.autoconf_at => try render_autoconf_at(step, contents, &output, config_header.values, src_path), .autoconf_at => try render_autoconf_at(step, contents, &aw, config_header.values, src_path),
else => unreachable, else => unreachable,
} }
}, },
.cmake => |file_source| { .cmake => |file_source| {
try output.appendSlice(c_generated_line); try bw.writeAll(c_generated_line);
const src_path = file_source.getPath2(b, step); const src_path = file_source.getPath2(b, step);
const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| { const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| {
return step.fail("unable to read cmake input file '{s}': {s}", .{ return step.fail("unable to read cmake input file '{s}': {s}", .{
src_path, @errorName(err), src_path, @errorName(err),
}); });
}; };
try render_cmake(step, contents, &output, config_header.values, src_path); try render_cmake(step, contents, bw, config_header.values, src_path);
}, },
.blank => { .blank => {
try output.appendSlice(c_generated_line); try bw.writeAll(c_generated_line);
try render_blank(&output, config_header.values, config_header.include_path, config_header.include_guard_override); try render_blank(gpa, bw, config_header.values, config_header.include_path, config_header.include_guard_override);
}, },
.nasm => { .nasm => {
try output.appendSlice(asm_generated_line); try bw.writeAll(asm_generated_line);
try render_nasm(&output, config_header.values); try render_nasm(bw, config_header.values);
}, },
} }
man.hash.addBytes(output.items); const output = aw.getWritten();
man.hash.addBytes(output);
if (try step.cacheHit(&man)) { if (try step.cacheHit(&man)) {
const digest = man.final(); const digest = man.final();
@ -256,13 +259,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
const sub_path_dirname = std.fs.path.dirname(sub_path).?; const sub_path_dirname = std.fs.path.dirname(sub_path).?;
b.cache_root.handle.makePath(sub_path_dirname) catch |err| { b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, sub_path_dirname, @errorName(err), b.cache_root, sub_path_dirname, @errorName(err),
}); });
}; };
b.cache_root.handle.writeFile(.{ .sub_path = sub_path, .data = output.items }) catch |err| { b.cache_root.handle.writeFile(.{ .sub_path = sub_path, .data = output }) catch |err| {
return step.fail("unable to write file '{}{s}': {s}", .{ return step.fail("unable to write file '{f}{s}': {s}", .{
b.cache_root, sub_path, @errorName(err), b.cache_root, sub_path, @errorName(err),
}); });
}; };
@ -274,7 +277,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
fn render_autoconf_undef( fn render_autoconf_undef(
step: *Step, step: *Step,
contents: []const u8, contents: []const u8,
output: *std.ArrayList(u8), bw: *Writer,
values: std.StringArrayHashMap(Value), values: std.StringArrayHashMap(Value),
src_path: []const u8, src_path: []const u8,
) !void { ) !void {
@ -289,15 +292,15 @@ fn render_autoconf_undef(
var line_it = std.mem.splitScalar(u8, contents, '\n'); var line_it = std.mem.splitScalar(u8, contents, '\n');
while (line_it.next()) |line| : (line_index += 1) { while (line_it.next()) |line| : (line_index += 1) {
if (!std.mem.startsWith(u8, line, "#")) { if (!std.mem.startsWith(u8, line, "#")) {
try output.appendSlice(line); try bw.writeAll(line);
try output.appendSlice("\n"); try bw.writeByte('\n');
continue; continue;
} }
var it = std.mem.tokenizeAny(u8, line[1..], " \t\r"); var it = std.mem.tokenizeAny(u8, line[1..], " \t\r");
const undef = it.next().?; const undef = it.next().?;
if (!std.mem.eql(u8, undef, "undef")) { if (!std.mem.eql(u8, undef, "undef")) {
try output.appendSlice(line); try bw.writeAll(line);
try output.appendSlice("\n"); try bw.writeByte('\n');
continue; continue;
} }
const name = it.next().?; const name = it.next().?;
@ -309,7 +312,7 @@ fn render_autoconf_undef(
continue; continue;
}; };
is_used.set(index); is_used.set(index);
try renderValueC(output, name, values.values()[index]); try renderValueC(bw, name, values.values()[index]);
} }
var unused_value_it = is_used.iterator(.{ .kind = .unset }); var unused_value_it = is_used.iterator(.{ .kind = .unset });
@ -326,12 +329,13 @@ fn render_autoconf_undef(
fn render_autoconf_at( fn render_autoconf_at(
step: *Step, step: *Step,
contents: []const u8, contents: []const u8,
output: *std.ArrayList(u8), aw: *std.io.Writer.Allocating,
values: std.StringArrayHashMap(Value), values: std.StringArrayHashMap(Value),
src_path: []const u8, src_path: []const u8,
) !void { ) !void {
const build = step.owner; const build = step.owner;
const allocator = build.allocator; const allocator = build.allocator;
const bw = &aw.interface;
const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); const used = allocator.alloc(bool, values.count()) catch @panic("OOM");
for (used) |*u| u.* = false; for (used) |*u| u.* = false;
@ -343,11 +347,11 @@ fn render_autoconf_at(
while (line_it.next()) |line| : (line_index += 1) { while (line_it.next()) |line| : (line_index += 1) {
const last_line = line_it.index == line_it.buffer.len; const last_line = line_it.index == line_it.buffer.len;
const old_len = output.items.len; const old_len = aw.getWritten().len;
expand_variables_autoconf_at(output, line, values, used) catch |err| switch (err) { expand_variables_autoconf_at(bw, line, values, used) catch |err| switch (err) {
error.MissingValue => { error.MissingValue => {
const name = output.items[old_len..]; const name = aw.getWritten()[old_len..];
defer output.shrinkRetainingCapacity(old_len); defer aw.shrinkRetainingCapacity(old_len);
try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{
src_path, line_index + 1, name, src_path, line_index + 1, name,
}); });
@ -362,9 +366,7 @@ fn render_autoconf_at(
continue; continue;
}, },
}; };
if (!last_line) { if (!last_line) try bw.writeByte('\n');
try output.append('\n');
}
} }
for (values.unmanaged.entries.slice().items(.key), used) |name, u| { for (values.unmanaged.entries.slice().items(.key), used) |name, u| {
@ -374,15 +376,13 @@ fn render_autoconf_at(
} }
} }
if (any_errors) { if (any_errors) return error.MakeFailed;
return error.MakeFailed;
}
} }
fn render_cmake( fn render_cmake(
step: *Step, step: *Step,
contents: []const u8, contents: []const u8,
output: *std.ArrayList(u8), bw: *Writer,
values: std.StringArrayHashMap(Value), values: std.StringArrayHashMap(Value),
src_path: []const u8, src_path: []const u8,
) !void { ) !void {
@ -417,10 +417,8 @@ fn render_cmake(
defer allocator.free(line); defer allocator.free(line);
if (!std.mem.startsWith(u8, line, "#")) { if (!std.mem.startsWith(u8, line, "#")) {
try output.appendSlice(line); try bw.writeAll(line);
if (!last_line) { if (!last_line) try bw.writeByte('\n');
try output.appendSlice("\n");
}
continue; continue;
} }
var it = std.mem.tokenizeAny(u8, line[1..], " \t\r"); var it = std.mem.tokenizeAny(u8, line[1..], " \t\r");
@ -428,10 +426,8 @@ fn render_cmake(
if (!std.mem.eql(u8, cmakedefine, "cmakedefine") and if (!std.mem.eql(u8, cmakedefine, "cmakedefine") and
!std.mem.eql(u8, cmakedefine, "cmakedefine01")) !std.mem.eql(u8, cmakedefine, "cmakedefine01"))
{ {
try output.appendSlice(line); try bw.writeAll(line);
if (!last_line) { if (!last_line) try bw.writeByte('\n');
try output.appendSlice("\n");
}
continue; continue;
} }
@ -502,7 +498,7 @@ fn render_cmake(
value = Value{ .ident = it.rest() }; value = Value{ .ident = it.rest() };
} }
try renderValueC(output, name, value); try renderValueC(bw, name, value);
} }
if (any_errors) { if (any_errors) {
@ -511,13 +507,14 @@ fn render_cmake(
} }
fn render_blank( fn render_blank(
output: *std.ArrayList(u8), gpa: std.mem.Allocator,
bw: *Writer,
defines: std.StringArrayHashMap(Value), defines: std.StringArrayHashMap(Value),
include_path: []const u8, include_path: []const u8,
include_guard_override: ?[]const u8, include_guard_override: ?[]const u8,
) !void { ) !void {
const include_guard_name = include_guard_override orelse blk: { const include_guard_name = include_guard_override orelse blk: {
const name = try output.allocator.dupe(u8, include_path); const name = try gpa.dupe(u8, include_path);
for (name) |*byte| { for (name) |*byte| {
switch (byte.*) { switch (byte.*) {
'a'...'z' => byte.* = byte.* - 'a' + 'A', 'a'...'z' => byte.* = byte.* - 'a' + 'A',
@ -527,92 +524,53 @@ fn render_blank(
} }
break :blk name; break :blk name;
}; };
defer if (include_guard_override == null) gpa.free(include_guard_name);
try output.appendSlice("#ifndef "); try bw.print(
try output.appendSlice(include_guard_name); \\#ifndef {[0]s}
try output.appendSlice("\n#define "); \\#define {[0]s}
try output.appendSlice(include_guard_name); \\
try output.appendSlice("\n"); , .{include_guard_name});
const values = defines.values(); const values = defines.values();
for (defines.keys(), 0..) |name, i| { for (defines.keys(), 0..) |name, i| try renderValueC(bw, name, values[i]);
try renderValueC(output, name, values[i]);
}
try output.appendSlice("#endif /* "); try bw.print(
try output.appendSlice(include_guard_name); \\#endif /* {s} */
try output.appendSlice(" */\n"); \\
, .{include_guard_name});
} }
fn render_nasm(output: *std.ArrayList(u8), defines: std.StringArrayHashMap(Value)) !void { fn render_nasm(bw: *Writer, defines: std.StringArrayHashMap(Value)) !void {
const values = defines.values(); for (defines.keys(), defines.values()) |name, value| try renderValueNasm(bw, name, value);
for (defines.keys(), 0..) |name, i| {
try renderValueNasm(output, name, values[i]);
}
} }
fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void {
switch (value) { switch (value) {
.undef => { .undef => try bw.print("/* #undef {s} */\n", .{name}),
try output.appendSlice("/* #undef "); .defined => try bw.print("#define {s}\n", .{name}),
try output.appendSlice(name); .boolean => |b| try bw.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }),
try output.appendSlice(" */\n"); .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }),
}, .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }),
.defined => {
try output.appendSlice("#define ");
try output.appendSlice(name);
try output.appendSlice("\n");
},
.boolean => |b| {
try output.appendSlice("#define ");
try output.appendSlice(name);
try output.appendSlice(if (b) " 1\n" else " 0\n");
},
.int => |i| {
try output.writer().print("#define {s} {d}\n", .{ name, i });
},
.ident => |ident| {
try output.writer().print("#define {s} {s}\n", .{ name, ident });
},
.string => |string| {
// TODO: use C-specific escaping instead of zig string literals // TODO: use C-specific escaping instead of zig string literals
try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) }); .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
},
} }
} }
fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void {
switch (value) { switch (value) {
.undef => { .undef => try bw.print("; %undef {s}\n", .{name}),
try output.appendSlice("; %undef "); .defined => try bw.print("%define {s}\n", .{name}),
try output.appendSlice(name); .boolean => |b| try bw.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }),
try output.appendSlice("\n"); .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }),
}, .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }),
.defined => {
try output.appendSlice("%define ");
try output.appendSlice(name);
try output.appendSlice("\n");
},
.boolean => |b| {
try output.appendSlice("%define ");
try output.appendSlice(name);
try output.appendSlice(if (b) " 1\n" else " 0\n");
},
.int => |i| {
try output.writer().print("%define {s} {d}\n", .{ name, i });
},
.ident => |ident| {
try output.writer().print("%define {s} {s}\n", .{ name, ident });
},
.string => |string| {
// TODO: use nasm-specific escaping instead of zig string literals // TODO: use nasm-specific escaping instead of zig string literals
try output.writer().print("%define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) }); .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
},
} }
} }
fn expand_variables_autoconf_at( fn expand_variables_autoconf_at(
output: *std.ArrayList(u8), bw: *Writer,
contents: []const u8, contents: []const u8,
values: std.StringArrayHashMap(Value), values: std.StringArrayHashMap(Value),
used: []bool, used: []bool,
@ -637,23 +595,17 @@ fn expand_variables_autoconf_at(
const key = contents[curr + 1 .. close_pos]; const key = contents[curr + 1 .. close_pos];
const index = values.getIndex(key) orelse { const index = values.getIndex(key) orelse {
// Report the missing key to the caller. // Report the missing key to the caller.
try output.appendSlice(key); try bw.writeAll(key);
return error.MissingValue; return error.MissingValue;
}; };
const value = values.unmanaged.entries.slice().items(.value)[index]; const value = values.unmanaged.entries.slice().items(.value)[index];
used[index] = true; used[index] = true;
try output.appendSlice(contents[source_offset..curr]); try bw.writeAll(contents[source_offset..curr]);
switch (value) { switch (value) {
.undef, .defined => {}, .undef, .defined => {},
.boolean => |b| { .boolean => |b| try bw.writeByte(@as(u8, '0') + @intFromBool(b)),
try output.append(if (b) '1' else '0'); .int => |i| try bw.print("{d}", .{i}),
}, .ident, .string => |s| try bw.writeAll(s),
.int => |i| {
try output.writer().print("{d}", .{i});
},
.ident, .string => |s| {
try output.appendSlice(s);
},
} }
curr = close_pos; curr = close_pos;
@ -661,7 +613,7 @@ fn expand_variables_autoconf_at(
} }
} }
try output.appendSlice(contents[source_offset..]); try bw.writeAll(contents[source_offset..]);
} }
fn expand_variables_cmake( fn expand_variables_cmake(
@ -669,7 +621,7 @@ fn expand_variables_cmake(
contents: []const u8, contents: []const u8,
values: std.StringArrayHashMap(Value), values: std.StringArrayHashMap(Value),
) ![]const u8 { ) ![]const u8 {
var result = std.ArrayList(u8).init(allocator); var result: std.ArrayList(u8) = .init(allocator);
errdefer result.deinit(); errdefer result.deinit();
const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/_.+-"; const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/_.+-";
@ -681,7 +633,7 @@ fn expand_variables_cmake(
source: usize, source: usize,
target: usize, target: usize,
}; };
var var_stack = std.ArrayList(Position).init(allocator); var var_stack: std.ArrayList(Position) = .init(allocator);
defer var_stack.deinit(); defer var_stack.deinit();
loop: while (curr < contents.len) : (curr += 1) { loop: while (curr < contents.len) : (curr += 1) {
switch (contents[curr]) { switch (contents[curr]) {
@ -707,7 +659,7 @@ fn expand_variables_cmake(
try result.append(if (b) '1' else '0'); try result.append(if (b) '1' else '0');
}, },
.int => |i| { .int => |i| {
try result.writer().print("{d}", .{i}); try result.print("{d}", .{i});
}, },
.ident, .string => |s| { .ident, .string => |s| {
try result.appendSlice(s); try result.appendSlice(s);
@ -764,7 +716,7 @@ fn expand_variables_cmake(
try result.append(if (b) '1' else '0'); try result.append(if (b) '1' else '0');
}, },
.int => |i| { .int => |i| {
try result.writer().print("{d}", .{i}); try result.print("{d}", .{i});
}, },
.ident, .string => |s| { .ident, .string => |s| {
try result.appendSlice(s); try result.appendSlice(s);
@ -801,17 +753,17 @@ fn testReplaceVariablesAutoconfAt(
expected: []const u8, expected: []const u8,
values: std.StringArrayHashMap(Value), values: std.StringArrayHashMap(Value),
) !void { ) !void {
var output = std.ArrayList(u8).init(allocator); var output: std.io.Writer.Allocating = .init(allocator);
defer output.deinit(); defer output.deinit();
const used = try allocator.alloc(bool, values.count()); const used = try allocator.alloc(bool, values.count());
for (used) |*u| u.* = false; for (used) |*u| u.* = false;
defer allocator.free(used); defer allocator.free(used);
try expand_variables_autoconf_at(&output, contents, values, used); try expand_variables_autoconf_at(&output.interface, contents, values, used);
for (used) |u| if (!u) return error.UnusedValue; for (used) |u| if (!u) return error.UnusedValue;
try std.testing.expectEqualStrings(expected, output.items); try std.testing.expectEqualStrings(expected, output.getWritten());
} }
fn testReplaceVariablesCMake( fn testReplaceVariablesCMake(
@ -828,7 +780,7 @@ fn testReplaceVariablesCMake(
test "expand_variables_autoconf_at simple cases" { test "expand_variables_autoconf_at simple cases" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var values = std.StringArrayHashMap(Value).init(allocator); var values: std.StringArrayHashMap(Value) = .init(allocator);
defer values.deinit(); defer values.deinit();
// empty strings are preserved // empty strings are preserved
@ -924,7 +876,7 @@ test "expand_variables_autoconf_at simple cases" {
test "expand_variables_autoconf_at edge cases" { test "expand_variables_autoconf_at edge cases" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var values = std.StringArrayHashMap(Value).init(allocator); var values: std.StringArrayHashMap(Value) = .init(allocator);
defer values.deinit(); defer values.deinit();
// @-vars resolved only when they wrap valid characters, otherwise considered literals // @-vars resolved only when they wrap valid characters, otherwise considered literals
@ -940,7 +892,7 @@ test "expand_variables_autoconf_at edge cases" {
test "expand_variables_cmake simple cases" { test "expand_variables_cmake simple cases" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var values = std.StringArrayHashMap(Value).init(allocator); var values: std.StringArrayHashMap(Value) = .init(allocator);
defer values.deinit(); defer values.deinit();
try values.putNoClobber("undef", .undef); try values.putNoClobber("undef", .undef);
@ -1028,7 +980,7 @@ test "expand_variables_cmake simple cases" {
test "expand_variables_cmake edge cases" { test "expand_variables_cmake edge cases" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var values = std.StringArrayHashMap(Value).init(allocator); var values: std.StringArrayHashMap(Value) = .init(allocator);
defer values.deinit(); defer values.deinit();
// special symbols // special symbols
@ -1089,7 +1041,7 @@ test "expand_variables_cmake edge cases" {
test "expand_variables_cmake escaped characters" { test "expand_variables_cmake escaped characters" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var values = std.StringArrayHashMap(Value).init(allocator); var values: std.StringArrayHashMap(Value) = .init(allocator);
defer values.deinit(); defer values.deinit();
try values.putNoClobber("string", Value{ .string = "text" }); try values.putNoClobber("string", Value{ .string = "text" });

View File

@ -164,7 +164,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
const full_h_prefix = b.getInstallPath(h_dir, dir.dest_rel_path); const full_h_prefix = b.getInstallPath(h_dir, dir.dest_rel_path);
var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
return step.fail("unable to open source directory '{}': {s}", .{ return step.fail("unable to open source directory '{f}': {s}", .{
src_dir_path, @errorName(err), src_dir_path, @errorName(err),
}); });
}; };

View File

@ -65,7 +65,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
const src_dir_path = install_dir.options.source_dir.getPath3(b, step); const src_dir_path = install_dir.options.source_dir.getPath3(b, step);
const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir); const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir);
var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
return step.fail("unable to open source directory '{}': {s}", .{ return step.fail("unable to open source directory '{f}': {s}", .{
src_dir_path, @errorName(err), src_dir_path, @errorName(err),
}); });
}; };

View File

@ -12,23 +12,23 @@ pub const base_id: Step.Id = .options;
step: Step, step: Step,
generated_file: GeneratedFile, generated_file: GeneratedFile,
contents: std.ArrayList(u8), contents: std.ArrayListUnmanaged(u8),
args: std.ArrayList(Arg), args: std.ArrayListUnmanaged(Arg),
encountered_types: std.StringHashMap(void), encountered_types: std.StringHashMapUnmanaged(void),
pub fn create(owner: *std.Build) *Options { pub fn create(owner: *std.Build) *Options {
const options = owner.allocator.create(Options) catch @panic("OOM"); const options = owner.allocator.create(Options) catch @panic("OOM");
options.* = .{ options.* = .{
.step = Step.init(.{ .step = .init(.{
.id = base_id, .id = base_id,
.name = "options", .name = "options",
.owner = owner, .owner = owner,
.makeFn = make, .makeFn = make,
}), }),
.generated_file = undefined, .generated_file = undefined,
.contents = std.ArrayList(u8).init(owner.allocator), .contents = .empty,
.args = std.ArrayList(Arg).init(owner.allocator), .args = .empty,
.encountered_types = std.StringHashMap(void).init(owner.allocator), .encountered_types = .empty,
}; };
options.generated_file = .{ .step = &options.step }; options.generated_file = .{ .step = &options.step };
@ -40,110 +40,119 @@ pub fn addOption(options: *Options, comptime T: type, name: []const u8, value: T
} }
fn addOptionFallible(options: *Options, comptime T: type, name: []const u8, value: T) !void { fn addOptionFallible(options: *Options, comptime T: type, name: []const u8, value: T) !void {
const out = options.contents.writer(); try printType(options, &options.contents, T, value, 0, name);
try printType(options, out, T, value, 0, name);
} }
fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent: u8, name: ?[]const u8) !void { fn printType(
options: *Options,
out: *std.ArrayListUnmanaged(u8),
comptime T: type,
value: T,
indent: u8,
name: ?[]const u8,
) !void {
const gpa = options.step.owner.allocator;
switch (T) { switch (T) {
[]const []const u8 => { []const []const u8 => {
if (name) |payload| { if (name) |payload| {
try out.print("pub const {}: []const []const u8 = ", .{std.zig.fmtId(payload)}); try out.print(gpa, "pub const {f}: []const []const u8 = ", .{std.zig.fmtId(payload)});
} }
try out.writeAll("&[_][]const u8{\n"); try out.appendSlice(gpa, "&[_][]const u8{\n");
for (value) |slice| { for (value) |slice| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}); try out.print(gpa, " \"{f}\",\n", .{std.zig.fmtString(slice)});
} }
if (name != null) { if (name != null) {
try out.writeAll("};\n"); try out.appendSlice(gpa, "};\n");
} else { } else {
try out.writeAll("},\n"); try out.appendSlice(gpa, "},\n");
} }
return; return;
}, },
[]const u8 => { []const u8 => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: []const u8 = \"{}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) }); try out.print(gpa, "pub const {f}: []const u8 = \"{f}\";", .{
std.zig.fmtId(some), std.zig.fmtString(value),
});
} else { } else {
try out.print("\"{}\",", .{std.zig.fmtEscapes(value)}); try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)});
} }
return out.writeAll("\n"); return out.appendSlice(gpa, "\n");
}, },
[:0]const u8 => { [:0]const u8 => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: [:0]const u8 = \"{}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) }); try out.print(gpa, "pub const {f}: [:0]const u8 = \"{f}\";", .{ std.zig.fmtId(some), std.zig.fmtString(value) });
} else { } else {
try out.print("\"{}\",", .{std.zig.fmtEscapes(value)}); try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)});
} }
return out.writeAll("\n"); return out.appendSlice(gpa, "\n");
}, },
?[]const u8 => { ?[]const u8 => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(some)}); try out.print(gpa, "pub const {f}: ?[]const u8 = ", .{std.zig.fmtId(some)});
} }
if (value) |payload| { if (value) |payload| {
try out.print("\"{}\"", .{std.zig.fmtEscapes(payload)}); try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)});
} else { } else {
try out.writeAll("null"); try out.appendSlice(gpa, "null");
} }
if (name != null) { if (name != null) {
try out.writeAll(";\n"); try out.appendSlice(gpa, ";\n");
} else { } else {
try out.writeAll(",\n"); try out.appendSlice(gpa, ",\n");
} }
return; return;
}, },
?[:0]const u8 => { ?[:0]const u8 => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(some)}); try out.print(gpa, "pub const {f}: ?[:0]const u8 = ", .{std.zig.fmtId(some)});
} }
if (value) |payload| { if (value) |payload| {
try out.print("\"{}\"", .{std.zig.fmtEscapes(payload)}); try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)});
} else { } else {
try out.writeAll("null"); try out.appendSlice(gpa, "null");
} }
if (name != null) { if (name != null) {
try out.writeAll(";\n"); try out.appendSlice(gpa, ";\n");
} else { } else {
try out.writeAll(",\n"); try out.appendSlice(gpa, ",\n");
} }
return; return;
}, },
std.SemanticVersion => { std.SemanticVersion => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: @import(\"std\").SemanticVersion = ", .{std.zig.fmtId(some)}); try out.print(gpa, "pub const {f}: @import(\"std\").SemanticVersion = ", .{std.zig.fmtId(some)});
} }
try out.writeAll(".{\n"); try out.appendSlice(gpa, ".{\n");
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" .major = {d},\n", .{value.major}); try out.print(gpa, " .major = {d},\n", .{value.major});
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" .minor = {d},\n", .{value.minor}); try out.print(gpa, " .minor = {d},\n", .{value.minor});
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" .patch = {d},\n", .{value.patch}); try out.print(gpa, " .patch = {d},\n", .{value.patch});
if (value.pre) |some| { if (value.pre) |some| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}); try out.print(gpa, " .pre = \"{f}\",\n", .{std.zig.fmtString(some)});
} }
if (value.build) |some| { if (value.build) |some| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}); try out.print(gpa, " .build = \"{f}\",\n", .{std.zig.fmtString(some)});
} }
if (name != null) { if (name != null) {
try out.writeAll("};\n"); try out.appendSlice(gpa, "};\n");
} else { } else {
try out.writeAll("},\n"); try out.appendSlice(gpa, "},\n");
} }
return; return;
}, },
@ -153,21 +162,21 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.array => { .array => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
} }
try out.print("{s} {{\n", .{@typeName(T)}); try out.print(gpa, "{s} {{\n", .{@typeName(T)});
for (value) |item| { for (value) |item| {
try out.writeByteNTimes(' ', indent + 4); try out.appendNTimes(gpa, ' ', indent + 4);
try printType(options, out, @TypeOf(item), item, indent + 4, null); try printType(options, out, @TypeOf(item), item, indent + 4, null);
} }
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.writeAll("}"); try out.appendSlice(gpa, "}");
if (name != null) { if (name != null) {
try out.writeAll(";\n"); try out.appendSlice(gpa, ";\n");
} else { } else {
try out.writeAll(",\n"); try out.appendSlice(gpa, ",\n");
} }
return; return;
}, },
@ -177,27 +186,27 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
} }
if (name) |some| { if (name) |some| {
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
} }
try out.print("&[_]{s} {{\n", .{@typeName(p.child)}); try out.print(gpa, "&[_]{s} {{\n", .{@typeName(p.child)});
for (value) |item| { for (value) |item| {
try out.writeByteNTimes(' ', indent + 4); try out.appendNTimes(gpa, ' ', indent + 4);
try printType(options, out, @TypeOf(item), item, indent + 4, null); try printType(options, out, @TypeOf(item), item, indent + 4, null);
} }
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.writeAll("}"); try out.appendSlice(gpa, "}");
if (name != null) { if (name != null) {
try out.writeAll(";\n"); try out.appendSlice(gpa, ";\n");
} else { } else {
try out.writeAll(",\n"); try out.appendSlice(gpa, ",\n");
} }
return; return;
}, },
.optional => { .optional => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
} }
if (value) |inner| { if (value) |inner| {
@ -206,13 +215,13 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
_ = options.contents.pop(); _ = options.contents.pop();
_ = options.contents.pop(); _ = options.contents.pop();
} else { } else {
try out.writeAll("null"); try out.appendSlice(gpa, "null");
} }
if (name != null) { if (name != null) {
try out.writeAll(";\n"); try out.appendSlice(gpa, ";\n");
} else { } else {
try out.writeAll(",\n"); try out.appendSlice(gpa, ",\n");
} }
return; return;
}, },
@ -224,9 +233,9 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
.null, .null,
=> { => {
if (name) |some| { if (name) |some| {
try out.print("pub const {}: {s} = {any};\n", .{ std.zig.fmtId(some), @typeName(T), value }); try out.print(gpa, "pub const {f}: {s} = {any};\n", .{ std.zig.fmtId(some), @typeName(T), value });
} else { } else {
try out.print("{any},\n", .{value}); try out.print(gpa, "{any},\n", .{value});
} }
return; return;
}, },
@ -234,10 +243,10 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
try printEnum(options, out, T, info, indent); try printEnum(options, out, T, info, indent);
if (name) |some| { if (name) |some| {
try out.print("pub const {}: {} = .{p_};\n", .{ try out.print(gpa, "pub const {f}: {f} = .{f};\n", .{
std.zig.fmtId(some), std.zig.fmtId(some),
std.zig.fmtId(@typeName(T)), std.zig.fmtId(@typeName(T)),
std.zig.fmtId(@tagName(value)), std.zig.fmtIdFlags(@tagName(value), .{ .allow_underscore = true, .allow_primitive = true }),
}); });
} }
return; return;
@ -246,7 +255,7 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
try printStruct(options, out, T, info, indent); try printStruct(options, out, T, info, indent);
if (name) |some| { if (name) |some| {
try out.print("pub const {}: {} = ", .{ try out.print(gpa, "pub const {f}: {f} = ", .{
std.zig.fmtId(some), std.zig.fmtId(some),
std.zig.fmtId(@typeName(T)), std.zig.fmtId(@typeName(T)),
}); });
@ -258,7 +267,7 @@ fn printType(options: *Options, out: anytype, comptime T: type, value: T, indent
} }
} }
fn printUserDefinedType(options: *Options, out: anytype, comptime T: type, indent: u8) !void { fn printUserDefinedType(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T: type, indent: u8) !void {
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.@"enum" => |info| { .@"enum" => |info| {
return try printEnum(options, out, T, info, indent); return try printEnum(options, out, T, info, indent);
@ -270,94 +279,119 @@ fn printUserDefinedType(options: *Options, out: anytype, comptime T: type, inden
} }
} }
fn printEnum(options: *Options, out: anytype, comptime T: type, comptime val: std.builtin.Type.Enum, indent: u8) !void { fn printEnum(
const gop = try options.encountered_types.getOrPut(@typeName(T)); options: *Options,
out: *std.ArrayListUnmanaged(u8),
comptime T: type,
comptime val: std.builtin.Type.Enum,
indent: u8,
) !void {
const gpa = options.step.owner.allocator;
const gop = try options.encountered_types.getOrPut(gpa, @typeName(T));
if (gop.found_existing) return; if (gop.found_existing) return;
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print("pub const {} = enum ({s}) {{\n", .{ std.zig.fmtId(@typeName(T)), @typeName(val.tag_type) }); try out.print(gpa, "pub const {f} = enum ({s}) {{\n", .{ std.zig.fmtId(@typeName(T)), @typeName(val.tag_type) });
inline for (val.fields) |field| { inline for (val.fields) |field| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" {p} = {d},\n", .{ std.zig.fmtId(field.name), field.value }); try out.print(gpa, " {f} = {d},\n", .{
std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true }), field.value,
});
} }
if (!val.is_exhaustive) { if (!val.is_exhaustive) {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.writeAll(" _,\n"); try out.appendSlice(gpa, " _,\n");
} }
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.writeAll("};\n"); try out.appendSlice(gpa, "};\n");
} }
fn printStruct(options: *Options, out: anytype, comptime T: type, comptime val: std.builtin.Type.Struct, indent: u8) !void { fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T: type, comptime val: std.builtin.Type.Struct, indent: u8) !void {
const gop = try options.encountered_types.getOrPut(@typeName(T)); const gpa = options.step.owner.allocator;
const gop = try options.encountered_types.getOrPut(gpa, @typeName(T));
if (gop.found_existing) return; if (gop.found_existing) return;
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print("pub const {} = ", .{std.zig.fmtId(@typeName(T))}); try out.print(gpa, "pub const {f} = ", .{std.zig.fmtId(@typeName(T))});
switch (val.layout) { switch (val.layout) {
.@"extern" => try out.writeAll("extern struct"), .@"extern" => try out.appendSlice(gpa, "extern struct"),
.@"packed" => try out.writeAll("packed struct"), .@"packed" => try out.appendSlice(gpa, "packed struct"),
else => try out.writeAll("struct"), else => try out.appendSlice(gpa, "struct"),
} }
try out.writeAll(" {\n"); try out.appendSlice(gpa, " {\n");
inline for (val.fields) |field| { inline for (val.fields) |field| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
const type_name = @typeName(field.type); const type_name = @typeName(field.type);
// If the type name doesn't contains a '.' the type is from zig builtins. // If the type name doesn't contains a '.' the type is from zig builtins.
if (std.mem.containsAtLeast(u8, type_name, 1, ".")) { if (std.mem.containsAtLeast(u8, type_name, 1, ".")) {
try out.print(" {p_}: {}", .{ std.zig.fmtId(field.name), std.zig.fmtId(type_name) }); try out.print(gpa, " {f}: {f}", .{
std.zig.fmtIdFlags(field.name, .{ .allow_underscore = true, .allow_primitive = true }),
std.zig.fmtId(type_name),
});
} else { } else {
try out.print(" {p_}: {s}", .{ std.zig.fmtId(field.name), type_name }); try out.print(gpa, " {f}: {s}", .{
std.zig.fmtIdFlags(field.name, .{ .allow_underscore = true, .allow_primitive = true }),
type_name,
});
} }
if (field.defaultValue()) |default_value| { if (field.defaultValue()) |default_value| {
try out.writeAll(" = "); try out.appendSlice(gpa, " = ");
switch (@typeInfo(@TypeOf(default_value))) { switch (@typeInfo(@TypeOf(default_value))) {
.@"enum" => try out.print(".{s},\n", .{@tagName(default_value)}), .@"enum" => try out.print(gpa, ".{s},\n", .{@tagName(default_value)}),
.@"struct" => |info| { .@"struct" => |info| {
try printStructValue(options, out, info, default_value, indent + 4); try printStructValue(options, out, info, default_value, indent + 4);
}, },
else => try printType(options, out, @TypeOf(default_value), default_value, indent, null), else => try printType(options, out, @TypeOf(default_value), default_value, indent, null),
} }
} else { } else {
try out.writeAll(",\n"); try out.appendSlice(gpa, ",\n");
} }
} }
// TODO: write declarations // TODO: write declarations
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.writeAll("};\n"); try out.appendSlice(gpa, "};\n");
inline for (val.fields) |field| { inline for (val.fields) |field| {
try printUserDefinedType(options, out, field.type, 0); try printUserDefinedType(options, out, field.type, 0);
} }
} }
fn printStructValue(options: *Options, out: anytype, comptime struct_val: std.builtin.Type.Struct, val: anytype, indent: u8) !void { fn printStructValue(
try out.writeAll(".{\n"); options: *Options,
out: *std.ArrayListUnmanaged(u8),
comptime struct_val: std.builtin.Type.Struct,
val: anytype,
indent: u8,
) !void {
const gpa = options.step.owner.allocator;
try out.appendSlice(gpa, ".{\n");
if (struct_val.is_tuple) { if (struct_val.is_tuple) {
inline for (struct_val.fields) |field| { inline for (struct_val.fields) |field| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try printType(options, out, @TypeOf(@field(val, field.name)), @field(val, field.name), indent, null); try printType(options, out, @TypeOf(@field(val, field.name)), @field(val, field.name), indent, null);
} }
} else { } else {
inline for (struct_val.fields) |field| { inline for (struct_val.fields) |field| {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.print(" .{p_} = ", .{std.zig.fmtId(field.name)}); try out.print(gpa, " .{f} = ", .{
std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true, .allow_underscore = true }),
});
const field_name = @field(val, field.name); const field_name = @field(val, field.name);
switch (@typeInfo(@TypeOf(field_name))) { switch (@typeInfo(@TypeOf(field_name))) {
.@"enum" => try out.print(".{s},\n", .{@tagName(field_name)}), .@"enum" => try out.print(gpa, ".{s},\n", .{@tagName(field_name)}),
.@"struct" => |struct_info| { .@"struct" => |struct_info| {
try printStructValue(options, out, struct_info, field_name, indent + 4); try printStructValue(options, out, struct_info, field_name, indent + 4);
}, },
@ -367,10 +401,10 @@ fn printStructValue(options: *Options, out: anytype, comptime struct_val: std.bu
} }
if (indent == 0) { if (indent == 0) {
try out.writeAll("};\n"); try out.appendSlice(gpa, "};\n");
} else { } else {
try out.writeByteNTimes(' ', indent); try out.appendNTimes(gpa, ' ', indent);
try out.writeAll("},\n"); try out.appendSlice(gpa, "},\n");
} }
} }
@ -440,7 +474,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
error.FileNotFound => { error.FileNotFound => {
const sub_dirname = fs.path.dirname(sub_path).?; const sub_dirname = fs.path.dirname(sub_path).?;
b.cache_root.handle.makePath(sub_dirname) catch |e| { b.cache_root.handle.makePath(sub_dirname) catch |e| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, sub_dirname, @errorName(e), b.cache_root, sub_dirname, @errorName(e),
}); });
}; };
@ -452,13 +486,13 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
const tmp_sub_path_dirname = fs.path.dirname(tmp_sub_path).?; const tmp_sub_path_dirname = fs.path.dirname(tmp_sub_path).?;
b.cache_root.handle.makePath(tmp_sub_path_dirname) catch |err| { b.cache_root.handle.makePath(tmp_sub_path_dirname) catch |err| {
return step.fail("unable to make temporary directory '{}{s}': {s}", .{ return step.fail("unable to make temporary directory '{f}{s}': {s}", .{
b.cache_root, tmp_sub_path_dirname, @errorName(err), b.cache_root, tmp_sub_path_dirname, @errorName(err),
}); });
}; };
b.cache_root.handle.writeFile(.{ .sub_path = tmp_sub_path, .data = options.contents.items }) catch |err| { b.cache_root.handle.writeFile(.{ .sub_path = tmp_sub_path, .data = options.contents.items }) catch |err| {
return step.fail("unable to write options to '{}{s}': {s}", .{ return step.fail("unable to write options to '{f}{s}': {s}", .{
b.cache_root, tmp_sub_path, @errorName(err), b.cache_root, tmp_sub_path, @errorName(err),
}); });
}; };
@ -467,7 +501,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
error.PathAlreadyExists => { error.PathAlreadyExists => {
// Other process beat us to it. Clean up the temp file. // Other process beat us to it. Clean up the temp file.
b.cache_root.handle.deleteFile(tmp_sub_path) catch |e| { b.cache_root.handle.deleteFile(tmp_sub_path) catch |e| {
try step.addError("warning: unable to delete temp file '{}{s}': {s}", .{ try step.addError("warning: unable to delete temp file '{f}{s}': {s}", .{
b.cache_root, tmp_sub_path, @errorName(e), b.cache_root, tmp_sub_path, @errorName(e),
}); });
}; };
@ -475,7 +509,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
return; return;
}, },
else => { else => {
return step.fail("unable to rename options from '{}{s}' to '{}{s}': {s}", .{ return step.fail("unable to rename options from '{f}{s}' to '{f}{s}': {s}", .{
b.cache_root, tmp_sub_path, b.cache_root, tmp_sub_path,
b.cache_root, sub_path, b.cache_root, sub_path,
@errorName(err), @errorName(err),
@ -483,7 +517,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
}, },
}; };
}, },
else => |e| return step.fail("unable to access options file '{}{s}': {s}", .{ else => |e| return step.fail("unable to access options file '{f}{s}': {s}", .{
b.cache_root, sub_path, @errorName(e), b.cache_root, sub_path, @errorName(e),
}), }),
} }
@ -643,5 +677,5 @@ test Options {
\\ \\
, options.contents.items); , options.contents.items);
_ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0), .zig); _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(arena.allocator(), 0), .zig);
} }

View File

@ -832,7 +832,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
else => unreachable, else => unreachable,
}; };
b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, output_sub_dir_path, @errorName(err), b.cache_root, output_sub_dir_path, @errorName(err),
}); });
}; };
@ -864,7 +864,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
else => unreachable, else => unreachable,
}; };
b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, output_sub_dir_path, @errorName(err), b.cache_root, output_sub_dir_path, @errorName(err),
}); });
}; };
@ -903,21 +903,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |err| { b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |err| {
if (err == error.PathAlreadyExists) { if (err == error.PathAlreadyExists) {
b.cache_root.handle.deleteTree(o_sub_path) catch |del_err| { b.cache_root.handle.deleteTree(o_sub_path) catch |del_err| {
return step.fail("unable to remove dir '{}'{s}: {s}", .{ return step.fail("unable to remove dir '{f}'{s}: {s}", .{
b.cache_root, b.cache_root,
tmp_dir_path, tmp_dir_path,
@errorName(del_err), @errorName(del_err),
}); });
}; };
b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |retry_err| { b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |retry_err| {
return step.fail("unable to rename dir '{}{s}' to '{}{s}': {s}", .{ return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {s}", .{
b.cache_root, tmp_dir_path, b.cache_root, tmp_dir_path,
b.cache_root, o_sub_path, b.cache_root, o_sub_path,
@errorName(retry_err), @errorName(retry_err),
}); });
}; };
} else { } else {
return step.fail("unable to rename dir '{}{s}' to '{}{s}': {s}", .{ return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {s}", .{
b.cache_root, tmp_dir_path, b.cache_root, tmp_dir_path,
b.cache_root, o_sub_path, b.cache_root, o_sub_path,
@errorName(err), @errorName(err),
@ -964,7 +964,7 @@ pub fn rerunInFuzzMode(
.artifact => |pa| { .artifact => |pa| {
const artifact = pa.artifact; const artifact = pa.artifact;
const file_path: []const u8 = p: { const file_path: []const u8 = p: {
if (artifact == run.producer.?) break :p b.fmt("{}", .{run.rebuilt_executable.?}); if (artifact == run.producer.?) break :p b.fmt("{f}", .{run.rebuilt_executable.?});
break :p artifact.installed_path orelse artifact.generated_bin.?.path.?; break :p artifact.installed_path orelse artifact.generated_bin.?.path.?;
}; };
try argv_list.append(arena, b.fmt("{s}{s}", .{ try argv_list.append(arena, b.fmt("{s}{s}", .{
@ -1011,24 +1011,17 @@ fn populateGeneratedPaths(
} }
} }
fn formatTerm( fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer) std.io.Writer.Error!void {
term: ?std.process.Child.Term,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;
if (term) |t| switch (t) { if (term) |t| switch (t) {
.Exited => |code| try writer.print("exited with code {}", .{code}), .Exited => |code| try w.print("exited with code {d}", .{code}),
.Signal => |sig| try writer.print("terminated with signal {}", .{sig}), .Signal => |sig| try w.print("terminated with signal {d}", .{sig}),
.Stopped => |sig| try writer.print("stopped with signal {}", .{sig}), .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
.Unknown => |code| try writer.print("terminated for unknown reason with code {}", .{code}), .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
} else { } else {
try writer.writeAll("exited with any code"); try w.writeAll("exited with any code");
} }
} }
fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(?std.process.Child.Term, formatTerm) {
return .{ .data = term }; return .{ .data = term };
} }
@ -1262,12 +1255,12 @@ fn runCommand(
const sub_path = b.pathJoin(&output_components); const sub_path = b.pathJoin(&output_components);
const sub_path_dirname = fs.path.dirname(sub_path).?; const sub_path_dirname = fs.path.dirname(sub_path).?;
b.cache_root.handle.makePath(sub_path_dirname) catch |err| { b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, sub_path_dirname, @errorName(err), b.cache_root, sub_path_dirname, @errorName(err),
}); });
}; };
b.cache_root.handle.writeFile(.{ .sub_path = sub_path, .data = stream.bytes.? }) catch |err| { b.cache_root.handle.writeFile(.{ .sub_path = sub_path, .data = stream.bytes.? }) catch |err| {
return step.fail("unable to write file '{}{s}': {s}", .{ return step.fail("unable to write file '{f}{s}': {s}", .{
b.cache_root, sub_path, @errorName(err), b.cache_root, sub_path, @errorName(err),
}); });
}; };
@ -1346,7 +1339,7 @@ fn runCommand(
}, },
.expect_term => |expected_term| { .expect_term => |expected_term| {
if (!termMatches(expected_term, result.term)) { if (!termMatches(expected_term, result.term)) {
return step.fail("the following command {} (expected {}):\n{s}", .{ return step.fail("the following command {f} (expected {f}):\n{s}", .{
fmtTerm(result.term), fmtTerm(result.term),
fmtTerm(expected_term), fmtTerm(expected_term),
try Step.allocPrintCmd(arena, cwd, final_argv), try Step.allocPrintCmd(arena, cwd, final_argv),
@ -1366,7 +1359,7 @@ fn runCommand(
}; };
const expected_term: std.process.Child.Term = .{ .Exited = 0 }; const expected_term: std.process.Child.Term = .{ .Exited = 0 };
if (!termMatches(expected_term, result.term)) { if (!termMatches(expected_term, result.term)) {
return step.fail("{s}the following command {} (expected {}):\n{s}", .{ return step.fail("{s}the following command {f} (expected {f}):\n{s}", .{
prefix, prefix,
fmtTerm(result.term), fmtTerm(result.term),
fmtTerm(expected_term), fmtTerm(expected_term),
@ -1797,10 +1790,10 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); stdout_bytes = try poller.fifo(.stdout).toOwnedSlice();
stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); stderr_bytes = try poller.fifo(.stderr).toOwnedSlice();
} else { } else {
stdout_bytes = try stdout.reader().readAllAlloc(arena, run.max_stdio_size); stdout_bytes = try stdout.deprecatedReader().readAllAlloc(arena, run.max_stdio_size);
} }
} else if (child.stderr) |stderr| { } else if (child.stderr) |stderr| {
stderr_bytes = try stderr.reader().readAllAlloc(arena, run.max_stdio_size); stderr_bytes = try stderr.deprecatedReader().readAllAlloc(arena, run.max_stdio_size);
} }
if (stderr_bytes) |bytes| if (bytes.len > 0) { if (stderr_bytes) |bytes| if (bytes.len > 0) {

View File

@ -76,7 +76,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
for (usf.output_source_files.items) |output_source_file| { for (usf.output_source_files.items) |output_source_file| {
if (fs.path.dirname(output_source_file.sub_path)) |dirname| { if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
b.build_root.handle.makePath(dirname) catch |err| { b.build_root.handle.makePath(dirname) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.build_root, dirname, @errorName(err), b.build_root, dirname, @errorName(err),
}); });
}; };
@ -84,7 +84,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
switch (output_source_file.contents) { switch (output_source_file.contents) {
.bytes => |bytes| { .bytes => |bytes| {
b.build_root.handle.writeFile(.{ .sub_path = output_source_file.sub_path, .data = bytes }) catch |err| { b.build_root.handle.writeFile(.{ .sub_path = output_source_file.sub_path, .data = bytes }) catch |err| {
return step.fail("unable to write file '{}{s}': {s}", .{ return step.fail("unable to write file '{f}{s}': {s}", .{
b.build_root, output_source_file.sub_path, @errorName(err), b.build_root, output_source_file.sub_path, @errorName(err),
}); });
}; };
@ -101,7 +101,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
output_source_file.sub_path, output_source_file.sub_path,
.{}, .{},
) catch |err| { ) catch |err| {
return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{ return step.fail("unable to update file from '{s}' to '{f}{s}': {s}", .{
source_path, b.build_root, output_source_file.sub_path, @errorName(err), source_path, b.build_root, output_source_file.sub_path, @errorName(err),
}); });
}; };

View File

@ -217,7 +217,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
const src_dir_path = dir.source.getPath3(b, step); const src_dir_path = dir.source.getPath3(b, step);
var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
return step.fail("unable to open source directory '{}': {s}", .{ return step.fail("unable to open source directory '{f}': {s}", .{
src_dir_path, @errorName(err), src_dir_path, @errorName(err),
}); });
}; };
@ -258,7 +258,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest }); write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, cache_path, @errorName(err), b.cache_root, cache_path, @errorName(err),
}); });
}; };
@ -269,7 +269,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
for (write_file.files.items) |file| { for (write_file.files.items) |file| {
if (fs.path.dirname(file.sub_path)) |dirname| { if (fs.path.dirname(file.sub_path)) |dirname| {
cache_dir.makePath(dirname) catch |err| { cache_dir.makePath(dirname) catch |err| {
return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{
b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err), b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err),
}); });
}; };
@ -277,7 +277,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
switch (file.contents) { switch (file.contents) {
.bytes => |bytes| { .bytes => |bytes| {
cache_dir.writeFile(.{ .sub_path = file.sub_path, .data = bytes }) catch |err| { cache_dir.writeFile(.{ .sub_path = file.sub_path, .data = bytes }) catch |err| {
return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{ return step.fail("unable to write file '{f}{s}{c}{s}': {s}", .{
b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err), b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err),
}); });
}; };
@ -291,7 +291,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
file.sub_path, file.sub_path,
.{}, .{},
) catch |err| { ) catch |err| {
return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {s}", .{
source_path, source_path,
b.cache_root, b.cache_root,
cache_path, cache_path,
@ -315,7 +315,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
if (dest_dirname.len != 0) { if (dest_dirname.len != 0) {
cache_dir.makePath(dest_dirname) catch |err| { cache_dir.makePath(dest_dirname) catch |err| {
return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{
b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err), b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
}); });
}; };
@ -338,7 +338,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
dest_path, dest_path,
.{}, .{},
) catch |err| { ) catch |err| {
return step.fail("unable to update file from '{}' to '{}{s}{c}{s}': {s}", .{ return step.fail("unable to update file from '{f}' to '{f}{s}{c}{s}': {s}", .{
src_entry_path, b.cache_root, cache_path, fs.path.sep, dest_path, @errorName(err), src_entry_path, b.cache_root, cache_path, fs.path.sep, dest_path, @errorName(err),
}); });
}; };

View File

@ -211,7 +211,7 @@ const Os = switch (builtin.os.tag) {
.ADD = true, .ADD = true,
.ONLYDIR = true, .ONLYDIR = true,
}, fan_mask, path.root_dir.handle.fd, path.subPathOrDot()) catch |err| { }, fan_mask, path.root_dir.handle.fd, path.subPathOrDot()) catch |err| {
fatal("unable to watch {}: {s}", .{ path, @errorName(err) }); fatal("unable to watch {f}: {s}", .{ path, @errorName(err) });
}; };
} }
break :rs &dh_gop.value_ptr.reaction_set; break :rs &dh_gop.value_ptr.reaction_set;
@ -265,7 +265,7 @@ const Os = switch (builtin.os.tag) {
.ONLYDIR = true, .ONLYDIR = true,
}, fan_mask, path.root_dir.handle.fd, path.subPathOrDot()) catch |err| switch (err) { }, fan_mask, path.root_dir.handle.fd, path.subPathOrDot()) catch |err| switch (err) {
error.FileNotFound => {}, // Expected, harmless. error.FileNotFound => {}, // Expected, harmless.
else => |e| std.log.warn("unable to unwatch '{}': {s}", .{ path, @errorName(e) }), else => |e| std.log.warn("unable to unwatch '{f}': {s}", .{ path, @errorName(e) }),
}; };
w.dir_table.swapRemoveAt(i); w.dir_table.swapRemoveAt(i);
@ -659,7 +659,7 @@ const Os = switch (builtin.os.tag) {
path.root_dir.handle.fd path.root_dir.handle.fd
else else
posix.openat(path.root_dir.handle.fd, path.sub_path, dir_open_flags, 0) catch |err| { posix.openat(path.root_dir.handle.fd, path.sub_path, dir_open_flags, 0) catch |err| {
fatal("failed to open directory {}: {s}", .{ path, @errorName(err) }); fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) });
}; };
// Empirically the dir has to stay open or else no events are triggered. // Empirically the dir has to stay open or else no events are triggered.
errdefer if (!skip_open_dir) posix.close(dir_fd); errdefer if (!skip_open_dir) posix.close(dir_fd);

View File

@ -9,6 +9,7 @@ const Progress = @This();
const posix = std.posix; const posix = std.posix;
const is_big_endian = builtin.cpu.arch.endian() == .big; const is_big_endian = builtin.cpu.arch.endian() == .big;
const is_windows = builtin.os.tag == .windows; const is_windows = builtin.os.tag == .windows;
const Writer = std.io.Writer;
/// `null` if the current node (and its children) should /// `null` if the current node (and its children) should
/// not print on update() /// not print on update()
@ -606,6 +607,36 @@ pub fn unlockStdErr() void {
stderr_mutex.unlock(); stderr_mutex.unlock();
} }
/// Protected by `stderr_mutex`.
const stderr_writer: *Writer = &stderr_file_writer.interface;
/// Protected by `stderr_mutex`.
var stderr_file_writer: std.fs.File.Writer = .{
.interface = std.fs.File.Writer.initInterface(&.{}),
.file = if (is_windows) undefined else .stderr(),
.mode = .streaming,
};
/// Allows the caller to freely write to the returned `Writer`,
/// initialized with `buffer`, until `unlockStderrWriter` is called.
///
/// During the lock, any `std.Progress` information is cleared from the terminal.
///
/// The lock is recursive; the same thread may hold the lock multiple times.
pub fn lockStderrWriter(buffer: []u8) *Writer {
stderr_mutex.lock();
clearWrittenWithEscapeCodes() catch {};
if (is_windows) stderr_file_writer.file = .stderr();
stderr_writer.flush() catch {};
stderr_writer.buffer = buffer;
return stderr_writer;
}
pub fn unlockStderrWriter() void {
stderr_writer.flush() catch {};
stderr_writer.buffer = &.{};
stderr_mutex.unlock();
}
fn ipcThreadRun(fd: posix.fd_t) anyerror!void { fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
// Store this data in the thread so that it does not need to be part of the // Store this data in the thread so that it does not need to be part of the
// linker data of the main executable. // linker data of the main executable.

View File

@ -122,7 +122,7 @@ fn mode(comptime x: comptime_int) comptime_int {
} }
pub fn main() !void { pub fn main() !void {
const stdout = std.fs.File.stdout().writer(); const stdout = std.fs.File.stdout().deprecatedWriter();
var buffer: [1024]u8 = undefined; var buffer: [1024]u8 = undefined;
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);

View File

@ -150,17 +150,11 @@ fn parseNum(text: []const u8) error{ InvalidVersion, Overflow }!usize {
}; };
} }
pub fn format( pub fn format(self: Version, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
self: Version,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
_ = options;
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
try std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); try w.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch });
if (self.pre) |pre| try std.fmt.format(out_stream, "-{s}", .{pre}); if (self.pre) |pre| try w.print("-{s}", .{pre});
if (self.build) |build| try std.fmt.format(out_stream, "+{s}", .{build}); if (self.build) |build| try w.print("+{s}", .{build});
} }
const expect = std.testing.expect; const expect = std.testing.expect;
@ -202,7 +196,7 @@ test format {
"1.0.0+0.build.1-rc.10000aaa-kk-0.1", "1.0.0+0.build.1-rc.10000aaa-kk-0.1",
"5.4.0-1018-raspi", "5.4.0-1018-raspi",
"5.7.123", "5.7.123",
}) |valid| try std.testing.expectFmt(valid, "{}", .{try parse(valid)}); }) |valid| try std.testing.expectFmt(valid, "{f}", .{try parse(valid)});
// Invalid version strings should be rejected. // Invalid version strings should be rejected.
for ([_][]const u8{ for ([_][]const u8{
@ -269,12 +263,12 @@ test format {
// Valid version string that may overflow. // Valid version string that may overflow.
const big_valid = "99999999999999999999999.999999999999999999.99999999999999999"; const big_valid = "99999999999999999999999.999999999999999999.99999999999999999";
if (parse(big_valid)) |ver| { if (parse(big_valid)) |ver| {
try std.testing.expectFmt(big_valid, "{}", .{ver}); try std.testing.expectFmt(big_valid, "{f}", .{ver});
} else |err| try expect(err == error.Overflow); } else |err| try expect(err == error.Overflow);
// Invalid version string that may overflow. // Invalid version string that may overflow.
const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12"; const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12";
if (parse(big_invalid)) |ver| std.debug.panic("expected error, found {}", .{ver}) else |_| {} if (parse(big_invalid)) |ver| std.debug.panic("expected error, found {f}", .{ver}) else |_| {}
} }
test "precedence" { test "precedence" {

View File

@ -301,29 +301,24 @@ pub const Os = struct {
/// This function is defined to serialize a Zig source code representation of this /// This function is defined to serialize a Zig source code representation of this
/// type, that, when parsed, will deserialize into the same data. /// type, that, when parsed, will deserialize into the same data.
pub fn format( pub fn format(ver: WindowsVersion, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
ver: WindowsVersion,
comptime fmt_str: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
const maybe_name = std.enums.tagName(WindowsVersion, ver); const maybe_name = std.enums.tagName(WindowsVersion, ver);
if (comptime std.mem.eql(u8, fmt_str, "s")) { if (comptime std.mem.eql(u8, f, "s")) {
if (maybe_name) |name| if (maybe_name) |name|
try writer.print(".{s}", .{name}) try w.print(".{s}", .{name})
else else
try writer.print(".{d}", .{@intFromEnum(ver)}); try w.print(".{d}", .{@intFromEnum(ver)});
} else if (comptime std.mem.eql(u8, fmt_str, "c")) { } else if (comptime std.mem.eql(u8, f, "c")) {
if (maybe_name) |name| if (maybe_name) |name|
try writer.print(".{s}", .{name}) try w.print(".{s}", .{name})
else else
try writer.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)}); try w.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)});
} else if (fmt_str.len == 0) { } else if (f.len == 0) {
if (maybe_name) |name| if (maybe_name) |name|
try writer.print("WindowsVersion.{s}", .{name}) try w.print("WindowsVersion.{s}", .{name})
else else
try writer.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)}); try w.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)});
} else std.fmt.invalidFmtError(fmt_str, ver); } else std.fmt.invalidFmtError(f, ver);
} }
}; };

View File

@ -394,25 +394,24 @@ pub fn canDetectLibC(self: Query) bool {
/// Formats a version with the patch component omitted if it is zero, /// Formats a version with the patch component omitted if it is zero,
/// unlike SemanticVersion.format which formats all its version components regardless. /// unlike SemanticVersion.format which formats all its version components regardless.
fn formatVersion(version: SemanticVersion, writer: anytype) !void { fn formatVersion(version: SemanticVersion, gpa: Allocator, list: *std.ArrayListUnmanaged(u8)) !void {
if (version.patch == 0) { if (version.patch == 0) {
try writer.print("{d}.{d}", .{ version.major, version.minor }); try list.print(gpa, "{d}.{d}", .{ version.major, version.minor });
} else { } else {
try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch }); try list.print(gpa, "{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
} }
} }
pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 { pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 {
if (self.isNativeTriple()) if (self.isNativeTriple()) return gpa.dupe(u8, "native");
return allocator.dupe(u8, "native");
const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native"; const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native"; const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
var result = std.ArrayList(u8).init(allocator); var result: std.ArrayListUnmanaged(u8) = .empty;
defer result.deinit(); defer result.deinit(gpa);
try result.writer().print("{s}-{s}", .{ arch_name, os_name }); try result.print(gpa, "{s}-{s}", .{ arch_name, os_name });
// The zig target syntax does not allow specifying a max os version with no min, so // The zig target syntax does not allow specifying a max os version with no min, so
// if either are present, we need the min. // if either are present, we need the min.
@ -420,11 +419,11 @@ pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 {
switch (min) { switch (min) {
.none => {}, .none => {},
.semver => |v| { .semver => |v| {
try result.writer().writeAll("."); try result.appendSlice(gpa, ".");
try formatVersion(v, result.writer()); try formatVersion(v, gpa, &result);
}, },
.windows => |v| { .windows => |v| {
try result.writer().print("{s}", .{v}); try result.print(gpa, "{d}", .{v});
}, },
} }
} }
@ -432,39 +431,39 @@ pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 {
switch (max) { switch (max) {
.none => {}, .none => {},
.semver => |v| { .semver => |v| {
try result.writer().writeAll("..."); try result.appendSlice(gpa, "...");
try formatVersion(v, result.writer()); try formatVersion(v, gpa, &result);
}, },
.windows => |v| { .windows => |v| {
// This is counting on a custom format() function defined on `WindowsVersion` // This is counting on a custom format() function defined on `WindowsVersion`
// to add a prefix '.' and make there be a total of three dots. // to add a prefix '.' and make there be a total of three dots.
try result.writer().print("..{s}", .{v}); try result.print(gpa, "..{d}", .{v});
}, },
} }
} }
if (self.glibc_version) |v| { if (self.glibc_version) |v| {
const name = if (self.abi) |abi| @tagName(abi) else "gnu"; const name = if (self.abi) |abi| @tagName(abi) else "gnu";
try result.ensureUnusedCapacity(name.len + 2); try result.ensureUnusedCapacity(gpa, name.len + 2);
result.appendAssumeCapacity('-'); result.appendAssumeCapacity('-');
result.appendSliceAssumeCapacity(name); result.appendSliceAssumeCapacity(name);
result.appendAssumeCapacity('.'); result.appendAssumeCapacity('.');
try formatVersion(v, result.writer()); try formatVersion(v, gpa, &result);
} else if (self.android_api_level) |lvl| { } else if (self.android_api_level) |lvl| {
const name = if (self.abi) |abi| @tagName(abi) else "android"; const name = if (self.abi) |abi| @tagName(abi) else "android";
try result.ensureUnusedCapacity(name.len + 2); try result.ensureUnusedCapacity(gpa, name.len + 2);
result.appendAssumeCapacity('-'); result.appendAssumeCapacity('-');
result.appendSliceAssumeCapacity(name); result.appendSliceAssumeCapacity(name);
result.appendAssumeCapacity('.'); result.appendAssumeCapacity('.');
try result.writer().print("{d}", .{lvl}); try result.print(gpa, "{d}", .{lvl});
} else if (self.abi) |abi| { } else if (self.abi) |abi| {
const name = @tagName(abi); const name = @tagName(abi);
try result.ensureUnusedCapacity(name.len + 1); try result.ensureUnusedCapacity(gpa, name.len + 1);
result.appendAssumeCapacity('-'); result.appendAssumeCapacity('-');
result.appendSliceAssumeCapacity(name); result.appendSliceAssumeCapacity(name);
} }
return result.toOwnedSlice(); return result.toOwnedSlice(gpa);
} }
/// Renders the query into a textual representation that can be parsed via the /// Renders the query into a textual representation that can be parsed via the

View File

@ -167,7 +167,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only });
defer file.close(); defer file.close();
try file.writer().writeAll(name); try file.deprecatedWriter().writeAll(name);
return; return;
}, },
.windows => { .windows => {
@ -281,7 +281,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
const file = try std.fs.cwd().openFile(path, .{}); const file = try std.fs.cwd().openFile(path, .{});
defer file.close(); defer file.close();
const data_len = try file.reader().readAll(buffer_ptr[0 .. max_name_len + 1]); const data_len = try file.deprecatedReader().readAll(buffer_ptr[0 .. max_name_len + 1]);
return if (data_len >= 1) buffer[0 .. data_len - 1] else null; return if (data_len >= 1) buffer[0 .. data_len - 1] else null;
}, },
@ -1163,7 +1163,7 @@ const LinuxThreadImpl = struct {
fn getCurrentId() Id { fn getCurrentId() Id {
return tls_thread_id orelse { return tls_thread_id orelse {
const tid = @as(u32, @bitCast(linux.gettid())); const tid: u32 = @bitCast(linux.gettid());
tls_thread_id = tid; tls_thread_id = tid;
return tid; return tid;
}; };

View File

@ -34,27 +34,22 @@ pub const Component = union(enum) {
return switch (component) { return switch (component) {
.raw => |raw| raw, .raw => |raw| raw,
.percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_| .percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_|
try std.fmt.allocPrint(arena, "{raw}", .{component}) try std.fmt.allocPrint(arena, "{fraw}", .{component})
else else
percent_encoded, percent_encoded,
}; };
} }
pub fn format( pub fn format(component: Component, w: *std.io.Writer, comptime fmt_str: []const u8) std.io.Writer.Error!void {
component: Component,
comptime fmt_str: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
if (fmt_str.len == 0) { if (fmt_str.len == 0) {
try writer.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{ try w.print("std.Uri.Component{{ .{s} = \"{f}\" }}", .{
@tagName(component), @tagName(component),
std.zig.fmtEscapes(switch (component) { std.zig.fmtString(switch (component) {
.raw, .percent_encoded => |string| string, .raw, .percent_encoded => |string| string,
}), }),
}); });
} else if (comptime std.mem.eql(u8, fmt_str, "raw")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "raw")) switch (component) {
.raw => |raw| try writer.writeAll(raw), .raw => |raw| try w.writeAll(raw),
.percent_encoded => |percent_encoded| { .percent_encoded => |percent_encoded| {
var start: usize = 0; var start: usize = 0;
var index: usize = 0; var index: usize = 0;
@ -63,51 +58,47 @@ pub const Component = union(enum) {
if (percent_encoded.len - index < 2) continue; if (percent_encoded.len - index < 2) continue;
const percent_encoded_char = const percent_encoded_char =
std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue; std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue;
try writer.print("{s}{c}", .{ try w.print("{s}{c}", .{
percent_encoded[start..percent], percent_encoded[start..percent],
percent_encoded_char, percent_encoded_char,
}); });
start = percent + 3; start = percent + 3;
index = percent + 3; index = percent + 3;
} }
try writer.writeAll(percent_encoded[start..]); try w.writeAll(percent_encoded[start..]);
}, },
} else if (comptime std.mem.eql(u8, fmt_str, "%")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "%")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isUnreserved), .raw => |raw| try percentEncode(w, raw, isUnreserved),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else if (comptime std.mem.eql(u8, fmt_str, "user")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "user")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isUserChar), .raw => |raw| try percentEncode(w, raw, isUserChar),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else if (comptime std.mem.eql(u8, fmt_str, "password")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "password")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isPasswordChar), .raw => |raw| try percentEncode(w, raw, isPasswordChar),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else if (comptime std.mem.eql(u8, fmt_str, "host")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "host")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isHostChar), .raw => |raw| try percentEncode(w, raw, isHostChar),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else if (comptime std.mem.eql(u8, fmt_str, "path")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "path")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isPathChar), .raw => |raw| try percentEncode(w, raw, isPathChar),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else if (comptime std.mem.eql(u8, fmt_str, "query")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "query")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isQueryChar), .raw => |raw| try percentEncode(w, raw, isQueryChar),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else if (comptime std.mem.eql(u8, fmt_str, "fragment")) switch (component) { } else if (comptime std.mem.eql(u8, fmt_str, "fragment")) switch (component) {
.raw => |raw| try percentEncode(writer, raw, isFragmentChar), .raw => |raw| try percentEncode(w, raw, isFragmentChar),
.percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
} else @compileError("invalid format string '" ++ fmt_str ++ "'"); } else @compileError("invalid format string '" ++ fmt_str ++ "'");
} }
pub fn percentEncode( pub fn percentEncode(w: *std.io.Writer, raw: []const u8, comptime isValidChar: fn (u8) bool) std.io.Writer.Error!void {
writer: anytype,
raw: []const u8,
comptime isValidChar: fn (u8) bool,
) @TypeOf(writer).Error!void {
var start: usize = 0; var start: usize = 0;
for (raw, 0..) |char, index| { for (raw, 0..) |char, index| {
if (isValidChar(char)) continue; if (isValidChar(char)) continue;
try writer.print("{s}%{X:0>2}", .{ raw[start..index], char }); try w.print("{s}%{X:0>2}", .{ raw[start..index], char });
start = index + 1; start = index + 1;
} }
try writer.writeAll(raw[start..]); try w.writeAll(raw[start..]);
} }
}; };
@ -247,11 +238,7 @@ pub const WriteToStreamOptions = struct {
port: bool = true, port: bool = true,
}; };
pub fn writeToStream( pub fn writeToStream(uri: Uri, writer: *std.io.Writer, options: WriteToStreamOptions) std.io.Writer.Error!void {
uri: Uri,
options: WriteToStreamOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
if (options.scheme) { if (options.scheme) {
try writer.print("{s}:", .{uri.scheme}); try writer.print("{s}:", .{uri.scheme});
if (options.authority and uri.host != null) { if (options.authority and uri.host != null) {
@ -261,39 +248,34 @@ pub fn writeToStream(
if (options.authority) { if (options.authority) {
if (options.authentication and uri.host != null) { if (options.authentication and uri.host != null) {
if (uri.user) |user| { if (uri.user) |user| {
try writer.print("{user}", .{user}); try writer.print("{fuser}", .{user});
if (uri.password) |password| { if (uri.password) |password| {
try writer.print(":{password}", .{password}); try writer.print(":{fpassword}", .{password});
} }
try writer.writeByte('@'); try writer.writeByte('@');
} }
} }
if (uri.host) |host| { if (uri.host) |host| {
try writer.print("{host}", .{host}); try writer.print("{fhost}", .{host});
if (options.port) { if (options.port) {
if (uri.port) |port| try writer.print(":{d}", .{port}); if (uri.port) |port| try writer.print(":{d}", .{port});
} }
} }
} }
if (options.path) { if (options.path) {
try writer.print("{path}", .{ try writer.print("{fpath}", .{
if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path, if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path,
}); });
if (options.query) { if (options.query) {
if (uri.query) |query| try writer.print("?{query}", .{query}); if (uri.query) |query| try writer.print("?{fquery}", .{query});
} }
if (options.fragment) { if (options.fragment) {
if (uri.fragment) |fragment| try writer.print("#{fragment}", .{fragment}); if (uri.fragment) |fragment| try writer.print("#{ffragment}", .{fragment});
} }
} }
} }
pub fn format( pub fn format(uri: Uri, writer: *std.io.Writer, comptime fmt_str: []const u8) std.io.Writer.Error!void {
uri: Uri,
comptime fmt_str: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
const scheme = comptime std.mem.indexOfScalar(u8, fmt_str, ';') != null or fmt_str.len == 0; const scheme = comptime std.mem.indexOfScalar(u8, fmt_str, ';') != null or fmt_str.len == 0;
const authentication = comptime std.mem.indexOfScalar(u8, fmt_str, '@') != null or fmt_str.len == 0; const authentication = comptime std.mem.indexOfScalar(u8, fmt_str, '@') != null or fmt_str.len == 0;
const authority = comptime std.mem.indexOfScalar(u8, fmt_str, '+') != null or fmt_str.len == 0; const authority = comptime std.mem.indexOfScalar(u8, fmt_str, '+') != null or fmt_str.len == 0;
@ -301,14 +283,14 @@ pub fn format(
const query = comptime std.mem.indexOfScalar(u8, fmt_str, '?') != null or fmt_str.len == 0; const query = comptime std.mem.indexOfScalar(u8, fmt_str, '?') != null or fmt_str.len == 0;
const fragment = comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null or fmt_str.len == 0; const fragment = comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null or fmt_str.len == 0;
return writeToStream(uri, .{ return writeToStream(uri, writer, .{
.scheme = scheme, .scheme = scheme,
.authentication = authentication, .authentication = authentication,
.authority = authority, .authority = authority,
.path = path, .path = path,
.query = query, .query = query,
.fragment = fragment, .fragment = fragment,
}, writer); });
} }
/// Parses the URI or returns an error. /// Parses the URI or returns an error.
@ -447,7 +429,7 @@ test remove_dot_segments {
fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component { fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component {
var aux = std.io.fixedBufferStream(aux_buf.*); var aux = std.io.fixedBufferStream(aux_buf.*);
if (!base.isEmpty()) { if (!base.isEmpty()) {
try aux.writer().print("{path}", .{base}); try aux.writer().print("{fpath}", .{base});
aux.pos = std.mem.lastIndexOfScalar(u8, aux.getWritten(), '/') orelse aux.pos = std.mem.lastIndexOfScalar(u8, aux.getWritten(), '/') orelse
return remove_dot_segments(new); return remove_dot_segments(new);
} }
@ -812,7 +794,7 @@ test "Special test" {
test "URI percent encoding" { test "URI percent encoding" {
try std.testing.expectFmt( try std.testing.expectFmt(
"%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad", "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad",
"{%}", "{f%}",
.{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }}, .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }},
); );
} }
@ -822,7 +804,7 @@ test "URI percent decoding" {
const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad";
var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*; var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*;
try std.testing.expectFmt(expected, "{raw}", .{Component{ .percent_encoded = &input }}); try std.testing.expectFmt(expected, "{fraw}", .{Component{ .percent_encoded = &input }});
var output: [expected.len]u8 = undefined; var output: [expected.len]u8 = undefined;
try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);
@ -834,7 +816,7 @@ test "URI percent decoding" {
const expected = "/abc%"; const expected = "/abc%";
var input = expected.*; var input = expected.*;
try std.testing.expectFmt(expected, "{raw}", .{Component{ .percent_encoded = &input }}); try std.testing.expectFmt(expected, "{fraw}", .{Component{ .percent_encoded = &input }});
var output: [expected.len]u8 = undefined; var output: [expected.len]u8 = undefined;
try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);
@ -848,7 +830,7 @@ test "URI query encoding" {
const parsed = try Uri.parse(address); const parsed = try Uri.parse(address);
// format the URI to percent encode it // format the URI to percent encode it
try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{/?}", .{parsed}); try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{f/?}", .{parsed});
} }
test "format" { test "format" {
@ -862,7 +844,7 @@ test "format" {
.query = null, .query = null,
.fragment = null, .fragment = null,
}; };
try std.testing.expectFmt("file:/foo/bar/baz", "{;/?#}", .{uri}); try std.testing.expectFmt("file:/foo/bar/baz", "{f;/?#}", .{uri});
} }
test "URI malformed input" { test "URI malformed input" {

View File

@ -435,3 +435,44 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {
pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool { pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
return orderIgnoreCase(lhs, rhs) == .lt; return orderIgnoreCase(lhs, rhs) == .lt;
} }
pub const HexEscape = struct {
bytes: []const u8,
charset: *const [16]u8,
pub const upper_charset = "0123456789ABCDEF";
pub const lower_charset = "0123456789abcdef";
pub fn format(se: HexEscape, w: *std.io.Writer) std.io.Writer.Error!void {
const charset = se.charset;
var buf: [4]u8 = undefined;
buf[0] = '\\';
buf[1] = 'x';
for (se.bytes) |c| {
if (std.ascii.isPrint(c)) {
try w.writeByte(c);
} else {
buf[2] = charset[c >> 4];
buf[3] = charset[c & 15];
try w.writeAll(&buf);
}
}
}
};
/// Replaces non-ASCII bytes with hex escapes.
pub fn hexEscape(bytes: []const u8, case: std.fmt.Case) std.fmt.Formatter(HexEscape, HexEscape.format) {
return .{ .data = .{ .bytes = bytes, .charset = switch (case) {
.lower => HexEscape.lower_charset,
.upper => HexEscape.upper_charset,
} } };
}
test hexEscape {
try std.testing.expectFmt("abc 123", "{f}", .{hexEscape("abc 123", .lower)});
try std.testing.expectFmt("ab\\xffc", "{f}", .{hexEscape("ab\xffc", .lower)});
try std.testing.expectFmt("abc 123", "{f}", .{hexEscape("abc 123", .upper)});
try std.testing.expectFmt("ab\\xFFc", "{f}", .{hexEscape("ab\xffc", .upper)});
}

View File

@ -34,20 +34,14 @@ pub const StackTrace = struct {
index: usize, index: usize,
instruction_addresses: []usize, instruction_addresses: []usize,
pub fn format( pub fn format(self: StackTrace, writer: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
self: StackTrace, if (fmt.len != 0) unreachable;
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
// TODO: re-evaluate whether to use format() methods at all. // TODO: re-evaluate whether to use format() methods at all.
// Until then, avoid an error when using GeneralPurposeAllocator with WebAssembly // Until then, avoid an error when using GeneralPurposeAllocator with WebAssembly
// where it tries to call detectTTYConfig here. // where it tries to call detectTTYConfig here.
if (builtin.os.tag == .freestanding) return; if (builtin.os.tag == .freestanding) return;
_ = options;
const debug_info = std.debug.getSelfDebugInfo() catch |err| { const debug_info = std.debug.getSelfDebugInfo() catch |err| {
return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
}; };

View File

@ -124,9 +124,9 @@ test "curve25519" {
const p = try Curve25519.basePoint.clampedMul(s); const p = try Curve25519.basePoint.clampedMul(s);
try p.rejectIdentity(); try p.rejectIdentity();
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&p.toBytes())}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&p.toBytes()}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145");
const q = try p.clampedMul(s); const q = try p.clampedMul(s);
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&q.toBytes())}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&q.toBytes()}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537");
try Curve25519.rejectNonCanonical(s); try Curve25519.rejectNonCanonical(s);
s[31] |= 0x80; s[31] |= 0x80;

View File

@ -509,8 +509,8 @@ test "key pair creation" {
_ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
const key_pair = try Ed25519.KeyPair.generateDeterministic(seed); const key_pair = try Ed25519.KeyPair.generateDeterministic(seed);
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.secret_key.toBytes())}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&key_pair.secret_key.toBytes()}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.public_key.toBytes())}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&key_pair.public_key.toBytes()}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
} }
test "signature" { test "signature" {
@ -520,7 +520,7 @@ test "signature" {
const sig = try key_pair.sign("test", null); const sig = try key_pair.sign("test", null);
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&sig.toBytes())}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&sig.toBytes()}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
try sig.verify("test", key_pair.public_key); try sig.verify("test", key_pair.public_key);
try std.testing.expectError(error.SignatureVerificationFailed, sig.verify("TEST", key_pair.public_key)); try std.testing.expectError(error.SignatureVerificationFailed, sig.verify("TEST", key_pair.public_key));
} }

View File

@ -546,7 +546,7 @@ test "packing/unpacking" {
var b = Edwards25519.basePoint; var b = Edwards25519.basePoint;
const pk = try b.mul(s); const pk = try b.mul(s);
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&pk.toBytes())}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&pk.toBytes()}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6");
const small_order_ss: [7][32]u8 = .{ const small_order_ss: [7][32]u8 = .{
.{ .{

View File

@ -175,21 +175,21 @@ pub const Ristretto255 = struct {
test "ristretto255" { test "ristretto255" {
const p = Ristretto255.basePoint; const p = Ristretto255.basePoint;
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&p.toBytes())}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
var r: [Ristretto255.encoded_length]u8 = undefined; var r: [Ristretto255.encoded_length]u8 = undefined;
_ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
var q = try Ristretto255.fromBytes(r); var q = try Ristretto255.fromBytes(r);
q = q.dbl().add(p); q = q.dbl().add(p);
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&q.toBytes())}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
const s = [_]u8{15} ++ [_]u8{0} ** 31; const s = [_]u8{15} ++ [_]u8{0} ** 31;
const w = try p.mul(s); const w = try p.mul(s);
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&w.toBytes())}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E");
try std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p))); try std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p)));
const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32; const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32;
const ph = Ristretto255.fromUniform(h); const ph = Ristretto255.fromUniform(h);
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ph.toBytes())}), "DCCA54E037A4311EFBEEF413ACD21D35276518970B7A61DC88F8587B493D5E19"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&ph.toBytes()}), "DCCA54E037A4311EFBEEF413ACD21D35276518970B7A61DC88F8587B493D5E19");
} }

View File

@ -850,10 +850,10 @@ test "scalar25519" {
var y = x.toBytes(); var y = x.toBytes();
try rejectNonCanonical(y); try rejectNonCanonical(y);
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&y)}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&y}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F");
const reduced = reduce(field_order_s); const reduced = reduce(field_order_s);
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&reduced)}), "0000000000000000000000000000000000000000000000000000000000000000"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&reduced}), "0000000000000000000000000000000000000000000000000000000000000000");
} }
test "non-canonical scalar25519" { test "non-canonical scalar25519" {
@ -867,7 +867,7 @@ test "mulAdd overflow check" {
const c: [32]u8 = [_]u8{0xff} ** 32; const c: [32]u8 = [_]u8{0xff} ** 32;
const x = mulAdd(a, b, c); const x = mulAdd(a, b, c);
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&x)}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903"); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&x}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903");
} }
test "scalar field inversion" { test "scalar field inversion" {

View File

@ -458,7 +458,7 @@ fn mode(comptime x: comptime_int) comptime_int {
} }
pub fn main() !void { pub fn main() !void {
const stdout = std.fs.File.stdout().writer(); const stdout = std.fs.File.stdout().deprecatedWriter();
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); defer arena.deinit();

View File

@ -1145,7 +1145,7 @@ test "xchacha20" {
var c: [m.len]u8 = undefined; var c: [m.len]u8 = undefined;
XChaCha20IETF.xor(c[0..], m[0..], 0, key, nonce); XChaCha20IETF.xor(c[0..], m[0..], 0, key, nonce);
var buf: [2 * c.len]u8 = undefined; var buf: [2 * c.len]u8 = undefined;
try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D"); try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&c}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
} }
{ {
const ad = "Additional data"; const ad = "Additional data";
@ -1154,7 +1154,7 @@ test "xchacha20" {
var out: [m.len]u8 = undefined; var out: [m.len]u8 = undefined;
try XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key); try XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key);
var buf: [2 * c.len]u8 = undefined; var buf: [2 * c.len]u8 = undefined;
try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234"); try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&c}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
try testing.expectEqualSlices(u8, out[0..], m); try testing.expectEqualSlices(u8, out[0..], m);
c[0] +%= 1; c[0] +%= 1;
try testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key)); try testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key));

View File

@ -1737,11 +1737,11 @@ test "NIST KAT test" {
var f = sha2.Sha256.init(.{}); var f = sha2.Sha256.init(.{});
const fw = f.writer(); const fw = f.writer();
var g = NistDRBG.init(seed); var g = NistDRBG.init(seed);
try std.fmt.format(fw, "# {s}\n\n", .{mode.name}); try std.fmt.deprecatedFormat(fw, "# {s}\n\n", .{mode.name});
for (0..100) |i| { for (0..100) |i| {
g.fill(&seed); g.fill(&seed);
try std.fmt.format(fw, "count = {}\n", .{i}); try std.fmt.deprecatedFormat(fw, "count = {}\n", .{i});
try std.fmt.format(fw, "seed = {s}\n", .{std.fmt.fmtSliceHexUpper(&seed)}); try std.fmt.deprecatedFormat(fw, "seed = {X}\n", .{&seed});
var g2 = NistDRBG.init(seed); var g2 = NistDRBG.init(seed);
// This is not equivalent to g2.fill(kseed[:]). As the reference // This is not equivalent to g2.fill(kseed[:]). As the reference
@ -1756,16 +1756,16 @@ test "NIST KAT test" {
const e = kp.public_key.encaps(eseed); const e = kp.public_key.encaps(eseed);
const ss2 = try kp.secret_key.decaps(&e.ciphertext); const ss2 = try kp.secret_key.decaps(&e.ciphertext);
try testing.expectEqual(ss2, e.shared_secret); try testing.expectEqual(ss2, e.shared_secret);
try std.fmt.format(fw, "pk = {s}\n", .{std.fmt.fmtSliceHexUpper(&kp.public_key.toBytes())}); try std.fmt.deprecatedFormat(fw, "pk = {X}\n", .{&kp.public_key.toBytes()});
try std.fmt.format(fw, "sk = {s}\n", .{std.fmt.fmtSliceHexUpper(&kp.secret_key.toBytes())}); try std.fmt.deprecatedFormat(fw, "sk = {X}\n", .{&kp.secret_key.toBytes()});
try std.fmt.format(fw, "ct = {s}\n", .{std.fmt.fmtSliceHexUpper(&e.ciphertext)}); try std.fmt.deprecatedFormat(fw, "ct = {X}\n", .{&e.ciphertext});
try std.fmt.format(fw, "ss = {s}\n\n", .{std.fmt.fmtSliceHexUpper(&e.shared_secret)}); try std.fmt.deprecatedFormat(fw, "ss = {X}\n\n", .{&e.shared_secret});
} }
var out: [32]u8 = undefined; var out: [32]u8 = undefined;
f.final(&out); f.final(&out);
var outHex: [64]u8 = undefined; var outHex: [64]u8 = undefined;
_ = try std.fmt.bufPrint(&outHex, "{s}", .{std.fmt.fmtSliceHexLower(&out)}); _ = try std.fmt.bufPrint(&outHex, "{x}", .{&out});
try testing.expectEqual(outHex, modeHash[1].*); try testing.expectEqual(outHex, modeHash[1].*);
} }
} }

View File

@ -1512,11 +1512,11 @@ fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) voi
const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false; const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false;
defer if (locked) key_log_file.unlock(); defer if (locked) key_log_file.unlock();
key_log_file.seekFromEnd(0) catch {}; key_log_file.seekFromEnd(0) catch {};
inline for (@typeInfo(@TypeOf(secrets)).@"struct".fields) |field| key_log_file.writer().print("{s}" ++ inline for (@typeInfo(@TypeOf(secrets)).@"struct".fields) |field| key_log_file.deprecatedWriter().print("{s}" ++
(if (@hasField(@TypeOf(context), "counter")) "_{d}" else "") ++ " {} {}\n", .{field.name} ++ (if (@hasField(@TypeOf(context), "counter")) "_{d}" else "") ++ " {x} {x}\n", .{field.name} ++
(if (@hasField(@TypeOf(context), "counter")) .{context.counter} else .{}) ++ .{ (if (@hasField(@TypeOf(context), "counter")) .{context.counter} else .{}) ++ .{
std.fmt.fmtSliceHexLower(context.client_random), context.client_random,
std.fmt.fmtSliceHexLower(@field(secrets, field.name)), @field(secrets, field.name),
}) catch {}; }) catch {};
} }

View File

@ -12,6 +12,7 @@ const windows = std.os.windows;
const native_arch = builtin.cpu.arch; const native_arch = builtin.cpu.arch;
const native_os = builtin.os.tag; const native_os = builtin.os.tag;
const native_endian = native_arch.endian(); const native_endian = native_arch.endian();
const Writer = std.io.Writer;
pub const MemoryAccessor = @import("debug/MemoryAccessor.zig"); pub const MemoryAccessor = @import("debug/MemoryAccessor.zig");
pub const FixedBufferReader = @import("debug/FixedBufferReader.zig"); pub const FixedBufferReader = @import("debug/FixedBufferReader.zig");
@ -204,13 +205,26 @@ pub fn unlockStdErr() void {
std.Progress.unlockStdErr(); std.Progress.unlockStdErr();
} }
/// Allows the caller to freely write to stderr until `unlockStdErr` is called.
///
/// During the lock, any `std.Progress` information is cleared from the terminal.
///
/// Returns a `Writer` with empty buffer, meaning that it is
/// in fact unbuffered and does not need to be flushed.
pub fn lockStderrWriter(buffer: []u8) *Writer {
return std.Progress.lockStderrWriter(buffer);
}
pub fn unlockStderrWriter() void {
std.Progress.unlockStderrWriter();
}
/// Print to stderr, unbuffered, and silently returning on failure. Intended /// Print to stderr, unbuffered, and silently returning on failure. Intended
/// for use in "printf debugging." Use `std.log` functions for proper logging. /// for use in "printf debugging". Use `std.log` functions for proper logging.
pub fn print(comptime fmt: []const u8, args: anytype) void { pub fn print(comptime fmt: []const u8, args: anytype) void {
lockStdErr(); const bw = lockStderrWriter(&.{});
defer unlockStdErr(); defer unlockStderrWriter();
const stderr = fs.File.stderr().writer(); nosuspend bw.print(fmt, args) catch return;
nosuspend stderr.print(fmt, args) catch return;
} }
pub fn getStderrMutex() *std.Thread.Mutex { pub fn getStderrMutex() *std.Thread.Mutex {
@ -232,50 +246,44 @@ pub fn getSelfDebugInfo() !*SelfInfo {
/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned. /// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.
/// Obtains the stderr mutex while dumping. /// Obtains the stderr mutex while dumping.
pub fn dumpHex(bytes: []const u8) void { pub fn dumpHex(bytes: []const u8) void {
lockStdErr(); const bw = lockStderrWriter(&.{});
defer unlockStdErr(); defer unlockStderrWriter();
dumpHexFallible(bytes) catch {}; const ttyconf = std.io.tty.detectConfig(.stderr());
dumpHexFallible(bw, ttyconf, bytes) catch {};
} }
/// Prints a hexadecimal view of the bytes, unbuffered, returning any error that occurs. /// Prints a hexadecimal view of the bytes, returning any error that occurs.
pub fn dumpHexFallible(bytes: []const u8) !void { pub fn dumpHexFallible(bw: *Writer, ttyconf: std.io.tty.Config, bytes: []const u8) !void {
const stderr: fs.File = .stderr();
const ttyconf = std.io.tty.detectConfig(stderr);
const writer = stderr.writer();
try dumpHexInternal(bytes, ttyconf, writer);
}
fn dumpHexInternal(bytes: []const u8, ttyconf: std.io.tty.Config, writer: anytype) !void {
var chunks = mem.window(u8, bytes, 16, 16); var chunks = mem.window(u8, bytes, 16, 16);
while (chunks.next()) |window| { while (chunks.next()) |window| {
// 1. Print the address. // 1. Print the address.
const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10; const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10;
try ttyconf.setColor(writer, .dim); try ttyconf.setColor(bw, .dim);
// We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more. // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.
// Also, make sure all lines are aligned by padding the address. // Also, make sure all lines are aligned by padding the address.
try writer.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 }); try bw.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 });
try ttyconf.setColor(writer, .reset); try ttyconf.setColor(bw, .reset);
// 2. Print the bytes. // 2. Print the bytes.
for (window, 0..) |byte, index| { for (window, 0..) |byte, index| {
try writer.print("{X:0>2} ", .{byte}); try bw.print("{X:0>2} ", .{byte});
if (index == 7) try writer.writeByte(' '); if (index == 7) try bw.writeByte(' ');
} }
try writer.writeByte(' '); try bw.writeByte(' ');
if (window.len < 16) { if (window.len < 16) {
var missing_columns = (16 - window.len) * 3; var missing_columns = (16 - window.len) * 3;
if (window.len < 8) missing_columns += 1; if (window.len < 8) missing_columns += 1;
try writer.writeByteNTimes(' ', missing_columns); try bw.splatByteAll(' ', missing_columns);
} }
// 3. Print the characters. // 3. Print the characters.
for (window) |byte| { for (window) |byte| {
if (std.ascii.isPrint(byte)) { if (std.ascii.isPrint(byte)) {
try writer.writeByte(byte); try bw.writeByte(byte);
} else { } else {
// Related: https://github.com/ziglang/zig/issues/7600 // Related: https://github.com/ziglang/zig/issues/7600
if (ttyconf == .windows_api) { if (ttyconf == .windows_api) {
try writer.writeByte('.'); try bw.writeByte('.');
continue; continue;
} }
@ -283,22 +291,23 @@ fn dumpHexInternal(bytes: []const u8, ttyconf: std.io.tty.Config, writer: anytyp
// We don't want to do this for all control codes because most control codes apart from // We don't want to do this for all control codes because most control codes apart from
// the ones that Zig has escape sequences for are likely not very useful to print as symbols. // the ones that Zig has escape sequences for are likely not very useful to print as symbols.
switch (byte) { switch (byte) {
'\n' => try writer.writeAll(""), '\n' => try bw.writeAll(""),
'\r' => try writer.writeAll(""), '\r' => try bw.writeAll(""),
'\t' => try writer.writeAll(""), '\t' => try bw.writeAll(""),
else => try writer.writeByte('.'), else => try bw.writeByte('.'),
} }
} }
} }
try writer.writeByte('\n'); try bw.writeByte('\n');
} }
} }
test dumpHexInternal { test dumpHexFallible {
const bytes: []const u8 = &.{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x12, 0x13 }; const bytes: []const u8 = &.{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x12, 0x13 };
var output = std.ArrayList(u8).init(std.testing.allocator); var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
defer output.deinit(); defer aw.deinit();
try dumpHexInternal(bytes, .no_color, output.writer());
try dumpHexFallible(&aw.interface, .no_color, bytes);
const expected = try std.fmt.allocPrint(std.testing.allocator, const expected = try std.fmt.allocPrint(std.testing.allocator,
\\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........ \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........
\\{x:0>[2]} 01 12 13 ... \\{x:0>[2]} 01 12 13 ...
@ -309,34 +318,36 @@ test dumpHexInternal {
@sizeOf(usize) * 2, @sizeOf(usize) * 2,
}); });
defer std.testing.allocator.free(expected); defer std.testing.allocator.free(expected);
try std.testing.expectEqualStrings(expected, output.items); try std.testing.expectEqualStrings(expected, aw.getWritten());
} }
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
/// TODO multithreaded awareness
pub fn dumpCurrentStackTrace(start_addr: ?usize) void { pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
nosuspend { const stderr = lockStderrWriter(&.{});
defer unlockStderrWriter();
nosuspend dumpCurrentStackTraceToWriter(start_addr, stderr) catch return;
}
/// Prints the current stack trace to the provided writer.
pub fn dumpCurrentStackTraceToWriter(start_addr: ?usize, writer: *Writer) !void {
if (builtin.target.cpu.arch.isWasm()) { if (builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) { if (native_os == .wasi) {
const stderr = fs.File.stderr().writer(); try writer.writeAll("Unable to dump stack trace: not implemented for Wasm\n");
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
} }
return; return;
} }
const stderr = fs.File.stderr().writer();
if (builtin.strip_debug_info) { if (builtin.strip_debug_info) {
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; try writer.writeAll("Unable to dump stack trace: debug info stripped\n");
return; return;
} }
const debug_info = getSelfDebugInfo() catch |err| { const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; try writer.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
return; return;
}; };
writeCurrentStackTrace(stderr, debug_info, io.tty.detectConfig(fs.File.stderr()), start_addr) catch |err| { writeCurrentStackTrace(writer, debug_info, io.tty.detectConfig(.stderr()), start_addr) catch |err| {
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; try writer.print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
return; return;
}; };
}
} }
pub const have_ucontext = posix.ucontext_t != void; pub const have_ucontext = posix.ucontext_t != void;
@ -402,16 +413,14 @@ pub inline fn getContext(context: *ThreadContext) bool {
/// Tries to print the stack trace starting from the supplied base pointer to stderr, /// Tries to print the stack trace starting from the supplied base pointer to stderr,
/// unbuffered, and ignores any error returned. /// unbuffered, and ignores any error returned.
/// TODO multithreaded awareness /// TODO multithreaded awareness
pub fn dumpStackTraceFromBase(context: *ThreadContext) void { pub fn dumpStackTraceFromBase(context: *ThreadContext, stderr: *Writer) void {
nosuspend { nosuspend {
if (builtin.target.cpu.arch.isWasm()) { if (builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) { if (native_os == .wasi) {
const stderr = fs.File.stderr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
} }
return; return;
} }
const stderr = fs.File.stderr().writer();
if (builtin.strip_debug_info) { if (builtin.strip_debug_info) {
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
return; return;
@ -420,7 +429,7 @@ pub fn dumpStackTraceFromBase(context: *ThreadContext) void {
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return; return;
}; };
const tty_config = io.tty.detectConfig(fs.File.stderr()); const tty_config = io.tty.detectConfig(.stderr());
if (native_os == .windows) { if (native_os == .windows) {
// On x86_64 and aarch64, the stack will be unwound using RtlVirtualUnwind using the context // On x86_64 and aarch64, the stack will be unwound using RtlVirtualUnwind using the context
// provided by the exception handler. On x86, RtlVirtualUnwind doesn't exist. Instead, a new backtrace // provided by the exception handler. On x86, RtlVirtualUnwind doesn't exist. Instead, a new backtrace
@ -510,21 +519,23 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
nosuspend { nosuspend {
if (builtin.target.cpu.arch.isWasm()) { if (builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) { if (native_os == .wasi) {
const stderr = fs.File.stderr().writer(); const stderr = lockStderrWriter(&.{});
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; defer unlockStderrWriter();
stderr.writeAll("Unable to dump stack trace: not implemented for Wasm\n") catch return;
} }
return; return;
} }
const stderr = fs.File.stderr().writer(); const stderr = lockStderrWriter(&.{});
defer unlockStderrWriter();
if (builtin.strip_debug_info) { if (builtin.strip_debug_info) {
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; stderr.writeAll("Unable to dump stack trace: debug info stripped\n") catch return;
return; return;
} }
const debug_info = getSelfDebugInfo() catch |err| { const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return; return;
}; };
writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(fs.File.stderr())) catch |err| { writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(.stderr())) catch |err| {
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return; return;
}; };
@ -573,14 +584,13 @@ pub fn panicExtra(
const size = 0x1000; const size = 0x1000;
const trunc_msg = "(msg truncated)"; const trunc_msg = "(msg truncated)";
var buf: [size + trunc_msg.len]u8 = undefined; var buf: [size + trunc_msg.len]u8 = undefined;
var bw: Writer = .fixed(buf[0..size]);
// a minor annoyance with this is that it will result in the NoSpaceLeft // a minor annoyance with this is that it will result in the NoSpaceLeft
// error being part of the @panic stack trace (but that error should // error being part of the @panic stack trace (but that error should
// only happen rarely) // only happen rarely)
const msg = std.fmt.bufPrint(buf[0..size], format, args) catch |err| switch (err) { const msg = if (bw.print(format, args)) |_| bw.buffered() else |_| blk: {
error.NoSpaceLeft => blk: {
@memcpy(buf[size..], trunc_msg); @memcpy(buf[size..], trunc_msg);
break :blk &buf; break :blk &buf;
},
}; };
std.builtin.panic.call(msg, ret_addr); std.builtin.panic.call(msg, ret_addr);
} }
@ -675,10 +685,9 @@ pub fn defaultPanic(
_ = panicking.fetchAdd(1, .seq_cst); _ = panicking.fetchAdd(1, .seq_cst);
{ {
lockStdErr(); const stderr = lockStderrWriter(&.{});
defer unlockStdErr(); defer unlockStderrWriter();
const stderr = fs.File.stderr().writer();
if (builtin.single_threaded) { if (builtin.single_threaded) {
stderr.print("panic: ", .{}) catch posix.abort(); stderr.print("panic: ", .{}) catch posix.abort();
} else { } else {
@ -688,7 +697,7 @@ pub fn defaultPanic(
stderr.print("{s}\n", .{msg}) catch posix.abort(); stderr.print("{s}\n", .{msg}) catch posix.abort();
if (@errorReturnTrace()) |t| dumpStackTrace(t.*); if (@errorReturnTrace()) |t| dumpStackTrace(t.*);
dumpCurrentStackTrace(first_trace_addr orelse @returnAddress()); dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), stderr) catch {};
} }
waitForOtherThreadToFinishPanicking(); waitForOtherThreadToFinishPanicking();
@ -723,7 +732,7 @@ fn waitForOtherThreadToFinishPanicking() void {
pub fn writeStackTrace( pub fn writeStackTrace(
stack_trace: std.builtin.StackTrace, stack_trace: std.builtin.StackTrace,
out_stream: anytype, writer: *Writer,
debug_info: *SelfInfo, debug_info: *SelfInfo,
tty_config: io.tty.Config, tty_config: io.tty.Config,
) !void { ) !void {
@ -736,15 +745,15 @@ pub fn writeStackTrace(
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
}) { }) {
const return_address = stack_trace.instruction_addresses[frame_index]; const return_address = stack_trace.instruction_addresses[frame_index];
try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config); try printSourceAtAddress(debug_info, writer, return_address - 1, tty_config);
} }
if (stack_trace.index > stack_trace.instruction_addresses.len) { if (stack_trace.index > stack_trace.instruction_addresses.len) {
const dropped_frames = stack_trace.index - stack_trace.instruction_addresses.len; const dropped_frames = stack_trace.index - stack_trace.instruction_addresses.len;
tty_config.setColor(out_stream, .bold) catch {}; tty_config.setColor(writer, .bold) catch {};
try out_stream.print("({d} additional stack frames skipped...)\n", .{dropped_frames}); try writer.print("({d} additional stack frames skipped...)\n", .{dropped_frames});
tty_config.setColor(out_stream, .reset) catch {}; tty_config.setColor(writer, .reset) catch {};
} }
} }
@ -954,7 +963,7 @@ pub const StackIterator = struct {
}; };
pub fn writeCurrentStackTrace( pub fn writeCurrentStackTrace(
out_stream: anytype, writer: *Writer,
debug_info: *SelfInfo, debug_info: *SelfInfo,
tty_config: io.tty.Config, tty_config: io.tty.Config,
start_addr: ?usize, start_addr: ?usize,
@ -962,7 +971,7 @@ pub fn writeCurrentStackTrace(
if (native_os == .windows) { if (native_os == .windows) {
var context: ThreadContext = undefined; var context: ThreadContext = undefined;
assert(getContext(&context)); assert(getContext(&context));
return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr); return writeStackTraceWindows(writer, debug_info, tty_config, &context, start_addr);
} }
var context: ThreadContext = undefined; var context: ThreadContext = undefined;
const has_context = getContext(&context); const has_context = getContext(&context);
@ -973,7 +982,7 @@ pub fn writeCurrentStackTrace(
defer it.deinit(); defer it.deinit();
while (it.next()) |return_address| { while (it.next()) |return_address| {
printLastUnwindError(&it, debug_info, out_stream, tty_config); printLastUnwindError(&it, debug_info, writer, tty_config);
// On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS, // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS,
// therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid // therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid
@ -981,8 +990,8 @@ pub fn writeCurrentStackTrace(
// condition on the subsequent iteration and return `null` thus terminating the loop. // condition on the subsequent iteration and return `null` thus terminating the loop.
// same behaviour for x86-windows-msvc // same behaviour for x86-windows-msvc
const address = return_address -| 1; const address = return_address -| 1;
try printSourceAtAddress(debug_info, out_stream, address, tty_config); try printSourceAtAddress(debug_info, writer, address, tty_config);
} else printLastUnwindError(&it, debug_info, out_stream, tty_config); } else printLastUnwindError(&it, debug_info, writer, tty_config);
} }
pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const windows.CONTEXT) usize { pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const windows.CONTEXT) usize {
@ -1042,7 +1051,7 @@ pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const w
} }
pub fn writeStackTraceWindows( pub fn writeStackTraceWindows(
out_stream: anytype, writer: *Writer,
debug_info: *SelfInfo, debug_info: *SelfInfo,
tty_config: io.tty.Config, tty_config: io.tty.Config,
context: *const windows.CONTEXT, context: *const windows.CONTEXT,
@ -1058,14 +1067,14 @@ pub fn writeStackTraceWindows(
return; return;
} else 0; } else 0;
for (addrs[start_i..]) |addr| { for (addrs[start_i..]) |addr| {
try printSourceAtAddress(debug_info, out_stream, addr - 1, tty_config); try printSourceAtAddress(debug_info, writer, addr - 1, tty_config);
} }
} }
fn printUnknownSource(debug_info: *SelfInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void { fn printUnknownSource(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: io.tty.Config) !void {
const module_name = debug_info.getModuleNameForAddress(address); const module_name = debug_info.getModuleNameForAddress(address);
return printLineInfo( return printLineInfo(
out_stream, writer,
null, null,
address, address,
"???", "???",
@ -1075,38 +1084,38 @@ fn printUnknownSource(debug_info: *SelfInfo, out_stream: anytype, address: usize
); );
} }
fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, out_stream: anytype, tty_config: io.tty.Config) void { fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writer, tty_config: io.tty.Config) void {
if (!have_ucontext) return; if (!have_ucontext) return;
if (it.getLastError()) |unwind_error| { if (it.getLastError()) |unwind_error| {
printUnwindError(debug_info, out_stream, unwind_error.address, unwind_error.err, tty_config) catch {}; printUnwindError(debug_info, writer, unwind_error.address, unwind_error.err, tty_config) catch {};
} }
} }
fn printUnwindError(debug_info: *SelfInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {
const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; const module_name = debug_info.getModuleNameForAddress(address) orelse "???";
try tty_config.setColor(out_stream, .dim); try tty_config.setColor(writer, .dim);
if (err == error.MissingDebugInfo) { if (err == error.MissingDebugInfo) {
try out_stream.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address }); try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
} else { } else {
try out_stream.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, err }); try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, err });
} }
try tty_config.setColor(out_stream, .reset); try tty_config.setColor(writer, .reset);
} }
pub fn printSourceAtAddress(debug_info: *SelfInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void { pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: io.tty.Config) !void {
const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config), error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
else => return err, else => return err,
}; };
const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch |err| switch (err) { const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config), error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
else => return err, else => return err,
}; };
defer if (symbol_info.source_location) |sl| debug_info.allocator.free(sl.file_name); defer if (symbol_info.source_location) |sl| debug_info.allocator.free(sl.file_name);
return printLineInfo( return printLineInfo(
out_stream, writer,
symbol_info.source_location, symbol_info.source_location,
address, address,
symbol_info.name, symbol_info.name,
@ -1117,7 +1126,7 @@ pub fn printSourceAtAddress(debug_info: *SelfInfo, out_stream: anytype, address:
} }
fn printLineInfo( fn printLineInfo(
out_stream: anytype, writer: *Writer,
source_location: ?SourceLocation, source_location: ?SourceLocation,
address: usize, address: usize,
symbol_name: []const u8, symbol_name: []const u8,
@ -1126,34 +1135,34 @@ fn printLineInfo(
comptime printLineFromFile: anytype, comptime printLineFromFile: anytype,
) !void { ) !void {
nosuspend { nosuspend {
try tty_config.setColor(out_stream, .bold); try tty_config.setColor(writer, .bold);
if (source_location) |*sl| { if (source_location) |*sl| {
try out_stream.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column }); try writer.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column });
} else { } else {
try out_stream.writeAll("???:?:?"); try writer.writeAll("???:?:?");
} }
try tty_config.setColor(out_stream, .reset); try tty_config.setColor(writer, .reset);
try out_stream.writeAll(": "); try writer.writeAll(": ");
try tty_config.setColor(out_stream, .dim); try tty_config.setColor(writer, .dim);
try out_stream.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });
try tty_config.setColor(out_stream, .reset); try tty_config.setColor(writer, .reset);
try out_stream.writeAll("\n"); try writer.writeAll("\n");
// Show the matching source code line if possible // Show the matching source code line if possible
if (source_location) |sl| { if (source_location) |sl| {
if (printLineFromFile(out_stream, sl)) { if (printLineFromFile(writer, sl)) {
if (sl.column > 0) { if (sl.column > 0) {
// The caret already takes one char // The caret already takes one char
const space_needed = @as(usize, @intCast(sl.column - 1)); const space_needed = @as(usize, @intCast(sl.column - 1));
try out_stream.writeByteNTimes(' ', space_needed); try writer.splatByteAll(' ', space_needed);
try tty_config.setColor(out_stream, .green); try tty_config.setColor(writer, .green);
try out_stream.writeAll("^"); try writer.writeAll("^");
try tty_config.setColor(out_stream, .reset); try tty_config.setColor(writer, .reset);
} }
try out_stream.writeAll("\n"); try writer.writeAll("\n");
} else |err| switch (err) { } else |err| switch (err) {
error.EndOfFile, error.FileNotFound => {}, error.EndOfFile, error.FileNotFound => {},
error.BadPathName => {}, error.BadPathName => {},
@ -1164,7 +1173,7 @@ fn printLineInfo(
} }
} }
fn printLineFromFileAnyOs(out_stream: anytype, source_location: SourceLocation) !void { fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !void {
// Need this to always block even in async I/O mode, because this could potentially // Need this to always block even in async I/O mode, because this could potentially
// be called from e.g. the event loop code crashing. // be called from e.g. the event loop code crashing.
var f = try fs.cwd().openFile(source_location.file_name, .{}); var f = try fs.cwd().openFile(source_location.file_name, .{});
@ -1197,31 +1206,31 @@ fn printLineFromFileAnyOs(out_stream: anytype, source_location: SourceLocation)
if (mem.indexOfScalar(u8, slice, '\n')) |pos| { if (mem.indexOfScalar(u8, slice, '\n')) |pos| {
const line = slice[0 .. pos + 1]; const line = slice[0 .. pos + 1];
mem.replaceScalar(u8, line, '\t', ' '); mem.replaceScalar(u8, line, '\t', ' ');
return out_stream.writeAll(line); return writer.writeAll(line);
} else { // Line is the last inside the buffer, and requires another read to find delimiter. Alternatively the file ends. } else { // Line is the last inside the buffer, and requires another read to find delimiter. Alternatively the file ends.
mem.replaceScalar(u8, slice, '\t', ' '); mem.replaceScalar(u8, slice, '\t', ' ');
try out_stream.writeAll(slice); try writer.writeAll(slice);
while (amt_read == buf.len) { while (amt_read == buf.len) {
amt_read = try f.read(buf[0..]); amt_read = try f.read(buf[0..]);
if (mem.indexOfScalar(u8, buf[0..amt_read], '\n')) |pos| { if (mem.indexOfScalar(u8, buf[0..amt_read], '\n')) |pos| {
const line = buf[0 .. pos + 1]; const line = buf[0 .. pos + 1];
mem.replaceScalar(u8, line, '\t', ' '); mem.replaceScalar(u8, line, '\t', ' ');
return out_stream.writeAll(line); return writer.writeAll(line);
} else { } else {
const line = buf[0..amt_read]; const line = buf[0..amt_read];
mem.replaceScalar(u8, line, '\t', ' '); mem.replaceScalar(u8, line, '\t', ' ');
try out_stream.writeAll(line); try writer.writeAll(line);
} }
} }
// Make sure printing last line of file inserts extra newline // Make sure printing last line of file inserts extra newline
try out_stream.writeByte('\n'); try writer.writeByte('\n');
} }
} }
test printLineFromFileAnyOs { test printLineFromFileAnyOs {
var output = std.ArrayList(u8).init(std.testing.allocator); var aw: Writer.Allocating = .init(std.testing.allocator);
defer output.deinit(); defer aw.deinit();
const output_stream = output.writer(); const output_stream = &aw.interface;
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
const join = std.fs.path.join; const join = std.fs.path.join;
@ -1243,8 +1252,8 @@ test printLineFromFileAnyOs {
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
try expectEqualStrings("no new lines in this file, but one is printed anyway\n", output.items); try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
} }
{ {
const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" }); const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
@ -1259,12 +1268,12 @@ test printLineFromFileAnyOs {
}); });
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
try expectEqualStrings("1\n", output.items); try expectEqualStrings("1\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
try expectEqualStrings("3\n", output.items); try expectEqualStrings("3\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
} }
{ {
const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
@ -1273,14 +1282,15 @@ test printLineFromFileAnyOs {
defer allocator.free(path); defer allocator.free(path);
const overlap = 10; const overlap = 10;
var writer = file.writer(); var file_writer = file.writer(&.{});
try writer.writeByteNTimes('a', std.heap.page_size_min - overlap); const writer = &file_writer.interface;
try writer.splatByteAll('a', std.heap.page_size_min - overlap);
try writer.writeByte('\n'); try writer.writeByte('\n');
try writer.writeByteNTimes('a', overlap); try writer.splatByteAll('a', overlap);
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
try expectEqualStrings(("a" ** overlap) ++ "\n", output.items); try expectEqualStrings(("a" ** overlap) ++ "\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
} }
{ {
const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
@ -1288,12 +1298,13 @@ test printLineFromFileAnyOs {
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
defer allocator.free(path); defer allocator.free(path);
var writer = file.writer(); var file_writer = file.writer(&.{});
try writer.writeByteNTimes('a', std.heap.page_size_max); const writer = &file_writer.interface;
try writer.splatByteAll('a', std.heap.page_size_max);
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items); try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
} }
{ {
const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
@ -1301,24 +1312,25 @@ test printLineFromFileAnyOs {
const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
defer allocator.free(path); defer allocator.free(path);
var writer = file.writer(); var file_writer = file.writer(&.{});
try writer.writeByteNTimes('a', 3 * std.heap.page_size_max); const writer = &file_writer.interface;
try writer.splatByteAll('a', 3 * std.heap.page_size_max);
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", output.items); try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
try writer.writeAll("a\na"); try writer.writeAll("a\na");
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", output.items); try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
try expectEqualStrings("a\n", output.items); try expectEqualStrings("a\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
} }
{ {
const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
@ -1326,18 +1338,19 @@ test printLineFromFileAnyOs {
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" }); const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
defer allocator.free(path); defer allocator.free(path);
var writer = file.writer(); var file_writer = file.writer(&.{});
const writer = &file_writer.interface;
const real_file_start = 3 * std.heap.page_size_min; const real_file_start = 3 * std.heap.page_size_min;
try writer.writeByteNTimes('\n', real_file_start); try writer.splatByteAll('\n', real_file_start);
try writer.writeAll("abc\ndef"); try writer.writeAll("abc\ndef");
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
try expectEqualStrings("abc\n", output.items); try expectEqualStrings("abc\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
try expectEqualStrings("def\n", output.items); try expectEqualStrings("def\n", aw.getWritten());
output.clearRetainingCapacity(); aw.clearRetainingCapacity();
} }
} }
@ -1461,7 +1474,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
} }
fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void { fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void {
const stderr = fs.File.stderr().writer(); const stderr = lockStderrWriter(&.{});
defer unlockStderrWriter();
_ = switch (sig) { _ = switch (sig) {
posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
// x86_64 doesn't have a full 64-bit virtual address space. // x86_64 doesn't have a full 64-bit virtual address space.
@ -1471,7 +1485,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque)
// but can also happen when no addressable memory is involved; // but can also happen when no addressable memory is involved;
// for example when reading/writing model-specific registers // for example when reading/writing model-specific registers
// by executing `rdmsr` or `wrmsr` in user-space (unprivileged mode). // by executing `rdmsr` or `wrmsr` in user-space (unprivileged mode).
stderr.print("General protection exception (no address available)\n", .{}) stderr.writeAll("General protection exception (no address available)\n")
else else
stderr.print("Segmentation fault at address 0x{x}\n", .{addr}), stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
posix.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}), posix.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
@ -1509,7 +1523,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque)
}, @ptrCast(ctx)).__mcontext_data; }, @ptrCast(ctx)).__mcontext_data;
} }
relocateContext(&new_ctx); relocateContext(&new_ctx);
dumpStackTraceFromBase(&new_ctx); dumpStackTraceFromBase(&new_ctx, stderr);
}, },
else => {}, else => {},
} }
@ -1539,10 +1553,10 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:
_ = panicking.fetchAdd(1, .seq_cst); _ = panicking.fetchAdd(1, .seq_cst);
{ {
lockStdErr(); const stderr = lockStderrWriter(&.{});
defer unlockStdErr(); defer unlockStderrWriter();
dumpSegfaultInfoWindows(info, msg, label); dumpSegfaultInfoWindows(info, msg, label, stderr);
} }
waitForOtherThreadToFinishPanicking(); waitForOtherThreadToFinishPanicking();
@ -1556,8 +1570,7 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:
posix.abort(); posix.abort();
} }
fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void { fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8, stderr: *Writer) void {
const stderr = fs.File.stderr().writer();
_ = switch (msg) { _ = switch (msg) {
0 => stderr.print("{s}\n", .{label.?}), 0 => stderr.print("{s}\n", .{label.?}),
1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}), 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
@ -1565,7 +1578,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[
else => unreachable, else => unreachable,
} catch posix.abort(); } catch posix.abort();
dumpStackTraceFromBase(info.ContextRecord); dumpStackTraceFromBase(info.ContextRecord, stderr);
} }
pub fn dumpStackPointerAddr(prefix: []const u8) void { pub fn dumpStackPointerAddr(prefix: []const u8) void {
@ -1588,10 +1601,10 @@ test "manage resources correctly" {
// self-hosted debug info is still too buggy // self-hosted debug info is still too buggy
if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest;
const writer = std.io.null_writer; var writer: std.io.Writer = .discarding(&.{});
var di = try SelfInfo.open(testing.allocator); var di = try SelfInfo.open(testing.allocator);
defer di.deinit(); defer di.deinit();
try printSourceAtAddress(&di, writer, showMyTrace(), io.tty.detectConfig(std.fs.File.stderr())); try printSourceAtAddress(&di, &writer, showMyTrace(), io.tty.detectConfig(.stderr()));
} }
noinline fn showMyTrace() usize { noinline fn showMyTrace() usize {
@ -1657,8 +1670,9 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
pub fn dump(t: @This()) void { pub fn dump(t: @This()) void {
if (!enabled) return; if (!enabled) return;
const tty_config = io.tty.detectConfig(std.fs.File.stderr()); const tty_config = io.tty.detectConfig(.stderr());
const stderr = fs.File.stderr().writer(); const stderr = lockStderrWriter(&.{});
defer unlockStderrWriter();
const end = @min(t.index, size); const end = @min(t.index, size);
const debug_info = getSelfDebugInfo() catch |err| { const debug_info = getSelfDebugInfo() catch |err| {
stderr.print( stderr.print(
@ -1688,7 +1702,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
t: @This(), t: @This(),
comptime fmt: []const u8, comptime fmt: []const u8,
options: std.fmt.FormatOptions, options: std.fmt.FormatOptions,
writer: anytype, writer: *Writer,
) !void { ) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, t); if (fmt.len != 0) std.fmt.invalidFmtError(fmt, t);
_ = options; _ = options;

View File

@ -2302,11 +2302,7 @@ pub const ElfModule = struct {
}; };
defer debuginfod_dir.close(); defer debuginfod_dir.close();
const filename = std.fmt.allocPrint( const filename = std.fmt.allocPrint(gpa, "{x}/debuginfo", .{id}) catch break :blk;
gpa,
"{s}/debuginfo",
.{std.fmt.fmtSliceHexLower(id)},
) catch break :blk;
defer gpa.free(filename); defer gpa.free(filename);
const path: Path = .{ const path: Path = .{
@ -2330,12 +2326,8 @@ pub const ElfModule = struct {
var id_prefix_buf: [2]u8 = undefined; var id_prefix_buf: [2]u8 = undefined;
var filename_buf: [38 + extension.len]u8 = undefined; var filename_buf: [38 + extension.len]u8 = undefined;
_ = std.fmt.bufPrint(&id_prefix_buf, "{s}", .{std.fmt.fmtSliceHexLower(id[0..1])}) catch unreachable; _ = std.fmt.bufPrint(&id_prefix_buf, "{x}", .{id[0..1]}) catch unreachable;
const filename = std.fmt.bufPrint( const filename = std.fmt.bufPrint(&filename_buf, "{x}" ++ extension, .{id[1..]}) catch break :blk;
&filename_buf,
"{s}" ++ extension,
.{std.fmt.fmtSliceHexLower(id[1..])},
) catch break :blk;
for (global_debug_directories) |global_directory| { for (global_debug_directories) |global_directory| {
const path: Path = .{ const path: Path = .{

View File

@ -395,7 +395,7 @@ const Msf = struct {
streams: []MsfStream, streams: []MsfStream,
fn init(allocator: Allocator, file: File) !Msf { fn init(allocator: Allocator, file: File) !Msf {
const in = file.reader(); const in = file.deprecatedReader();
const superblock = try in.readStruct(pdb.SuperBlock); const superblock = try in.readStruct(pdb.SuperBlock);
@ -514,7 +514,7 @@ const MsfStream = struct {
var offset = self.pos % self.block_size; var offset = self.pos % self.block_size;
try self.in_file.seekTo(block * self.block_size + offset); try self.in_file.seekTo(block * self.block_size + offset);
const in = self.in_file.reader(); const in = self.in_file.deprecatedReader();
var size: usize = 0; var size: usize = 0;
var rem_buffer = buffer; var rem_buffer = buffer;

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ const special_exponent = 0x7fffffff;
pub const min_buffer_size = 53; pub const min_buffer_size = 53;
/// Returns the minimum buffer size needed to print every float of a specific type and format. /// Returns the minimum buffer size needed to print every float of a specific type and format.
pub fn bufferSize(comptime mode: Format, comptime T: type) comptime_int { pub fn bufferSize(comptime mode: Mode, comptime T: type) comptime_int {
comptime std.debug.assert(@typeInfo(T) == .float); comptime std.debug.assert(@typeInfo(T) == .float);
return switch (mode) { return switch (mode) {
.scientific => 53, .scientific => 53,
@ -27,17 +27,17 @@ pub fn bufferSize(comptime mode: Format, comptime T: type) comptime_int {
}; };
} }
pub const FormatError = error{ pub const Error = error{
BufferTooSmall, BufferTooSmall,
}; };
pub const Format = enum { pub const Mode = enum {
scientific, scientific,
decimal, decimal,
}; };
pub const FormatOptions = struct { pub const Options = struct {
mode: Format = .scientific, mode: Mode = .scientific,
precision: ?usize = null, precision: ?usize = null,
}; };
@ -52,11 +52,11 @@ pub const FormatOptions = struct {
/// ///
/// When printing full precision decimals, use `bufferSize` to get the required space. It is /// When printing full precision decimals, use `bufferSize` to get the required space. It is
/// recommended to bound decimal output with a fixed precision to reduce the required buffer size. /// recommended to bound decimal output with a fixed precision to reduce the required buffer size.
pub fn formatFloat(buf: []u8, v_: anytype, options: FormatOptions) FormatError![]const u8 { pub fn render(buf: []u8, value: anytype, options: Options) Error![]const u8 {
const v = switch (@TypeOf(v_)) { const v = switch (@TypeOf(value)) {
// comptime_float internally is a f128; this preserves precision. // comptime_float internally is a f128; this preserves precision.
comptime_float => @as(f128, v_), comptime_float => @as(f128, value),
else => v_, else => value,
}; };
const T = @TypeOf(v); const T = @TypeOf(v);
@ -192,7 +192,7 @@ fn round(comptime T: type, f: FloatDecimal(T), mode: RoundMode, precision: usize
/// will not fit. /// will not fit.
/// ///
/// It is recommended to bound decimal formatting with an exact precision. /// It is recommended to bound decimal formatting with an exact precision.
pub fn formatScientific(comptime T: type, buf: []u8, f_: FloatDecimal(T), precision: ?usize) FormatError![]const u8 { pub fn formatScientific(comptime T: type, buf: []u8, f_: FloatDecimal(T), precision: ?usize) Error![]const u8 {
std.debug.assert(buf.len >= min_buffer_size); std.debug.assert(buf.len >= min_buffer_size);
var f = f_; var f = f_;
@ -263,7 +263,7 @@ pub fn formatScientific(comptime T: type, buf: []u8, f_: FloatDecimal(T), precis
/// The buffer provided must be greater than `min_buffer_size` bytes in length. If no precision is /// The buffer provided must be greater than `min_buffer_size` bytes in length. If no precision is
/// specified, this may still return an error. If precision is specified, `2 + precision` bytes will /// specified, this may still return an error. If precision is specified, `2 + precision` bytes will
/// always be written. /// always be written.
pub fn formatDecimal(comptime T: type, buf: []u8, f_: FloatDecimal(T), precision: ?usize) FormatError![]const u8 { pub fn formatDecimal(comptime T: type, buf: []u8, f_: FloatDecimal(T), precision: ?usize) Error![]const u8 {
std.debug.assert(buf.len >= min_buffer_size); std.debug.assert(buf.len >= min_buffer_size);
var f = f_; var f = f_;
@ -1520,7 +1520,7 @@ fn check(comptime T: type, value: T, comptime expected: []const u8) !void {
var buf: [6000]u8 = undefined; var buf: [6000]u8 = undefined;
const value_bits: I = @bitCast(value); const value_bits: I = @bitCast(value);
const s = try formatFloat(&buf, value, .{}); const s = try render(&buf, value, .{});
try std.testing.expectEqualStrings(expected, s); try std.testing.expectEqualStrings(expected, s);
if (T == f80 and builtin.target.os.tag == .windows and builtin.target.cpu.arch == .x86_64) return; if (T == f80 and builtin.target.os.tag == .windows and builtin.target.cpu.arch == .x86_64) return;

File diff suppressed because it is too large Load Diff

View File

@ -146,14 +146,11 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {
return out[0 .. out.len - 1 :0]; return out[0 .. out.len - 1 :0];
} }
pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter(formatJoin) { pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter([]const []const u8, formatJoin) {
return .{ .data = paths }; return .{ .data = paths };
} }
fn formatJoin(paths: []const []const u8, comptime fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void { fn formatJoin(paths: []const []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
_ = fmt;
_ = options;
const first_path_idx = for (paths, 0..) |p, idx| { const first_path_idx = for (paths, 0..) |p, idx| {
if (p.len != 0) break idx; if (p.len != 0) break idx;
} else return; } else return;

View File

@ -1798,11 +1798,11 @@ test "walker" {
var num_walked: usize = 0; var num_walked: usize = 0;
while (try walker.next()) |entry| { while (try walker.next()) |entry| {
testing.expect(expected_basenames.has(entry.basename)) catch |err| { testing.expect(expected_basenames.has(entry.basename)) catch |err| {
std.debug.print("found unexpected basename: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.basename)}); std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
return err; return err;
}; };
testing.expect(expected_paths.has(entry.path)) catch |err| { testing.expect(expected_paths.has(entry.path)) catch |err| {
std.debug.print("found unexpected path: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.path)}); std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
return err; return err;
}; };
// make sure that the entry.dir is the containing dir // make sure that the entry.dir is the containing dir
@ -1953,113 +1953,6 @@ test "chown" {
try dir.chown(null, null); try dir.chown(null, null);
} }
test "File.Metadata" {
var tmp = tmpDir(.{});
defer tmp.cleanup();
const file = try tmp.dir.createFile("test_file", .{ .read = true });
defer file.close();
const metadata = try file.metadata();
try testing.expectEqual(File.Kind.file, metadata.kind());
try testing.expectEqual(@as(u64, 0), metadata.size());
_ = metadata.accessed();
_ = metadata.modified();
_ = metadata.created();
}
test "File.Permissions" {
if (native_os == .wasi)
return error.SkipZigTest;
var tmp = tmpDir(.{});
defer tmp.cleanup();
const file = try tmp.dir.createFile("test_file", .{ .read = true });
defer file.close();
const metadata = try file.metadata();
var permissions = metadata.permissions();
try testing.expect(!permissions.readOnly());
permissions.setReadOnly(true);
try testing.expect(permissions.readOnly());
try file.setPermissions(permissions);
const new_permissions = (try file.metadata()).permissions();
try testing.expect(new_permissions.readOnly());
// Must be set to non-read-only to delete
permissions.setReadOnly(false);
try file.setPermissions(permissions);
}
test "File.PermissionsUnix" {
if (native_os == .windows or native_os == .wasi)
return error.SkipZigTest;
var tmp = tmpDir(.{});
defer tmp.cleanup();
const file = try tmp.dir.createFile("test_file", .{ .mode = 0o666, .read = true });
defer file.close();
const metadata = try file.metadata();
var permissions = metadata.permissions();
permissions.setReadOnly(true);
try testing.expect(permissions.readOnly());
try testing.expect(!permissions.inner.unixHas(.user, .write));
permissions.inner.unixSet(.user, .{ .write = true });
try testing.expect(!permissions.readOnly());
try testing.expect(permissions.inner.unixHas(.user, .write));
try testing.expect(permissions.inner.mode & 0o400 != 0);
permissions.setReadOnly(true);
try file.setPermissions(permissions);
permissions = (try file.metadata()).permissions();
try testing.expect(permissions.readOnly());
// Must be set to non-read-only to delete
permissions.setReadOnly(false);
try file.setPermissions(permissions);
const permissions_unix = File.PermissionsUnix.unixNew(0o754);
try testing.expect(permissions_unix.unixHas(.user, .execute));
try testing.expect(!permissions_unix.unixHas(.other, .execute));
}
test "delete a read-only file on windows" {
if (native_os != .windows)
return error.SkipZigTest;
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
const file = try tmp.dir.createFile("test_file", .{ .read = true });
defer file.close();
// Create a file and make it read-only
const metadata = try file.metadata();
var permissions = metadata.permissions();
permissions.setReadOnly(true);
try file.setPermissions(permissions);
// If the OS and filesystem support it, POSIX_SEMANTICS and IGNORE_READONLY_ATTRIBUTE
// is used meaning that the deletion of a read-only file will succeed.
// Otherwise, this delete will fail and the read-only flag must be unset before it's
// able to be deleted.
const delete_result = tmp.dir.deleteFile("test_file");
if (delete_result) {
try testing.expectError(error.FileNotFound, tmp.dir.deleteFile("test_file"));
} else |err| {
try testing.expectEqual(@as(anyerror, error.AccessDenied), err);
// Now make the file not read-only
permissions.setReadOnly(false);
try file.setPermissions(permissions);
try tmp.dir.deleteFile("test_file");
}
}
test "delete a setAsCwd directory on Windows" { test "delete a setAsCwd directory on Windows" {
if (native_os != .windows) return error.SkipZigTest; if (native_os != .windows) return error.SkipZigTest;

View File

@ -346,7 +346,7 @@ fn mode(comptime x: comptime_int) comptime_int {
} }
pub fn main() !void { pub fn main() !void {
const stdout = std.fs.File.stdout().writer(); const stdout = std.fs.File.stdout().deprecatedWriter();
var buffer: [1024]u8 = undefined; var buffer: [1024]u8 = undefined;
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);

View File

@ -436,7 +436,7 @@ pub fn DebugAllocator(comptime config: Config) type {
const stack_trace = bucketStackTrace(bucket, slot_count, slot_index, .alloc); const stack_trace = bucketStackTrace(bucket, slot_count, slot_index, .alloc);
const page_addr = @intFromPtr(bucket) & ~(page_size - 1); const page_addr = @intFromPtr(bucket) & ~(page_size - 1);
const addr = page_addr + slot_index * size_class; const addr = page_addr + slot_index * size_class;
log.err("memory address 0x{x} leaked: {}", .{ addr, stack_trace }); log.err("memory address 0x{x} leaked: {f}", .{ addr, stack_trace });
leaks = true; leaks = true;
} }
} }
@ -463,7 +463,7 @@ pub fn DebugAllocator(comptime config: Config) type {
while (it.next()) |large_alloc| { while (it.next()) |large_alloc| {
if (config.retain_metadata and large_alloc.freed) continue; if (config.retain_metadata and large_alloc.freed) continue;
const stack_trace = large_alloc.getStackTrace(.alloc); const stack_trace = large_alloc.getStackTrace(.alloc);
log.err("memory address 0x{x} leaked: {}", .{ log.err("memory address 0x{x} leaked: {f}", .{
@intFromPtr(large_alloc.bytes.ptr), stack_trace, @intFromPtr(large_alloc.bytes.ptr), stack_trace,
}); });
leaks = true; leaks = true;
@ -522,7 +522,7 @@ pub fn DebugAllocator(comptime config: Config) type {
.index = 0, .index = 0,
}; };
std.debug.captureStackTrace(ret_addr, &second_free_stack_trace); std.debug.captureStackTrace(ret_addr, &second_free_stack_trace);
log.err("Double free detected. Allocation: {} First free: {} Second free: {}", .{ log.err("Double free detected. Allocation: {f} First free: {f} Second free: {f}", .{
alloc_stack_trace, free_stack_trace, second_free_stack_trace, alloc_stack_trace, free_stack_trace, second_free_stack_trace,
}); });
} }
@ -568,7 +568,7 @@ pub fn DebugAllocator(comptime config: Config) type {
.index = 0, .index = 0,
}; };
std.debug.captureStackTrace(ret_addr, &free_stack_trace); std.debug.captureStackTrace(ret_addr, &free_stack_trace);
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {} Free: {}", .{ log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
entry.value_ptr.bytes.len, entry.value_ptr.bytes.len,
old_mem.len, old_mem.len,
entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.alloc),
@ -678,7 +678,7 @@ pub fn DebugAllocator(comptime config: Config) type {
.index = 0, .index = 0,
}; };
std.debug.captureStackTrace(ret_addr, &free_stack_trace); std.debug.captureStackTrace(ret_addr, &free_stack_trace);
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {} Free: {}", .{ log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
entry.value_ptr.bytes.len, entry.value_ptr.bytes.len,
old_mem.len, old_mem.len,
entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.alloc),
@ -907,7 +907,7 @@ pub fn DebugAllocator(comptime config: Config) type {
}; };
std.debug.captureStackTrace(return_address, &free_stack_trace); std.debug.captureStackTrace(return_address, &free_stack_trace);
if (old_memory.len != requested_size) { if (old_memory.len != requested_size) {
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {} Free: {}", .{ log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
requested_size, requested_size,
old_memory.len, old_memory.len,
bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .alloc),
@ -915,7 +915,7 @@ pub fn DebugAllocator(comptime config: Config) type {
}); });
} }
if (alignment != slot_alignment) { if (alignment != slot_alignment) {
log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {} Free: {}", .{ log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{
slot_alignment.toByteUnits(), slot_alignment.toByteUnits(),
alignment.toByteUnits(), alignment.toByteUnits(),
bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .alloc),
@ -1006,7 +1006,7 @@ pub fn DebugAllocator(comptime config: Config) type {
}; };
std.debug.captureStackTrace(return_address, &free_stack_trace); std.debug.captureStackTrace(return_address, &free_stack_trace);
if (memory.len != requested_size) { if (memory.len != requested_size) {
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {} Free: {}", .{ log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
requested_size, requested_size,
memory.len, memory.len,
bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .alloc),
@ -1014,7 +1014,7 @@ pub fn DebugAllocator(comptime config: Config) type {
}); });
} }
if (alignment != slot_alignment) { if (alignment != slot_alignment) {
log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {} Free: {}", .{ log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{
slot_alignment.toByteUnits(), slot_alignment.toByteUnits(),
alignment.toByteUnits(), alignment.toByteUnits(),
bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .alloc),
@ -1054,7 +1054,7 @@ const TraceKind = enum {
free, free,
}; };
const test_config = Config{}; const test_config: Config = .{};
test "small allocations - free in same order" { test "small allocations - free in same order" {
var gpa = DebugAllocator(test_config){}; var gpa = DebugAllocator(test_config){};

View File

@ -1,3 +1,7 @@
const builtin = @import("builtin");
const std = @import("std.zig");
const assert = std.debug.assert;
pub const Client = @import("http/Client.zig"); pub const Client = @import("http/Client.zig");
pub const Server = @import("http/Server.zig"); pub const Server = @import("http/Server.zig");
pub const protocol = @import("http/protocol.zig"); pub const protocol = @import("http/protocol.zig");
@ -38,8 +42,9 @@ pub const Method = enum(u64) {
return x; return x;
} }
pub fn write(self: Method, w: anytype) !void { pub fn format(self: Method, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
const bytes = std.mem.asBytes(&@intFromEnum(self)); comptime assert(f.len == 0);
const bytes: []const u8 = @ptrCast(&@intFromEnum(self));
const str = std.mem.sliceTo(bytes, 0); const str = std.mem.sliceTo(bytes, 0);
try w.writeAll(str); try w.writeAll(str);
} }
@ -77,7 +82,9 @@ pub const Method = enum(u64) {
}; };
} }
/// An HTTP method is idempotent if an identical request can be made once or several times in a row with the same effect while leaving the server in the same state. /// An HTTP method is idempotent if an identical request can be made once
/// or several times in a row with the same effect while leaving the server
/// in the same state.
/// ///
/// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent /// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent
/// ///
@ -90,7 +97,8 @@ pub const Method = enum(u64) {
}; };
} }
/// A cacheable response is an HTTP response that can be cached, that is stored to be retrieved and used later, saving a new request to the server. /// A cacheable response can be stored to be retrieved and used later,
/// saving a new request to the server.
/// ///
/// https://developer.mozilla.org/en-US/docs/Glossary/cacheable /// https://developer.mozilla.org/en-US/docs/Glossary/cacheable
/// ///
@ -282,10 +290,10 @@ pub const Status = enum(u10) {
} }
}; };
/// compression is intentionally omitted here since it is handled in `ContentEncoding`.
pub const TransferEncoding = enum { pub const TransferEncoding = enum {
chunked, chunked,
none, none,
// compression is intentionally omitted here, as std.http.Client stores it as content-encoding
}; };
pub const ContentEncoding = enum { pub const ContentEncoding = enum {
@ -308,9 +316,6 @@ pub const Header = struct {
value: []const u8, value: []const u8,
}; };
const builtin = @import("builtin");
const std = @import("std.zig");
test { test {
if (builtin.os.tag != .wasi) { if (builtin.os.tag != .wasi) {
_ = Client; _ = Client;

View File

@ -823,21 +823,28 @@ pub const Request = struct {
return error.UnsupportedTransferEncoding; return error.UnsupportedTransferEncoding;
const connection = req.connection.?; const connection = req.connection.?;
const w = connection.writer(); var connection_writer_adapter = connection.writer().adaptToNewApi();
const w = &connection_writer_adapter.new_interface;
sendAdapted(req, connection, w) catch |err| switch (err) {
error.WriteFailed => return connection_writer_adapter.err.?,
else => |e| return e,
};
}
try req.method.write(w); fn sendAdapted(req: *Request, connection: *Connection, w: *std.io.Writer) !void {
try req.method.format(w, "");
try w.writeByte(' '); try w.writeByte(' ');
if (req.method == .CONNECT) { if (req.method == .CONNECT) {
try req.uri.writeToStream(.{ .authority = true }, w); try req.uri.writeToStream(w, .{ .authority = true });
} else { } else {
try req.uri.writeToStream(.{ try req.uri.writeToStream(w, .{
.scheme = connection.proxied, .scheme = connection.proxied,
.authentication = connection.proxied, .authentication = connection.proxied,
.authority = connection.proxied, .authority = connection.proxied,
.path = true, .path = true,
.query = true, .query = true,
}, w); });
} }
try w.writeByte(' '); try w.writeByte(' ');
try w.writeAll(@tagName(req.version)); try w.writeAll(@tagName(req.version));
@ -845,7 +852,7 @@ pub const Request = struct {
if (try emitOverridableHeader("host: ", req.headers.host, w)) { if (try emitOverridableHeader("host: ", req.headers.host, w)) {
try w.writeAll("host: "); try w.writeAll("host: ");
try req.uri.writeToStream(.{ .authority = true }, w); try req.uri.writeToStream(w, .{ .authority = true });
try w.writeAll("\r\n"); try w.writeAll("\r\n");
} }
@ -1284,10 +1291,10 @@ pub const basic_authorization = struct {
pub fn valueLengthFromUri(uri: Uri) usize { pub fn valueLengthFromUri(uri: Uri) usize {
var stream = std.io.countingWriter(std.io.null_writer); var stream = std.io.countingWriter(std.io.null_writer);
try stream.writer().print("{user}", .{uri.user orelse Uri.Component.empty}); try stream.writer().print("{fuser}", .{uri.user orelse Uri.Component.empty});
const user_len = stream.bytes_written; const user_len = stream.bytes_written;
stream.bytes_written = 0; stream.bytes_written = 0;
try stream.writer().print("{password}", .{uri.password orelse Uri.Component.empty}); try stream.writer().print("{fpassword}", .{uri.password orelse Uri.Component.empty});
const password_len = stream.bytes_written; const password_len = stream.bytes_written;
return valueLength(@intCast(user_len), @intCast(password_len)); return valueLength(@intCast(user_len), @intCast(password_len));
} }
@ -1295,10 +1302,10 @@ pub const basic_authorization = struct {
pub fn value(uri: Uri, out: []u8) []u8 { pub fn value(uri: Uri, out: []u8) []u8 {
var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; var buf: [max_user_len + ":".len + max_password_len]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf); var stream = std.io.fixedBufferStream(&buf);
stream.writer().print("{user}", .{uri.user orelse Uri.Component.empty}) catch stream.writer().print("{fuser}", .{uri.user orelse Uri.Component.empty}) catch
unreachable; unreachable;
assert(stream.pos <= max_user_len); assert(stream.pos <= max_user_len);
stream.writer().print(":{password}", .{uri.password orelse Uri.Component.empty}) catch stream.writer().print(":{fpassword}", .{uri.password orelse Uri.Component.empty}) catch
unreachable; unreachable;
@memcpy(out[0..prefix.len], prefix); @memcpy(out[0..prefix.len], prefix);

View File

@ -385,10 +385,8 @@ test "general client/server API coverage" {
fn handleRequest(request: *http.Server.Request, listen_port: u16) !void { fn handleRequest(request: *http.Server.Request, listen_port: u16) !void {
const log = std.log.scoped(.server); const log = std.log.scoped(.server);
log.info("{} {s} {s}", .{ log.info("{f} {s} {s}", .{
request.head.method, request.head.method, @tagName(request.head.version), request.head.target,
@tagName(request.head.version),
request.head.target,
}); });
const gpa = std.testing.allocator; const gpa = std.testing.allocator;

View File

@ -364,6 +364,32 @@ pub fn GenericWriter(
const ptr: *const Context = @alignCast(@ptrCast(context)); const ptr: *const Context = @alignCast(@ptrCast(context));
return writeFn(ptr.*, bytes); return writeFn(ptr.*, bytes);
} }
/// Helper for bridging to the new `Writer` API while upgrading.
pub fn adaptToNewApi(self: *const Self) Adapter {
return .{
.derp_writer = self.*,
.new_interface = .{
.buffer = &.{},
.vtable = &.{ .drain = Adapter.drain },
},
};
}
pub const Adapter = struct {
derp_writer: Self,
new_interface: Writer,
err: ?Error = null,
fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
_ = splat;
const a: *@This() = @fieldParentPtr("new_interface", w);
return a.derp_writer.write(data[0]) catch |err| {
a.err = err;
return error.WriteFailed;
};
}
};
}; };
} }
@ -419,7 +445,7 @@ pub const tty = @import("io/tty.zig");
/// A Writer that doesn't write to anything. /// A Writer that doesn't write to anything.
pub const null_writer: NullWriter = .{ .context = {} }; pub const null_writer: NullWriter = .{ .context = {} };
pub const NullWriter = Writer(void, error{}, dummyWrite); pub const NullWriter = GenericWriter(void, error{}, dummyWrite);
fn dummyWrite(context: void, data: []const u8) error{}!usize { fn dummyWrite(context: void, data: []const u8) error{}!usize {
_ = context; _ = context;
return data.len; return data.len;

View File

@ -21,7 +21,7 @@ pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {
} }
pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void { pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void {
return std.fmt.format(self, format, args); return std.fmt.deprecatedFormat(self, format, args);
} }
pub fn writeByte(self: Self, byte: u8) anyerror!void { pub fn writeByte(self: Self, byte: u8) anyerror!void {
@ -81,3 +81,29 @@ pub fn writeFile(self: Self, file: std.fs.File) anyerror!void {
if (n < buf.len) return; if (n < buf.len) return;
} }
} }
/// Helper for bridging to the new `Writer` API while upgrading.
pub fn adaptToNewApi(self: *const Self) Adapter {
return .{
.derp_writer = self.*,
.new_interface = .{
.buffer = &.{},
.vtable = &.{ .drain = Adapter.drain },
},
};
}
pub const Adapter = struct {
derp_writer: Self,
new_interface: std.io.Writer,
err: ?Error = null,
fn drain(w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
_ = splat;
const a: *@This() = @fieldParentPtr("new_interface", w);
return a.derp_writer.write(data[0]) catch |err| {
a.err = err;
return error.WriteFailed;
};
}
};

View File

@ -26,7 +26,8 @@ pub const VTable = struct {
/// Returns the number of bytes written, which will be at minimum `0` and /// Returns the number of bytes written, which will be at minimum `0` and
/// at most `limit`. The number returned, including zero, does not indicate /// at most `limit`. The number returned, including zero, does not indicate
/// end of stream. `limit` is guaranteed to be at least as large as the /// end of stream. `limit` is guaranteed to be at least as large as the
/// buffer capacity of `w`. /// buffer capacity of `w`, a value whose minimum size is determined by the
/// stream implementation.
/// ///
/// The reader's internal logical seek position moves forward in accordance /// The reader's internal logical seek position moves forward in accordance
/// with the number of bytes returned from this function. /// with the number of bytes returned from this function.
@ -1243,10 +1244,10 @@ test peekArray {
test discardAll { test discardAll {
var r: Reader = .fixed("foobar"); var r: Reader = .fixed("foobar");
try r.discard(3); try r.discardAll(3);
try testing.expectEqualStrings("bar", try r.take(3)); try testing.expectEqualStrings("bar", try r.take(3));
try r.discard(0); try r.discardAll(0);
try testing.expectError(error.EndOfStream, r.discard(1)); try testing.expectError(error.EndOfStream, r.discardAll(1));
} }
test discardRemaining { test discardRemaining {
@ -1355,9 +1356,11 @@ test readVec {
test "expected error.EndOfStream" { test "expected error.EndOfStream" {
// Unit test inspired by https://github.com/ziglang/zig/issues/17733 // Unit test inspired by https://github.com/ziglang/zig/issues/17733
var r: std.io.Reader = .fixed(""); var buffer: [3]u8 = undefined;
try std.testing.expectError(error.EndOfStream, r.readEnum(enum(u8) { a, b }, .little)); var r: std.io.Reader = .fixed(&buffer);
try std.testing.expectError(error.EndOfStream, r.isBytes("foo")); r.end = 0; // capacity 3, but empty
try std.testing.expectError(error.EndOfStream, r.takeEnum(enum(u8) { a, b }, .little));
try std.testing.expectError(error.EndOfStream, r.take(3));
} }
fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
@ -1389,21 +1392,30 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
test "readAlloc when the backing reader provides one byte at a time" { test "readAlloc when the backing reader provides one byte at a time" {
const OneByteReader = struct { const OneByteReader = struct {
str: []const u8, str: []const u8,
curr: usize, i: usize,
reader: Reader,
fn read(self: *@This(), dest: []u8) usize { fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
if (self.str.len <= self.curr or dest.len == 0) assert(@intFromEnum(limit) >= 1);
return 0; const self: *@This() = @fieldParentPtr("reader", r);
if (self.str.len - self.i == 0) return error.EndOfStream;
dest[0] = self.str[self.curr]; try w.writeByte(self.str[self.i]);
self.curr += 1; self.i += 1;
return 1; return 1;
} }
}; };
const str = "This is a test"; const str = "This is a test";
var one_byte_stream: OneByteReader = .init(str); var one_byte_stream: OneByteReader = .{
const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1); .str = str,
.i = 0,
.reader = .{
.buffer = &.{},
.vtable = &.{ .stream = OneByteReader.stream },
.seek = 0,
.end = 0,
},
};
const res = try one_byte_stream.reader.allocRemaining(std.testing.allocator, .unlimited);
defer std.testing.allocator.free(res); defer std.testing.allocator.free(res);
try std.testing.expectEqualStrings(str, res); try std.testing.expectEqualStrings(str, res);
} }

View File

@ -37,6 +37,10 @@ pub const VTable = struct {
/// The last element of `data` is repeated as necessary so that it is /// The last element of `data` is repeated as necessary so that it is
/// written `splat` number of times, which may be zero. /// written `splat` number of times, which may be zero.
/// ///
/// This function may not be called if the data to be written could have
/// been stored in `buffer` instead, including when the amount of data to
/// be written is zero and the buffer capacity is zero.
///
/// Number of bytes consumed from `data` is returned, excluding bytes from /// Number of bytes consumed from `data` is returned, excluding bytes from
/// `buffer`. /// `buffer`.
/// ///
@ -800,19 +804,14 @@ pub fn printValue(
) Error!void { ) Error!void {
const T = @TypeOf(value); const T = @TypeOf(value);
if (comptime std.mem.eql(u8, fmt, "*")) { if (comptime std.mem.eql(u8, fmt, "*")) return w.printAddress(value);
return w.printAddress(value); if (fmt.len > 0 and fmt[0] == 'f') return value.format(w, fmt[1..]);
}
const is_any = comptime std.mem.eql(u8, fmt, ANY); const is_any = comptime std.mem.eql(u8, fmt, ANY);
if (!is_any and std.meta.hasMethod(T, "format")) { if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) {
if (fmt.len > 0 and fmt[0] == 'f') { // after 0.15.0 is tagged, delete this compile error and its condition
return value.format(w, fmt[1..]);
} else if (fmt.len == 0) {
// after 0.15.0 is tagged, delete the hasMethod condition and this compile error
@compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it"); @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
} }
}
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value), .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value),
@ -952,9 +951,8 @@ pub fn printValue(
}, },
.pointer => |ptr_info| switch (ptr_info.size) { .pointer => |ptr_info| switch (ptr_info.size) {
.one => switch (@typeInfo(ptr_info.child)) { .one => switch (@typeInfo(ptr_info.child)) {
.array, .@"enum", .@"union", .@"struct" => { .array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth),
return w.printValue(fmt, options, value.*, max_depth); .@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth),
},
else => { else => {
var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
try w.writeVecAll(&buffers); try w.writeVecAll(&buffers);
@ -1120,7 +1118,12 @@ pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error
pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void { pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void {
var buf: [4]u8 = undefined; var buf: [4]u8 = undefined;
const len = try std.unicode.utf8Encode(c, &buf); const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) {
error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: {
buf[0..3].* = std.unicode.replacement_character_utf8;
break :l 3;
},
};
return w.alignBufferOptions(buf[0..len], options); return w.alignBufferOptions(buf[0..len], options);
} }
@ -1553,13 +1556,7 @@ test "formatValue max_depth" {
x: f32, x: f32,
y: f32, y: f32,
pub fn format( pub fn format(self: SelfType, w: *Writer, comptime fmt: []const u8) Error!void {
self: SelfType,
comptime fmt: []const u8,
options: std.fmt.Options,
w: *Writer,
) Error!void {
_ = options;
if (fmt.len == 0) { if (fmt.len == 0) {
return w.print("({d:.3},{d:.3})", .{ self.x, self.y }); return w.print("({d:.3},{d:.3})", .{ self.x, self.y });
} else { } else {
@ -1600,131 +1597,131 @@ test "formatValue max_depth" {
try w.printValue("", .{}, inst, 0); try w.printValue("", .{}, inst, 0);
try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ ... }", w.buffered()); try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ ... }", w.buffered());
w.reset(); w = .fixed(&buf);
try w.printValue("", .{}, inst, 1); try w.printValue("", .{}, inst, 1);
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()); 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());
w.reset(); w = .fixed(&buf);
try w.printValue("", .{}, inst, 2); try w.printValue("", .{}, inst, 2);
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()); 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());
w.reset(); w = .fixed(&buf);
try w.printValue("", .{}, inst, 3); try w.printValue("", .{}, inst, 3);
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()); 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());
const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 }; const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 };
w.reset(); w = .fixed(&buf);
try w.printValue("", .{}, vec, 0); try w.printValue("", .{}, vec, 0);
try testing.expectEqualStrings("{ ... }", w.buffered()); try testing.expectEqualStrings("{ ... }", w.buffered());
w.reset(); w = .fixed(&buf);
try w.printValue("", .{}, vec, 1); try w.printValue("", .{}, vec, 1);
try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered()); try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());
} }
test printDuration { test printDuration {
testDurationCase("0ns", 0); try testDurationCase("0ns", 0);
testDurationCase("1ns", 1); try testDurationCase("1ns", 1);
testDurationCase("999ns", std.time.ns_per_us - 1); try testDurationCase("999ns", std.time.ns_per_us - 1);
testDurationCase("1us", std.time.ns_per_us); try testDurationCase("1us", std.time.ns_per_us);
testDurationCase("1.45us", 1450); try testDurationCase("1.45us", 1450);
testDurationCase("1.5us", 3 * std.time.ns_per_us / 2); try testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);
testDurationCase("14.5us", 14500); try testDurationCase("14.5us", 14500);
testDurationCase("145us", 145000); try testDurationCase("145us", 145000);
testDurationCase("999.999us", std.time.ns_per_ms - 1); try testDurationCase("999.999us", std.time.ns_per_ms - 1);
testDurationCase("1ms", std.time.ns_per_ms + 1); try testDurationCase("1ms", std.time.ns_per_ms + 1);
testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2); try testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);
testDurationCase("1.11ms", 1110000); try testDurationCase("1.11ms", 1110000);
testDurationCase("1.111ms", 1111000); try testDurationCase("1.111ms", 1111000);
testDurationCase("1.111ms", 1111100); try testDurationCase("1.111ms", 1111100);
testDurationCase("999.999ms", std.time.ns_per_s - 1); try testDurationCase("999.999ms", std.time.ns_per_s - 1);
testDurationCase("1s", std.time.ns_per_s); try testDurationCase("1s", std.time.ns_per_s);
testDurationCase("59.999s", std.time.ns_per_min - 1); try testDurationCase("59.999s", std.time.ns_per_min - 1);
testDurationCase("1m", std.time.ns_per_min); try testDurationCase("1m", std.time.ns_per_min);
testDurationCase("1h", std.time.ns_per_hour); try testDurationCase("1h", std.time.ns_per_hour);
testDurationCase("1d", std.time.ns_per_day); try testDurationCase("1d", std.time.ns_per_day);
testDurationCase("1w", std.time.ns_per_week); try testDurationCase("1w", std.time.ns_per_week);
testDurationCase("1y", 365 * std.time.ns_per_day); try testDurationCase("1y", 365 * std.time.ns_per_day);
testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1 try testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1
testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); try testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); try testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); try testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); try testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64)); try testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));
testing.expectFmt("=======0ns", "{D:=>10}", .{0}); try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
testing.expectFmt("1ns=======", "{D:=<10}", .{1}); try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1}); try testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});
} }
test printDurationSigned { test printDurationSigned {
testDurationCaseSigned("0ns", 0); try testDurationCaseSigned("0ns", 0);
testDurationCaseSigned("1ns", 1); try testDurationCaseSigned("1ns", 1);
testDurationCaseSigned("-1ns", -(1)); try testDurationCaseSigned("-1ns", -(1));
testDurationCaseSigned("999ns", std.time.ns_per_us - 1); try testDurationCaseSigned("999ns", std.time.ns_per_us - 1);
testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1)); try testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));
testDurationCaseSigned("1us", std.time.ns_per_us); try testDurationCaseSigned("1us", std.time.ns_per_us);
testDurationCaseSigned("-1us", -(std.time.ns_per_us)); try testDurationCaseSigned("-1us", -(std.time.ns_per_us));
testDurationCaseSigned("1.45us", 1450); try testDurationCaseSigned("1.45us", 1450);
testDurationCaseSigned("-1.45us", -(1450)); try testDurationCaseSigned("-1.45us", -(1450));
testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2); try testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);
testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2)); try testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));
testDurationCaseSigned("14.5us", 14500); try testDurationCaseSigned("14.5us", 14500);
testDurationCaseSigned("-14.5us", -(14500)); try testDurationCaseSigned("-14.5us", -(14500));
testDurationCaseSigned("145us", 145000); try testDurationCaseSigned("145us", 145000);
testDurationCaseSigned("-145us", -(145000)); try testDurationCaseSigned("-145us", -(145000));
testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1); try testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);
testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1)); try testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));
testDurationCaseSigned("1ms", std.time.ns_per_ms + 1); try testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);
testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1)); try testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));
testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2); try testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);
testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2)); try testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));
testDurationCaseSigned("1.11ms", 1110000); try testDurationCaseSigned("1.11ms", 1110000);
testDurationCaseSigned("-1.11ms", -(1110000)); try testDurationCaseSigned("-1.11ms", -(1110000));
testDurationCaseSigned("1.111ms", 1111000); try testDurationCaseSigned("1.111ms", 1111000);
testDurationCaseSigned("-1.111ms", -(1111000)); try testDurationCaseSigned("-1.111ms", -(1111000));
testDurationCaseSigned("1.111ms", 1111100); try testDurationCaseSigned("1.111ms", 1111100);
testDurationCaseSigned("-1.111ms", -(1111100)); try testDurationCaseSigned("-1.111ms", -(1111100));
testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1); try testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);
testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1)); try testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));
testDurationCaseSigned("1s", std.time.ns_per_s); try testDurationCaseSigned("1s", std.time.ns_per_s);
testDurationCaseSigned("-1s", -(std.time.ns_per_s)); try testDurationCaseSigned("-1s", -(std.time.ns_per_s));
testDurationCaseSigned("59.999s", std.time.ns_per_min - 1); try testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);
testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1)); try testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));
testDurationCaseSigned("1m", std.time.ns_per_min); try testDurationCaseSigned("1m", std.time.ns_per_min);
testDurationCaseSigned("-1m", -(std.time.ns_per_min)); try testDurationCaseSigned("-1m", -(std.time.ns_per_min));
testDurationCaseSigned("1h", std.time.ns_per_hour); try testDurationCaseSigned("1h", std.time.ns_per_hour);
testDurationCaseSigned("-1h", -(std.time.ns_per_hour)); try testDurationCaseSigned("-1h", -(std.time.ns_per_hour));
testDurationCaseSigned("1d", std.time.ns_per_day); try testDurationCaseSigned("1d", std.time.ns_per_day);
testDurationCaseSigned("-1d", -(std.time.ns_per_day)); try testDurationCaseSigned("-1d", -(std.time.ns_per_day));
testDurationCaseSigned("1w", std.time.ns_per_week); try testDurationCaseSigned("1w", std.time.ns_per_week);
testDurationCaseSigned("-1w", -(std.time.ns_per_week)); try testDurationCaseSigned("-1w", -(std.time.ns_per_week));
testDurationCaseSigned("1y", 365 * std.time.ns_per_day); try testDurationCaseSigned("1y", 365 * std.time.ns_per_day);
testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day)); try testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));
testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d try testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d try testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d
testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); try testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms)); try testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms));
testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); try testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us)); try testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us));
testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); try testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1)); try testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1));
testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms)); try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms));
testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1)); try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1));
testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); try testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999)); try testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64)); try testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));
testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1); try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64)); try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));
testing.expectFmt("=======0ns", "{s:=>10}", .{0}); try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
testing.expectFmt("1ns=======", "{s:=<10}", .{1}); try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
testing.expectFmt("-1ns======", "{s:=<10}", .{-(1)}); try testing.expectFmt("-1ns======", "{D:=<10}", .{-(1)});
testing.expectFmt(" -999ns ", "{s:^10}", .{-(std.time.ns_per_us - 1)}); try testing.expectFmt(" -999ns ", "{D:^10}", .{-(std.time.ns_per_us - 1)});
} }
fn testDurationCase(expected: []const u8, input: u64) !void { fn testDurationCase(expected: []const u8, input: u64) !void {
@ -1762,7 +1759,7 @@ test printIntOptions {
test "printInt with comptime_int" { test "printInt with comptime_int" {
var buf: [20]u8 = undefined; var buf: [20]u8 = undefined;
var w: Writer = .fixed(&buf); var w: Writer = .fixed(&buf);
try w.printInt(@as(comptime_int, 123456789123456789), "", .{}); try w.printInt("", .{}, @as(comptime_int, 123456789123456789));
try std.testing.expectEqualStrings("123456789123456789", w.buffered()); try std.testing.expectEqualStrings("123456789123456789", w.buffered());
} }
@ -1777,7 +1774,7 @@ test "printFloat with comptime_float" {
fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void { fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {
var buffer: [100]u8 = undefined; var buffer: [100]u8 = undefined;
var w: Writer = .fixed(&buffer); var w: Writer = .fixed(&buffer);
w.printIntOptions(value, base, case, options); try w.printIntOptions(value, base, case, options);
try testing.expectEqualStrings(expected, w.buffered()); try testing.expectEqualStrings(expected, w.buffered());
} }
@ -1832,17 +1829,15 @@ test "fixed output" {
try w.writeAll("world"); try w.writeAll("world");
try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld")); try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
try testing.expectError(error.WriteStreamEnd, w.writeAll("!")); try testing.expectError(error.WriteFailed, w.writeAll("!"));
try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld")); try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
w.reset(); w = .fixed(&buffer);
try testing.expect(w.buffered().len == 0); try testing.expect(w.buffered().len == 0);
try testing.expectError(error.WriteStreamEnd, w.writeAll("Hello world!")); try testing.expectError(error.WriteFailed, w.writeAll("Hello world!"));
try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl")); try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl"));
try w.seekTo((try w.getEndPos()) + 1);
try testing.expectError(error.WriteStreamEnd, w.writeAll("H"));
} }
pub fn failingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { pub fn failingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {

View File

@ -33,7 +33,7 @@ pub const BufferedAtomicFile = struct {
self.atomic_file = try dir.atomicFile(dest_path, atomic_file_options); self.atomic_file = try dir.atomicFile(dest_path, atomic_file_options);
errdefer self.atomic_file.deinit(); errdefer self.atomic_file.deinit();
self.file_writer = self.atomic_file.file.writer(); self.file_writer = self.atomic_file.file.deprecatedWriter();
self.buffered_writer = .{ .unbuffered_writer = self.file_writer }; self.buffered_writer = .{ .unbuffered_writer = self.file_writer };
return self; return self;
} }

View File

@ -24,7 +24,7 @@ test "write a file, read it, then delete it" {
var file = try tmp.dir.createFile(tmp_file_name, .{}); var file = try tmp.dir.createFile(tmp_file_name, .{});
defer file.close(); defer file.close();
var buf_stream = io.bufferedWriter(file.writer()); var buf_stream = io.bufferedWriter(file.deprecatedWriter());
const st = buf_stream.writer(); const st = buf_stream.writer();
try st.print("begin", .{}); try st.print("begin", .{});
try st.writeAll(data[0..]); try st.writeAll(data[0..]);
@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {
const expected_file_size: u64 = "begin".len + data.len + "end".len; const expected_file_size: u64 = "begin".len + data.len + "end".len;
try expectEqual(expected_file_size, file_size); try expectEqual(expected_file_size, file_size);
var buf_stream = io.bufferedReader(file.reader()); var buf_stream = io.bufferedReader(file.deprecatedReader());
const st = buf_stream.reader(); const st = buf_stream.reader();
const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
defer std.testing.allocator.free(contents); defer std.testing.allocator.free(contents);
@ -66,7 +66,7 @@ test "BitStreams with File Stream" {
var file = try tmp.dir.createFile(tmp_file_name, .{}); var file = try tmp.dir.createFile(tmp_file_name, .{});
defer file.close(); defer file.close();
var bit_stream = io.bitWriter(native_endian, file.writer()); var bit_stream = io.bitWriter(native_endian, file.deprecatedWriter());
try bit_stream.writeBits(@as(u2, 1), 1); try bit_stream.writeBits(@as(u2, 1), 1);
try bit_stream.writeBits(@as(u5, 2), 2); try bit_stream.writeBits(@as(u5, 2), 2);
@ -80,7 +80,7 @@ test "BitStreams with File Stream" {
var file = try tmp.dir.openFile(tmp_file_name, .{}); var file = try tmp.dir.openFile(tmp_file_name, .{});
defer file.close(); defer file.close();
var bit_stream = io.bitReader(native_endian, file.reader()); var bit_stream = io.bitReader(native_endian, file.deprecatedReader());
var out_bits: u16 = undefined; var out_bits: u16 = undefined;

View File

@ -56,7 +56,7 @@ pub const Value = union(enum) {
std.debug.lockStdErr(); std.debug.lockStdErr();
defer std.debug.unlockStdErr(); defer std.debug.unlockStdErr();
const stderr = std.fs.File.stderr().writer(); const stderr = std.fs.File.stderr().deprecatedWriter();
stringify(self, .{}, stderr) catch return; stringify(self, .{}, stderr) catch return;
} }

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("../std.zig");
const assert = std.debug.assert;
const stringify = @import("stringify.zig").stringify; const stringify = @import("stringify.zig").stringify;
const StringifyOptions = @import("stringify.zig").StringifyOptions; const StringifyOptions = @import("stringify.zig").StringifyOptions;
@ -14,14 +15,8 @@ pub fn Formatter(comptime T: type) type {
value: T, value: T,
options: StringifyOptions, options: StringifyOptions,
pub fn format( pub fn format(self: @This(), writer: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
self: @This(), comptime assert(f.len == 0);
comptime fmt_spec: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt_spec;
_ = options;
try stringify(self.value, self.options, writer); try stringify(self.value, self.options, writer);
} }
}; };

View File

@ -689,7 +689,8 @@ fn outputUnicodeEscape(codepoint: u21, out_stream: anytype) !void {
// then it may be represented as a six-character sequence: a reverse solidus, followed // then it may be represented as a six-character sequence: a reverse solidus, followed
// by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point.
try out_stream.writeAll("\\u"); try out_stream.writeAll("\\u");
try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); //try w.printInt("x", .{ .width = 4, .fill = '0' }, codepoint);
try std.fmt.deprecatedFormat(out_stream, "{x:0>4}", .{codepoint});
} else { } else {
assert(codepoint <= 0x10FFFF); assert(codepoint <= 0x10FFFF);
// To escape an extended character that is not in the Basic Multilingual Plane, // To escape an extended character that is not in the Basic Multilingual Plane,
@ -697,9 +698,11 @@ fn outputUnicodeEscape(codepoint: u21, out_stream: anytype) !void {
const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800; const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00; const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;
try out_stream.writeAll("\\u"); try out_stream.writeAll("\\u");
try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); //try w.printInt("x", .{ .width = 4, .fill = '0' }, high);
try std.fmt.deprecatedFormat(out_stream, "{x:0>4}", .{high});
try out_stream.writeAll("\\u"); try out_stream.writeAll("\\u");
try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); //try w.printInt("x", .{ .width = 4, .fill = '0' }, low);
try std.fmt.deprecatedFormat(out_stream, "{x:0>4}", .{low});
} }
} }

View File

@ -47,7 +47,7 @@
//! // Print the message to stderr, silently ignoring any errors //! // Print the message to stderr, silently ignoring any errors
//! std.debug.lockStdErr(); //! std.debug.lockStdErr();
//! defer std.debug.unlockStdErr(); //! defer std.debug.unlockStdErr();
//! const stderr = std.fs.File.stderr().writer(); //! const stderr = std.fs.File.stderr().deprecatedWriter();
//! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return; //! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
//! } //! }
//! //!
@ -148,7 +148,7 @@ pub fn defaultLog(
) void { ) void {
const level_txt = comptime message_level.asText(); const level_txt = comptime message_level.asText();
const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
const stderr = std.fs.File.stderr().writer(); const stderr = std.fs.File.stderr().deprecatedWriter();
var bw = std.io.bufferedWriter(stderr); var bw = std.io.bufferedWriter(stderr);
const writer = bw.writer(); const writer = bw.writer();

View File

@ -2322,13 +2322,7 @@ pub const Const = struct {
/// this function will fail to print the string, printing "(BigInt)" instead of a number. /// this function will fail to print the string, printing "(BigInt)" instead of a number.
/// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
/// See `toString` and `toStringAlloc` for a way to print big integers without failure. /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
pub fn format( pub fn format(self: Const, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
self: Const,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
_ = options;
comptime var base = 10; comptime var base = 10;
comptime var case: std.fmt.Case = .lower; comptime var case: std.fmt.Case = .lower;
@ -2350,7 +2344,7 @@ pub const Const = struct {
const available_len = 64; const available_len = 64;
if (self.limbs.len > available_len) if (self.limbs.len > available_len)
return out_stream.writeAll("(BigInt)"); return w.writeAll("(BigInt)");
var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined; var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined;
@ -2360,7 +2354,7 @@ pub const Const = struct {
}; };
var buf: [biggest.sizeInBaseUpperBound(base)]u8 = undefined; var buf: [biggest.sizeInBaseUpperBound(base)]u8 = undefined;
const len = self.toString(&buf, base, case, &limbs); const len = self.toString(&buf, base, case, &limbs);
return out_stream.writeAll(buf[0..len]); return w.writeAll(buf[0..len]);
} }
/// Converts self to a string in the requested base. /// Converts self to a string in the requested base.
@ -2934,13 +2928,8 @@ pub const Managed = struct {
/// this function will fail to print the string, printing "(BigInt)" instead of a number. /// this function will fail to print the string, printing "(BigInt)" instead of a number.
/// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
/// See `toString` and `toStringAlloc` for a way to print big integers without failure. /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
pub fn format( pub fn format(self: Managed, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
self: Managed, return self.toConst().format(w, f);
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
return self.toConst().format(fmt, options, out_stream);
} }
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==

View File

@ -3813,10 +3813,10 @@ test "(BigInt) positive" {
try a.pow(&a, 64 * @sizeOf(Limb) * 8); try a.pow(&a, 64 * @sizeOf(Limb) * 8);
try b.sub(&a, &c); try b.sub(&a, &c);
const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a}); const a_fmt = try std.fmt.allocPrintSentinel(testing.allocator, "{fd}", .{a}, 0);
defer testing.allocator.free(a_fmt); defer testing.allocator.free(a_fmt);
const b_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{b}); const b_fmt = try std.fmt.allocPrintSentinel(testing.allocator, "{fd}", .{b}, 0);
defer testing.allocator.free(b_fmt); defer testing.allocator.free(b_fmt);
try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
@ -3838,10 +3838,10 @@ test "(BigInt) negative" {
a.negate(); a.negate();
try b.add(&a, &c); try b.add(&a, &c);
const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a}); const a_fmt = try std.fmt.allocPrintSentinel(testing.allocator, "{fd}", .{a}, 0);
defer testing.allocator.free(a_fmt); defer testing.allocator.free(a_fmt);
const b_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{b}); const b_fmt = try std.fmt.allocPrintSentinel(testing.allocator, "{fd}", .{b}, 0);
defer testing.allocator.free(b_fmt); defer testing.allocator.free(b_fmt);
try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));

View File

@ -991,6 +991,7 @@ test "0 sized struct" {
test "struct with many fields" { test "struct with many fields" {
const ManyFields = struct { const ManyFields = struct {
fn Type(count: comptime_int) type { fn Type(count: comptime_int) type {
@setEvalBranchQuota(50000);
var fields: [count]std.builtin.Type.StructField = undefined; var fields: [count]std.builtin.Type.StructField = undefined;
for (0..count) |i| { for (0..count) |i| {
fields[i] = .{ fields[i] = .{

View File

@ -161,22 +161,14 @@ pub const Address = extern union {
} }
} }
pub fn format( pub fn format(self: Address, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
self: Address, comptime assert(fmt.len == 0);
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
switch (self.any.family) { switch (self.any.family) {
posix.AF.INET => try self.in.format(fmt, options, out_stream), posix.AF.INET => try self.in.format(w, fmt),
posix.AF.INET6 => try self.in6.format(fmt, options, out_stream), posix.AF.INET6 => try self.in6.format(w, fmt),
posix.AF.UNIX => { posix.AF.UNIX => {
if (!has_unix_sockets) { if (!has_unix_sockets) unreachable;
unreachable; try w.writeAll(std.mem.sliceTo(&self.un.path, 0));
}
try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)});
}, },
else => unreachable, else => unreachable,
} }
@ -349,22 +341,10 @@ pub const Ip4Address = extern struct {
self.sa.port = mem.nativeToBig(u16, port); self.sa.port = mem.nativeToBig(u16, port);
} }
pub fn format( pub fn format(self: Ip4Address, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
self: Ip4Address, comptime assert(fmt.len == 0);
comptime fmt: []const u8, const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);
options: std.fmt.FormatOptions, try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });
out_stream: anytype,
) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
_ = options;
const bytes = @as(*const [4]u8, @ptrCast(&self.sa.addr));
try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
bytes[0],
bytes[1],
bytes[2],
bytes[3],
self.getPort(),
});
} }
pub fn getOsSockLen(self: Ip4Address) posix.socklen_t { pub fn getOsSockLen(self: Ip4Address) posix.socklen_t {
@ -653,17 +633,11 @@ pub const Ip6Address = extern struct {
self.sa.port = mem.nativeToBig(u16, port); self.sa.port = mem.nativeToBig(u16, port);
} }
pub fn format( pub fn format(self: Ip6Address, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
self: Ip6Address, comptime assert(fmt.len == 0);
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
_ = options;
const port = mem.bigToNative(u16, self.sa.port); const port = mem.bigToNative(u16, self.sa.port);
if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
self.sa.addr[12], self.sa.addr[12],
self.sa.addr[13], self.sa.addr[13],
self.sa.addr[14], self.sa.addr[14],
@ -711,14 +685,14 @@ pub const Ip6Address = extern struct {
longest_len = 0; longest_len = 0;
} }
try out_stream.writeAll("["); try w.writeAll("[");
var i: usize = 0; var i: usize = 0;
var abbrv = false; var abbrv = false;
while (i < native_endian_parts.len) : (i += 1) { while (i < native_endian_parts.len) : (i += 1) {
if (i == longest_start) { if (i == longest_start) {
// Emit "::" for the longest zero run // Emit "::" for the longest zero run
if (!abbrv) { if (!abbrv) {
try out_stream.writeAll(if (i == 0) "::" else ":"); try w.writeAll(if (i == 0) "::" else ":");
abbrv = true; abbrv = true;
} }
i += longest_len - 1; // Skip the compressed range i += longest_len - 1; // Skip the compressed range
@ -727,12 +701,12 @@ pub const Ip6Address = extern struct {
if (abbrv) { if (abbrv) {
abbrv = false; abbrv = false;
} }
try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]}); try w.print("{x}", .{native_endian_parts[i]});
if (i != native_endian_parts.len - 1) { if (i != native_endian_parts.len - 1) {
try out_stream.writeAll(":"); try w.writeAll(":");
} }
} }
try std.fmt.format(out_stream, "]:{}", .{port}); try w.print("]:{}", .{port});
} }
pub fn getOsSockLen(self: Ip6Address) posix.socklen_t { pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {
@ -894,7 +868,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
const name_c = try allocator.dupeZ(u8, name); const name_c = try allocator.dupeZ(u8, name);
defer allocator.free(name_c); defer allocator.free(name_c);
const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); const port_c = try std.fmt.allocPrintSentinel(allocator, "{}", .{port}, 0);
defer allocator.free(port_c); defer allocator.free(port_c);
const ws2_32 = windows.ws2_32; const ws2_32 = windows.ws2_32;
@ -966,7 +940,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
const name_c = try allocator.dupeZ(u8, name); const name_c = try allocator.dupeZ(u8, name);
defer allocator.free(name_c); defer allocator.free(name_c);
const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); const port_c = try std.fmt.allocPrintSentinel(allocator, "{}", .{port}, 0);
defer allocator.free(port_c); defer allocator.free(port_c);
const hints: posix.addrinfo = .{ const hints: posix.addrinfo = .{
@ -1356,7 +1330,7 @@ fn linuxLookupNameFromHosts(
}; };
defer file.close(); defer file.close();
var buffered_reader = std.io.bufferedReader(file.reader()); var buffered_reader = std.io.bufferedReader(file.deprecatedReader());
const reader = buffered_reader.reader(); const reader = buffered_reader.reader();
var line_buf: [512]u8 = undefined; var line_buf: [512]u8 = undefined;
while (reader.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { while (reader.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
@ -1557,7 +1531,7 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
}; };
defer file.close(); defer file.close();
var buf_reader = std.io.bufferedReader(file.reader()); var buf_reader = std.io.bufferedReader(file.deprecatedReader());
const stream = buf_reader.reader(); const stream = buf_reader.reader();
var line_buf: [512]u8 = undefined; var line_buf: [512]u8 = undefined;
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {

View File

@ -7,18 +7,12 @@ const testing = std.testing;
test "parse and render IP addresses at comptime" { test "parse and render IP addresses at comptime" {
if (builtin.os.tag == .wasi) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest;
comptime { comptime {
var ipAddrBuffer: [16]u8 = undefined;
// Parses IPv6 at comptime
const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable; const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable;
var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable; try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr});
try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3]));
// Parses IPv4 at comptime
const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable; const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable;
var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable; try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr});
try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2]));
// Returns error for invalid IP addresses at comptime
try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0)); try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0));
try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0)); try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0));
try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("::123.123.123.123", 0)); try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("::123.123.123.123", 0));
@ -28,13 +22,8 @@ test "parse and render IP addresses at comptime" {
test "format IPv6 address with no zero runs" { test "format IPv6 address with no zero runs" {
if (builtin.os.tag == .wasi) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest;
const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0); const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0);
try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr});
var buffer: [50]u8 = undefined;
const result = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;
try std.testing.expectEqualStrings("[2001:db8:1:2:3:4:5:6]:0", result);
} }
test "parse IPv6 addresses and check compressed form" { test "parse IPv6 addresses and check compressed form" {
@ -111,12 +100,12 @@ test "parse and render IPv6 addresses" {
}; };
for (ips, 0..) |ip, i| { for (ips, 0..) |ip, i| {
const addr = net.Address.parseIp6(ip, 0) catch unreachable; const addr = net.Address.parseIp6(ip, 0) catch unreachable;
var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
if (builtin.os.tag == .linux) { if (builtin.os.tag == .linux) {
const addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable; const addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable;
var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr_via_resolve}) catch unreachable; var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr_via_resolve}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3])); try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3]));
} }
} }
@ -159,7 +148,7 @@ test "parse and render IPv4 addresses" {
"127.0.0.1", "127.0.0.1",
}) |ip| { }) |ip| {
const addr = net.Address.parseIp4(ip, 0) catch unreachable; const addr = net.Address.parseIp4(ip, 0) catch unreachable;
var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
} }
@ -175,10 +164,8 @@ test "parse and render UNIX addresses" {
if (builtin.os.tag == .wasi) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest;
if (!net.has_unix_sockets) return error.SkipZigTest; if (!net.has_unix_sockets) return error.SkipZigTest;
var buffer: [14]u8 = undefined;
const addr = net.Address.initUnix("/tmp/testpath") catch unreachable; const addr = net.Address.initUnix("/tmp/testpath") catch unreachable;
const fmt_addr = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; try std.testing.expectFmt("/tmp/testpath", "{f}", .{addr});
try std.testing.expectEqualSlices(u8, "/tmp/testpath", fmt_addr);
const too_long = [_]u8{'a'} ** 200; const too_long = [_]u8{'a'} ** 200;
try testing.expectError(error.NameTooLong, net.Address.initUnix(too_long[0..])); try testing.expectError(error.NameTooLong, net.Address.initUnix(too_long[0..]));

View File

@ -31,6 +31,7 @@ pub const uefi = @import("os/uefi.zig");
pub const wasi = @import("os/wasi.zig"); pub const wasi = @import("os/wasi.zig");
pub const emscripten = @import("os/emscripten.zig"); pub const emscripten = @import("os/emscripten.zig");
pub const windows = @import("os/windows.zig"); pub const windows = @import("os/windows.zig");
pub const freebsd = @import("os/freebsd.zig");
test { test {
_ = linux; _ = linux;

49
lib/std/os/freebsd.zig Normal file
View File

@ -0,0 +1,49 @@
const std = @import("../std.zig");
const fd_t = std.c.fd_t;
const off_t = std.c.off_t;
const unexpectedErrno = std.posix.unexpectedErrno;
const errno = std.posix.errno;
pub const CopyFileRangeError = error{
/// If infd is not open for reading or outfd is not open for writing, or
/// opened for writing with O_APPEND, or if infd and outfd refer to the
/// same file.
BadFileFlags,
/// If the copy exceeds the process's file size limit or the maximum
/// file size for the file system outfd re- sides on.
FileTooBig,
/// A signal interrupted the system call before it could be completed.
/// This may happen for files on some NFS mounts. When this happens,
/// the values pointed to by inoffp and outoffp are reset to the
/// initial values for the system call.
Interrupted,
/// One of:
/// * infd and outfd refer to the same file and the byte ranges overlap.
/// * The flags argument is not zero.
/// * Either infd or outfd refers to a file object that is not a regular file.
InvalidArguments,
/// An I/O error occurred while reading/writing the files.
InputOutput,
/// Corrupted data was detected while reading from a file system.
CorruptedData,
/// Either infd or outfd refers to a directory.
IsDir,
/// File system that stores outfd is full.
NoSpaceLeft,
};
pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: u32) CopyFileRangeError!usize {
const rc = std.c.copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.BADF => return error.BadFileFlags,
.FBIG => return error.FileTooBig,
.INTR => return error.Interrupted,
.INVAL => return error.InvalidArguments,
.IO => return error.InputOutput,
.INTEGRITY => return error.CorruptedData,
.ISDIR => return error.IsDir,
.NOSPC => return error.NoSpaceLeft,
else => |err| return unexpectedErrno(err),
}
}

View File

@ -9421,3 +9421,131 @@ pub const msghdr_const = extern struct {
controllen: usize, controllen: usize,
flags: u32, flags: u32,
}; };
/// The syscalls, but with Zig error sets, going through libc if linking libc,
/// and with some footguns eliminated.
pub const wrapped = struct {
pub const lfs64_abi = builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid());
const system = if (builtin.link_libc) std.c else std.os.linux;
pub const SendfileError = std.posix.UnexpectedError || error{
/// `out_fd` is an unconnected socket, or out_fd closed its read end.
BrokenPipe,
/// Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd.
UnsupportedOperation,
/// Nonblocking I/O has been selected but the write would block.
WouldBlock,
/// Unspecified error while reading from in_fd.
InputOutput,
/// Insufficient kernel memory to read from in_fd.
SystemResources,
/// `offset` is not `null` but the input file is not seekable.
Unseekable,
};
pub fn sendfile(
out_fd: fd_t,
in_fd: fd_t,
in_offset: ?*off_t,
in_len: usize,
) SendfileError!usize {
const adjusted_len = @min(in_len, 0x7ffff000); // Prevents EOVERFLOW.
const sendfileSymbol = if (lfs64_abi) system.sendfile64 else system.sendfile;
const rc = sendfileSymbol(out_fd, in_fd, in_offset, adjusted_len);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.BADF => return invalidApiUsage(), // Always a race condition.
.FAULT => return invalidApiUsage(), // Segmentation fault.
.OVERFLOW => return unexpectedErrno(.OVERFLOW), // We avoid passing too large of a `count`.
.NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
.INVAL => return error.UnsupportedOperation,
.AGAIN => return error.WouldBlock,
.IO => return error.InputOutput,
.PIPE => return error.BrokenPipe,
.NOMEM => return error.SystemResources,
.NXIO => return error.Unseekable,
.SPIPE => return error.Unseekable,
else => |err| return unexpectedErrno(err),
}
}
pub const CopyFileRangeError = std.posix.UnexpectedError || error{
/// One of:
/// * One or more file descriptors are not valid.
/// * fd_in is not open for reading; or fd_out is not open for writing.
/// * The O_APPEND flag is set for the open file description referred
/// to by the file descriptor fd_out.
BadFileFlags,
/// One of:
/// * An attempt was made to write at a position past the maximum file
/// offset the kernel supports.
/// * An attempt was made to write a range that exceeds the allowed
/// maximum file size. The maximum file size differs between
/// filesystem implementations and can be different from the maximum
/// allowed file offset.
/// * An attempt was made to write beyond the process's file size
/// resource limit. This may also result in the process receiving a
/// SIGXFSZ signal.
FileTooBig,
/// One of:
/// * either fd_in or fd_out is not a regular file
/// * flags argument is not zero
/// * fd_in and fd_out refer to the same file and the source and target ranges overlap.
InvalidArguments,
/// A low-level I/O error occurred while copying.
InputOutput,
/// Either fd_in or fd_out refers to a directory.
IsDir,
OutOfMemory,
/// There is not enough space on the target filesystem to complete the copy.
NoSpaceLeft,
/// (since Linux 5.19) the filesystem does not support this operation.
OperationNotSupported,
/// The requested source or destination range is too large to represent
/// in the specified data types.
Overflow,
/// fd_out refers to an immutable file.
PermissionDenied,
/// Either fd_in or fd_out refers to an active swap file.
SwapFile,
/// The files referred to by fd_in and fd_out are not on the same
/// filesystem, and the source and target filesystems are not of the
/// same type, or do not support cross-filesystem copy.
NotSameFileSystem,
};
pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: u32) CopyFileRangeError!usize {
const rc = system.copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.BADF => return error.BadFileFlags,
.FBIG => return error.FileTooBig,
.INVAL => return error.InvalidArguments,
.IO => return error.InputOutput,
.ISDIR => return error.IsDir,
.NOMEM => return error.OutOfMemory,
.NOSPC => return error.NoSpaceLeft,
.OPNOTSUPP => return error.OperationNotSupported,
.OVERFLOW => return error.Overflow,
.PERM => return error.PermissionDenied,
.TXTBSY => return error.SwapFile,
.XDEV => return error.NotSameFileSystem,
else => |err| return unexpectedErrno(err),
}
}
const unexpectedErrno = std.posix.unexpectedErrno;
fn invalidApiUsage() error{Unexpected} {
if (builtin.mode == .Debug) @panic("invalid API usage");
return error.Unexpected;
}
fn errno(rc: anytype) E {
if (builtin.link_libc) {
return if (rc == -1) @enumFromInt(std.c._errno().*) else .SUCCESS;
} else {
return errnoFromSyscall(rc);
}
}
};

View File

@ -1,4 +1,5 @@
const std = @import("../std.zig"); const std = @import("../std.zig");
const assert = std.debug.assert;
/// A protocol is an interface identified by a GUID. /// A protocol is an interface identified by a GUID.
pub const protocol = @import("uefi/protocol.zig"); pub const protocol = @import("uefi/protocol.zig");
@ -59,31 +60,21 @@ pub const Guid = extern struct {
node: [6]u8, node: [6]u8,
/// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
pub fn format( pub fn format(self: @This(), writer: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void {
self: @This(), comptime assert(f.len == 0);
comptime f: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
if (f.len == 0) {
const fmt = std.fmt.fmtSliceHexLower;
const time_low = @byteSwap(self.time_low); const time_low = @byteSwap(self.time_low);
const time_mid = @byteSwap(self.time_mid); const time_mid = @byteSwap(self.time_mid);
const time_high_and_version = @byteSwap(self.time_high_and_version); const time_high_and_version = @byteSwap(self.time_high_and_version);
return std.fmt.format(writer, "{:0>8}-{:0>4}-{:0>4}-{:0>2}{:0>2}-{:0>12}", .{ return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
fmt(std.mem.asBytes(&time_low)), std.mem.asBytes(&time_low),
fmt(std.mem.asBytes(&time_mid)), std.mem.asBytes(&time_mid),
fmt(std.mem.asBytes(&time_high_and_version)), std.mem.asBytes(&time_high_and_version),
fmt(std.mem.asBytes(&self.clock_seq_high_and_reserved)), std.mem.asBytes(&self.clock_seq_high_and_reserved),
fmt(std.mem.asBytes(&self.clock_seq_low)), std.mem.asBytes(&self.clock_seq_low),
fmt(std.mem.asBytes(&self.node)), std.mem.asBytes(&self.node),
}); });
} else {
std.fmt.invalidFmtError(f, self);
}
} }
pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool { pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool {

View File

@ -79,30 +79,6 @@ pub const File = extern struct {
VolumeFull, VolumeFull,
}; };
pub const SeekableStream = io.SeekableStream(
*File,
SeekError,
SeekError,
setPosition,
seekBy,
getPosition,
getEndPos,
);
pub const Reader = io.GenericReader(*File, ReadError, read);
pub const Writer = io.GenericWriter(*File, WriteError, write);
pub fn seekableStream(self: *File) SeekableStream {
return .{ .context = self };
}
pub fn reader(self: *File) Reader {
return .{ .context = self };
}
pub fn writer(self: *File) Writer {
return .{ .context = self };
}
pub fn open( pub fn open(
self: *const File, self: *const File,
file_name: [*:0]const u16, file_name: [*:0]const u16,

View File

@ -1690,40 +1690,6 @@ pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.so
return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen))); return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen)));
} }
pub fn sendmsg(
s: ws2_32.SOCKET,
msg: *ws2_32.WSAMSG_const,
flags: u32,
) i32 {
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_send)));
}
}
pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) };
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendTo(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_send, flags, to, @as(i32, @intCast(to_len)), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_send)));
}
}
pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf };
var bytes_received: DWORD = undefined;
var flags_inout = flags;
if (ws2_32.WSARecvFrom(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_received, &flags_inout, from, @as(?*i32, @ptrCast(from_len)), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_received)));
}
}
pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 { pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
return ws2_32.WSAPoll(fds, n, timeout); return ws2_32.WSAPoll(fds, n, timeout);
} }

View File

@ -1829,7 +1829,7 @@ pub extern "ws2_32" fn sendto(
buf: [*]const u8, buf: [*]const u8,
len: i32, len: i32,
flags: i32, flags: i32,
to: *const sockaddr, to: ?*const sockaddr,
tolen: i32, tolen: i32,
) callconv(.winapi) i32; ) callconv(.winapi) i32;
@ -2116,14 +2116,6 @@ pub extern "ws2_32" fn WSASendMsg(
lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE, lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
) callconv(.winapi) i32; ) callconv(.winapi) i32;
pub extern "ws2_32" fn WSARecvMsg(
s: SOCKET,
lpMsg: *WSAMSG,
lpdwNumberOfBytesRecv: ?*u32,
lpOverlapped: ?*OVERLAPPED,
lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
) callconv(.winapi) i32;
pub extern "ws2_32" fn WSASendDisconnect( pub extern "ws2_32" fn WSASendDisconnect(
s: SOCKET, s: SOCKET,
lpOutboundDisconnectData: ?*WSABUF, lpOutboundDisconnectData: ?*WSABUF,

View File

@ -651,7 +651,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
} }
const file: fs.File = .{ .handle = fd }; const file: fs.File = .{ .handle = fd };
const stream = file.reader(); const stream = file.deprecatedReader();
stream.readNoEof(buf) catch return error.Unexpected; stream.readNoEof(buf) catch return error.Unexpected;
} }

View File

@ -667,7 +667,7 @@ test "mmap" {
const file = try tmp.dir.createFile(test_out_file, .{}); const file = try tmp.dir.createFile(test_out_file, .{});
defer file.close(); defer file.close();
const stream = file.writer(); const stream = file.deprecatedWriter();
var i: u32 = 0; var i: u32 = 0;
while (i < alloc_size / @sizeOf(u32)) : (i += 1) { while (i < alloc_size / @sizeOf(u32)) : (i += 1) {

View File

@ -1553,7 +1553,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); const file = try std.fs.openFileAbsolute("/etc/passwd", .{});
defer file.close(); defer file.close();
const reader = file.reader(); const reader = file.deprecatedReader();
const State = enum { const State = enum {
Start, Start,
@ -1895,7 +1895,7 @@ pub fn createEnvironFromMap(
var i: usize = 0; var i: usize = 0;
if (zig_progress_action == .add) { if (zig_progress_action == .add) {
envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
i += 1; i += 1;
} }
@ -1906,16 +1906,16 @@ pub fn createEnvironFromMap(
.add => unreachable, .add => unreachable,
.delete => continue, .delete => continue,
.edit => { .edit => {
envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={d}", .{ envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{
pair.key_ptr.*, options.zig_progress_fd.?, pair.key_ptr.*, options.zig_progress_fd.?,
}); }, 0);
i += 1; i += 1;
continue; continue;
}, },
.nothing => {}, .nothing => {},
}; };
envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }); envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0);
i += 1; i += 1;
} }
} }
@ -1965,7 +1965,7 @@ pub fn createEnvironFromExisting(
var existing_index: usize = 0; var existing_index: usize = 0;
if (zig_progress_action == .add) { if (zig_progress_action == .add) {
envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
i += 1; i += 1;
} }
@ -1974,7 +1974,7 @@ pub fn createEnvironFromExisting(
.add => unreachable, .add => unreachable,
.delete => continue, .delete => continue,
.edit => { .edit => {
envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
i += 1; i += 1;
continue; continue;
}, },

View File

@ -1004,12 +1004,12 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
fn writeIntFd(fd: i32, value: ErrInt) !void { fn writeIntFd(fd: i32, value: ErrInt) !void {
const file: File = .{ .handle = fd }; const file: File = .{ .handle = fd };
file.writer().writeInt(u64, @intCast(value), .little) catch return error.SystemResources; file.deprecatedWriter().writeInt(u64, @intCast(value), .little) catch return error.SystemResources;
} }
fn readIntFd(fd: i32) !ErrInt { fn readIntFd(fd: i32) !ErrInt {
const file: File = .{ .handle = fd }; const file: File = .{ .handle = fd };
return @intCast(file.reader().readInt(u64, .little) catch return error.SystemResources); return @intCast(file.deprecatedReader().readInt(u64, .little) catch return error.SystemResources);
} }
const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);

View File

@ -105,7 +105,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
.error_set, .error_set,
=> { => {
if (actual != expected) { if (actual != expected) {
print("expected {}, found {}\n", .{ expected, actual }); print("expected {any}, found {any}\n", .{ expected, actual });
return error.TestExpectedEqual; return error.TestExpectedEqual;
} }
}, },
@ -267,9 +267,13 @@ test "expectEqual null" {
/// This function is intended to be used only in tests. When the formatted result of the template /// This function is intended to be used only in tests. When the formatted result of the template
/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how /// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how
/// they are not equal, then returns an error. It depends on `expectEqualStrings()` for printing /// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing
/// diagnostics. /// diagnostics.
pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void { pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
if (@inComptime()) {
var buffer: [std.fmt.count(template, args)]u8 = undefined;
return expectEqualStrings(expected, try std.fmt.bufPrint(&buffer, template, args));
}
const actual = try std.fmt.allocPrint(allocator, template, args); const actual = try std.fmt.allocPrint(allocator, template, args);
defer allocator.free(actual); defer allocator.free(actual);
return expectEqualStrings(expected, actual); return expectEqualStrings(expected, actual);
@ -415,7 +419,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
print("... truncated ...\n", .{}); print("... truncated ...\n", .{});
} }
} }
differ.write(stderr.writer()) catch {}; differ.write(stderr.deprecatedWriter()) catch {};
if (expected_truncated) { if (expected_truncated) {
const end_offset = window_start + expected_window.len; const end_offset = window_start + expected_window.len;
const num_missing_items = expected.len - (window_start + expected_window.len); const num_missing_items = expected.len - (window_start + expected_window.len);
@ -437,7 +441,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
print("... truncated ...\n", .{}); print("... truncated ...\n", .{});
} }
} }
differ.write(stderr.writer()) catch {}; differ.write(stderr.deprecatedWriter()) catch {};
if (actual_truncated) { if (actual_truncated) {
const end_offset = window_start + actual_window.len; const end_offset = window_start + actual_window.len;
const num_missing_items = actual.len - (window_start + actual_window.len); const num_missing_items = actual.len - (window_start + actual_window.len);
@ -637,6 +641,11 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir {
pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void { pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| { if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {
if (@inComptime()) {
@compileError(std.fmt.comptimePrint("\nexpected:\n{s}\nfound:\n{s}\ndifference starts at index {d}", .{
expected, actual, diff_index,
}));
}
print("\n====== expected this output: =========\n", .{}); print("\n====== expected this output: =========\n", .{});
printWithVisibleNewlines(expected); printWithVisibleNewlines(expected);
print("\n======== instead found this: =========\n", .{}); print("\n======== instead found this: =========\n", .{});
@ -1108,7 +1117,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
const arg_i_str = comptime str: { const arg_i_str = comptime str: {
var str_buf: [100]u8 = undefined; var str_buf: [100]u8 = undefined;
const args_i = i + 1; const args_i = i + 1;
const str_len = std.fmt.formatIntBuf(&str_buf, args_i, 10, .lower, .{}); const str_len = std.fmt.printInt(&str_buf, args_i, 10, .lower, .{});
break :str str_buf[0..str_len]; break :str str_buf[0..str_len];
}; };
@field(args, arg_i_str) = @field(extra_args, field.name); @field(args, arg_i_str) = @field(extra_args, field.name);
@ -1138,7 +1147,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
error.OutOfMemory => { error.OutOfMemory => {
if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {
print( print(
"\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {}", "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}",
.{ .{
fail_index, fail_index,
needed_alloc_count, needed_alloc_count,

Some files were not shown because too many files have changed in this diff Show More