mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-29 12:27:25 +03:00
Merge remote-tracking branch 'origin/master' into zig-ast-to-zir
This commit is contained in:
@@ -162,7 +162,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
mem.copy(T, self.items[oldlen..], items);
|
||||
}
|
||||
|
||||
pub usingnamespace if (T != u8) struct { } else struct {
|
||||
pub usingnamespace if (T != u8) struct {} else struct {
|
||||
pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
|
||||
|
||||
/// Initializes a Writer which will append to the list.
|
||||
|
||||
@@ -2559,3 +2559,10 @@ pub const InstalledFile = struct {
|
||||
dir: InstallDir,
|
||||
path: []const u8,
|
||||
};
|
||||
|
||||
test "" {
|
||||
// The only purpose of this test is to get all these untested functions
|
||||
// to be referenced to avoid regression so it is okay to skip some targets.
|
||||
if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64)
|
||||
std.meta.refAllDecls(@This());
|
||||
}
|
||||
|
||||
@@ -215,3 +215,7 @@ pub const InstallRawStep = struct {
|
||||
try emitRaw(builder.allocator, full_src_path, full_dest_path);
|
||||
}
|
||||
};
|
||||
|
||||
test "" {
|
||||
std.meta.refAllDecls(InstallRawStep);
|
||||
}
|
||||
|
||||
+17
-10
@@ -166,7 +166,7 @@ pub const TypeInfo = union(enum) {
|
||||
Fn: Fn,
|
||||
BoundFn: Fn,
|
||||
Opaque: void,
|
||||
Frame: void,
|
||||
Frame: Frame,
|
||||
AnyFrame: AnyFrame,
|
||||
Vector: Vector,
|
||||
EnumLiteral: void,
|
||||
@@ -244,8 +244,8 @@ pub const TypeInfo = union(enum) {
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const Struct = struct {
|
||||
layout: ContainerLayout,
|
||||
fields: []StructField,
|
||||
decls: []Declaration,
|
||||
fields: []const StructField,
|
||||
decls: []const Declaration,
|
||||
};
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
@@ -265,12 +265,13 @@ pub const TypeInfo = union(enum) {
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const Error = struct {
|
||||
name: []const u8,
|
||||
/// This field is ignored when using @Type().
|
||||
value: comptime_int,
|
||||
};
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const ErrorSet = ?[]Error;
|
||||
pub const ErrorSet = ?[]const Error;
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
@@ -284,8 +285,8 @@ pub const TypeInfo = union(enum) {
|
||||
pub const Enum = struct {
|
||||
layout: ContainerLayout,
|
||||
tag_type: type,
|
||||
fields: []EnumField,
|
||||
decls: []Declaration,
|
||||
fields: []const EnumField,
|
||||
decls: []const Declaration,
|
||||
is_exhaustive: bool,
|
||||
};
|
||||
|
||||
@@ -302,8 +303,8 @@ pub const TypeInfo = union(enum) {
|
||||
pub const Union = struct {
|
||||
layout: ContainerLayout,
|
||||
tag_type: ?type,
|
||||
fields: []UnionField,
|
||||
decls: []Declaration,
|
||||
fields: []const UnionField,
|
||||
decls: []const Declaration,
|
||||
};
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
@@ -321,7 +322,13 @@ pub const TypeInfo = union(enum) {
|
||||
is_generic: bool,
|
||||
is_var_args: bool,
|
||||
return_type: ?type,
|
||||
args: []FnArg,
|
||||
args: []const FnArg,
|
||||
};
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const Frame = struct {
|
||||
function: var,
|
||||
};
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
@@ -361,7 +368,7 @@ pub const TypeInfo = union(enum) {
|
||||
is_export: bool,
|
||||
lib_name: ?[]const u8,
|
||||
return_type: type,
|
||||
arg_names: [][]const u8,
|
||||
arg_names: []const []const u8,
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
|
||||
@@ -102,6 +102,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
|
||||
pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
|
||||
pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
|
||||
pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int;
|
||||
pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn chdir(path: [*:0]const u8) c_int;
|
||||
|
||||
+50
-50
@@ -278,62 +278,62 @@ pub const Token = struct {
|
||||
|
||||
// TODO extensions
|
||||
pub const keywords = std.ComptimeStringMap(Id, .{
|
||||
.{"auto", .Keyword_auto},
|
||||
.{"break", .Keyword_break},
|
||||
.{"case", .Keyword_case},
|
||||
.{"char", .Keyword_char},
|
||||
.{"const", .Keyword_const},
|
||||
.{"continue", .Keyword_continue},
|
||||
.{"default", .Keyword_default},
|
||||
.{"do", .Keyword_do},
|
||||
.{"double", .Keyword_double},
|
||||
.{"else", .Keyword_else},
|
||||
.{"enum", .Keyword_enum},
|
||||
.{"extern", .Keyword_extern},
|
||||
.{"float", .Keyword_float},
|
||||
.{"for", .Keyword_for},
|
||||
.{"goto", .Keyword_goto},
|
||||
.{"if", .Keyword_if},
|
||||
.{"int", .Keyword_int},
|
||||
.{"long", .Keyword_long},
|
||||
.{"register", .Keyword_register},
|
||||
.{"return", .Keyword_return},
|
||||
.{"short", .Keyword_short},
|
||||
.{"signed", .Keyword_signed},
|
||||
.{"sizeof", .Keyword_sizeof},
|
||||
.{"static", .Keyword_static},
|
||||
.{"struct", .Keyword_struct},
|
||||
.{"switch", .Keyword_switch},
|
||||
.{"typedef", .Keyword_typedef},
|
||||
.{"union", .Keyword_union},
|
||||
.{"unsigned", .Keyword_unsigned},
|
||||
.{"void", .Keyword_void},
|
||||
.{"volatile", .Keyword_volatile},
|
||||
.{"while", .Keyword_while},
|
||||
.{ "auto", .Keyword_auto },
|
||||
.{ "break", .Keyword_break },
|
||||
.{ "case", .Keyword_case },
|
||||
.{ "char", .Keyword_char },
|
||||
.{ "const", .Keyword_const },
|
||||
.{ "continue", .Keyword_continue },
|
||||
.{ "default", .Keyword_default },
|
||||
.{ "do", .Keyword_do },
|
||||
.{ "double", .Keyword_double },
|
||||
.{ "else", .Keyword_else },
|
||||
.{ "enum", .Keyword_enum },
|
||||
.{ "extern", .Keyword_extern },
|
||||
.{ "float", .Keyword_float },
|
||||
.{ "for", .Keyword_for },
|
||||
.{ "goto", .Keyword_goto },
|
||||
.{ "if", .Keyword_if },
|
||||
.{ "int", .Keyword_int },
|
||||
.{ "long", .Keyword_long },
|
||||
.{ "register", .Keyword_register },
|
||||
.{ "return", .Keyword_return },
|
||||
.{ "short", .Keyword_short },
|
||||
.{ "signed", .Keyword_signed },
|
||||
.{ "sizeof", .Keyword_sizeof },
|
||||
.{ "static", .Keyword_static },
|
||||
.{ "struct", .Keyword_struct },
|
||||
.{ "switch", .Keyword_switch },
|
||||
.{ "typedef", .Keyword_typedef },
|
||||
.{ "union", .Keyword_union },
|
||||
.{ "unsigned", .Keyword_unsigned },
|
||||
.{ "void", .Keyword_void },
|
||||
.{ "volatile", .Keyword_volatile },
|
||||
.{ "while", .Keyword_while },
|
||||
|
||||
// ISO C99
|
||||
.{"_Bool", .Keyword_bool},
|
||||
.{"_Complex", .Keyword_complex},
|
||||
.{"_Imaginary", .Keyword_imaginary},
|
||||
.{"inline", .Keyword_inline},
|
||||
.{"restrict", .Keyword_restrict},
|
||||
.{ "_Bool", .Keyword_bool },
|
||||
.{ "_Complex", .Keyword_complex },
|
||||
.{ "_Imaginary", .Keyword_imaginary },
|
||||
.{ "inline", .Keyword_inline },
|
||||
.{ "restrict", .Keyword_restrict },
|
||||
|
||||
// ISO C11
|
||||
.{"_Alignas", .Keyword_alignas},
|
||||
.{"_Alignof", .Keyword_alignof},
|
||||
.{"_Atomic", .Keyword_atomic},
|
||||
.{"_Generic", .Keyword_generic},
|
||||
.{"_Noreturn", .Keyword_noreturn},
|
||||
.{"_Static_assert", .Keyword_static_assert},
|
||||
.{"_Thread_local", .Keyword_thread_local},
|
||||
.{ "_Alignas", .Keyword_alignas },
|
||||
.{ "_Alignof", .Keyword_alignof },
|
||||
.{ "_Atomic", .Keyword_atomic },
|
||||
.{ "_Generic", .Keyword_generic },
|
||||
.{ "_Noreturn", .Keyword_noreturn },
|
||||
.{ "_Static_assert", .Keyword_static_assert },
|
||||
.{ "_Thread_local", .Keyword_thread_local },
|
||||
|
||||
// Preprocessor directives
|
||||
.{"include", .Keyword_include},
|
||||
.{"define", .Keyword_define},
|
||||
.{"ifdef", .Keyword_ifdef},
|
||||
.{"ifndef", .Keyword_ifndef},
|
||||
.{"error", .Keyword_error},
|
||||
.{"pragma", .Keyword_pragma},
|
||||
.{ "include", .Keyword_include },
|
||||
.{ "define", .Keyword_define },
|
||||
.{ "ifdef", .Keyword_ifdef },
|
||||
.{ "ifndef", .Keyword_ifndef },
|
||||
.{ "error", .Keyword_error },
|
||||
.{ "pragma", .Keyword_pragma },
|
||||
});
|
||||
|
||||
// TODO do this in the preprocessor
|
||||
|
||||
+7
-3
@@ -52,9 +52,13 @@ pub const LineInfo = struct {
|
||||
|
||||
var stderr_mutex = std.Mutex.init();
|
||||
|
||||
/// Tries to write to stderr, unbuffered, and ignores any error returned.
|
||||
/// Does not append a newline.
|
||||
pub fn warn(comptime fmt: []const u8, args: var) void {
|
||||
/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
|
||||
/// "printf debugging".
|
||||
pub const warn = print;
|
||||
|
||||
/// Print to stderr, unbuffered, and silently returning on failure. Intended
|
||||
/// for use in "printf debugging." Use `std.log` functions for proper logging.
|
||||
pub fn print(comptime fmt: []const u8, args: var) void {
|
||||
const held = stderr_mutex.acquire();
|
||||
defer held.release();
|
||||
const stderr = io.getStdErr().writer();
|
||||
|
||||
+175
-175
@@ -69,14 +69,14 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
|
||||
///
|
||||
/// If a formatted user type contains a function of the type
|
||||
/// ```
|
||||
/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void
|
||||
/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: var) !void
|
||||
/// ```
|
||||
/// with `?` being the type formatted, this function will be called instead of the default implementation.
|
||||
/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
|
||||
///
|
||||
/// A user type may be a `struct`, `vector`, `union` or `enum` type.
|
||||
pub fn format(
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
comptime fmt: []const u8,
|
||||
args: var,
|
||||
) !void {
|
||||
@@ -136,7 +136,7 @@ pub fn format(
|
||||
.Start => switch (c) {
|
||||
'{' => {
|
||||
if (start_index < i) {
|
||||
try out_stream.writeAll(fmt[start_index..i]);
|
||||
try writer.writeAll(fmt[start_index..i]);
|
||||
}
|
||||
|
||||
start_index = i;
|
||||
@@ -148,7 +148,7 @@ pub fn format(
|
||||
},
|
||||
'}' => {
|
||||
if (start_index < i) {
|
||||
try out_stream.writeAll(fmt[start_index..i]);
|
||||
try writer.writeAll(fmt[start_index..i]);
|
||||
}
|
||||
state = .CloseBrace;
|
||||
},
|
||||
@@ -183,7 +183,7 @@ pub fn format(
|
||||
args[arg_to_print],
|
||||
fmt[0..0],
|
||||
options,
|
||||
out_stream,
|
||||
writer,
|
||||
default_max_depth,
|
||||
);
|
||||
|
||||
@@ -214,7 +214,7 @@ pub fn format(
|
||||
args[arg_to_print],
|
||||
fmt[specifier_start..i],
|
||||
options,
|
||||
out_stream,
|
||||
writer,
|
||||
default_max_depth,
|
||||
);
|
||||
state = .Start;
|
||||
@@ -259,7 +259,7 @@ pub fn format(
|
||||
args[arg_to_print],
|
||||
fmt[specifier_start..specifier_end],
|
||||
options,
|
||||
out_stream,
|
||||
writer,
|
||||
default_max_depth,
|
||||
);
|
||||
state = .Start;
|
||||
@@ -285,7 +285,7 @@ pub fn format(
|
||||
args[arg_to_print],
|
||||
fmt[specifier_start..specifier_end],
|
||||
options,
|
||||
out_stream,
|
||||
writer,
|
||||
default_max_depth,
|
||||
);
|
||||
state = .Start;
|
||||
@@ -306,7 +306,7 @@ pub fn format(
|
||||
}
|
||||
}
|
||||
if (start_index < fmt.len) {
|
||||
try out_stream.writeAll(fmt[start_index..]);
|
||||
try writer.writeAll(fmt[start_index..]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,140 +314,140 @@ pub fn formatType(
|
||||
value: var,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
max_depth: usize,
|
||||
) @TypeOf(out_stream).Error!void {
|
||||
) @TypeOf(writer).Error!void {
|
||||
if (comptime std.mem.eql(u8, fmt, "*")) {
|
||||
try out_stream.writeAll(@typeName(@TypeOf(value).Child));
|
||||
try out_stream.writeAll("@");
|
||||
try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream);
|
||||
try writer.writeAll(@typeName(@TypeOf(value).Child));
|
||||
try writer.writeAll("@");
|
||||
try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
|
||||
return;
|
||||
}
|
||||
|
||||
const T = @TypeOf(value);
|
||||
if (comptime std.meta.trait.hasFn("format")(T)) {
|
||||
return try value.format(fmt, options, out_stream);
|
||||
return try value.format(fmt, options, writer);
|
||||
}
|
||||
|
||||
switch (@typeInfo(T)) {
|
||||
.ComptimeInt, .Int, .ComptimeFloat, .Float => {
|
||||
return formatValue(value, fmt, options, out_stream);
|
||||
return formatValue(value, fmt, options, writer);
|
||||
},
|
||||
.Void => {
|
||||
return formatBuf("void", options, out_stream);
|
||||
return formatBuf("void", options, writer);
|
||||
},
|
||||
.Bool => {
|
||||
return formatBuf(if (value) "true" else "false", options, out_stream);
|
||||
return formatBuf(if (value) "true" else "false", options, writer);
|
||||
},
|
||||
.Optional => {
|
||||
if (value) |payload| {
|
||||
return formatType(payload, fmt, options, out_stream, max_depth);
|
||||
return formatType(payload, fmt, options, writer, max_depth);
|
||||
} else {
|
||||
return formatBuf("null", options, out_stream);
|
||||
return formatBuf("null", options, writer);
|
||||
}
|
||||
},
|
||||
.ErrorUnion => {
|
||||
if (value) |payload| {
|
||||
return formatType(payload, fmt, options, out_stream, max_depth);
|
||||
return formatType(payload, fmt, options, writer, max_depth);
|
||||
} else |err| {
|
||||
return formatType(err, fmt, options, out_stream, max_depth);
|
||||
return formatType(err, fmt, options, writer, max_depth);
|
||||
}
|
||||
},
|
||||
.ErrorSet => {
|
||||
try out_stream.writeAll("error.");
|
||||
return out_stream.writeAll(@errorName(value));
|
||||
try writer.writeAll("error.");
|
||||
return writer.writeAll(@errorName(value));
|
||||
},
|
||||
.Enum => |enumInfo| {
|
||||
try out_stream.writeAll(@typeName(T));
|
||||
try writer.writeAll(@typeName(T));
|
||||
if (enumInfo.is_exhaustive) {
|
||||
try out_stream.writeAll(".");
|
||||
try out_stream.writeAll(@tagName(value));
|
||||
try writer.writeAll(".");
|
||||
try writer.writeAll(@tagName(value));
|
||||
return;
|
||||
}
|
||||
|
||||
// Use @tagName only if value is one of known fields
|
||||
inline for (enumInfo.fields) |enumField| {
|
||||
if (@enumToInt(value) == enumField.value) {
|
||||
try out_stream.writeAll(".");
|
||||
try out_stream.writeAll(@tagName(value));
|
||||
try writer.writeAll(".");
|
||||
try writer.writeAll(@tagName(value));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try out_stream.writeAll("(");
|
||||
try formatType(@enumToInt(value), fmt, options, out_stream, max_depth);
|
||||
try out_stream.writeAll(")");
|
||||
try writer.writeAll("(");
|
||||
try formatType(@enumToInt(value), fmt, options, writer, max_depth);
|
||||
try writer.writeAll(")");
|
||||
},
|
||||
.Union => {
|
||||
try out_stream.writeAll(@typeName(T));
|
||||
try writer.writeAll(@typeName(T));
|
||||
if (max_depth == 0) {
|
||||
return out_stream.writeAll("{ ... }");
|
||||
return writer.writeAll("{ ... }");
|
||||
}
|
||||
const info = @typeInfo(T).Union;
|
||||
if (info.tag_type) |UnionTagType| {
|
||||
try out_stream.writeAll("{ .");
|
||||
try out_stream.writeAll(@tagName(@as(UnionTagType, value)));
|
||||
try out_stream.writeAll(" = ");
|
||||
try writer.writeAll("{ .");
|
||||
try writer.writeAll(@tagName(@as(UnionTagType, value)));
|
||||
try writer.writeAll(" = ");
|
||||
inline for (info.fields) |u_field| {
|
||||
if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
|
||||
try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1);
|
||||
try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
|
||||
}
|
||||
}
|
||||
try out_stream.writeAll(" }");
|
||||
try writer.writeAll(" }");
|
||||
} else {
|
||||
try format(out_stream, "@{x}", .{@ptrToInt(&value)});
|
||||
try format(writer, "@{x}", .{@ptrToInt(&value)});
|
||||
}
|
||||
},
|
||||
.Struct => |StructT| {
|
||||
try out_stream.writeAll(@typeName(T));
|
||||
try writer.writeAll(@typeName(T));
|
||||
if (max_depth == 0) {
|
||||
return out_stream.writeAll("{ ... }");
|
||||
return writer.writeAll("{ ... }");
|
||||
}
|
||||
try out_stream.writeAll("{");
|
||||
try writer.writeAll("{");
|
||||
inline for (StructT.fields) |f, i| {
|
||||
if (i == 0) {
|
||||
try out_stream.writeAll(" .");
|
||||
try writer.writeAll(" .");
|
||||
} else {
|
||||
try out_stream.writeAll(", .");
|
||||
try writer.writeAll(", .");
|
||||
}
|
||||
try out_stream.writeAll(f.name);
|
||||
try out_stream.writeAll(" = ");
|
||||
try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1);
|
||||
try writer.writeAll(f.name);
|
||||
try writer.writeAll(" = ");
|
||||
try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1);
|
||||
}
|
||||
try out_stream.writeAll(" }");
|
||||
try writer.writeAll(" }");
|
||||
},
|
||||
.Pointer => |ptr_info| switch (ptr_info.size) {
|
||||
.One => switch (@typeInfo(ptr_info.child)) {
|
||||
.Array => |info| {
|
||||
if (info.child == u8) {
|
||||
return formatText(value, fmt, options, out_stream);
|
||||
return formatText(value, fmt, options, writer);
|
||||
}
|
||||
return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
|
||||
return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
|
||||
},
|
||||
.Enum, .Union, .Struct => {
|
||||
return formatType(value.*, fmt, options, out_stream, max_depth);
|
||||
return formatType(value.*, fmt, options, writer, max_depth);
|
||||
},
|
||||
else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
|
||||
else => return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
|
||||
},
|
||||
.Many, .C => {
|
||||
if (ptr_info.sentinel) |sentinel| {
|
||||
return formatType(mem.span(value), fmt, options, out_stream, max_depth);
|
||||
return formatType(mem.span(value), fmt, options, writer, max_depth);
|
||||
}
|
||||
if (ptr_info.child == u8) {
|
||||
if (fmt.len > 0 and fmt[0] == 's') {
|
||||
return formatText(mem.span(value), fmt, options, out_stream);
|
||||
return formatText(mem.span(value), fmt, options, writer);
|
||||
}
|
||||
}
|
||||
return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
|
||||
return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
|
||||
},
|
||||
.Slice => {
|
||||
if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
|
||||
return formatText(value, fmt, options, out_stream);
|
||||
return formatText(value, fmt, options, writer);
|
||||
}
|
||||
if (ptr_info.child == u8) {
|
||||
return formatText(value, fmt, options, out_stream);
|
||||
return formatText(value, fmt, options, writer);
|
||||
}
|
||||
return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
|
||||
return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
|
||||
},
|
||||
},
|
||||
.Array => |info| {
|
||||
@@ -462,27 +462,27 @@ pub fn formatType(
|
||||
.sentinel = null,
|
||||
},
|
||||
});
|
||||
return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth);
|
||||
return formatType(@as(Slice, &value), fmt, options, writer, max_depth);
|
||||
},
|
||||
.Vector => {
|
||||
const len = @typeInfo(T).Vector.len;
|
||||
try out_stream.writeAll("{ ");
|
||||
try writer.writeAll("{ ");
|
||||
var i: usize = 0;
|
||||
while (i < len) : (i += 1) {
|
||||
try formatValue(value[i], fmt, options, out_stream);
|
||||
try formatValue(value[i], fmt, options, writer);
|
||||
if (i < len - 1) {
|
||||
try out_stream.writeAll(", ");
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
}
|
||||
try out_stream.writeAll(" }");
|
||||
try writer.writeAll(" }");
|
||||
},
|
||||
.Fn => {
|
||||
return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
|
||||
return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
|
||||
},
|
||||
.Type => return out_stream.writeAll(@typeName(T)),
|
||||
.Type => return writer.writeAll(@typeName(T)),
|
||||
.EnumLiteral => {
|
||||
const buffer = [_]u8{'.'} ++ @tagName(value);
|
||||
return formatType(buffer, fmt, options, out_stream, max_depth);
|
||||
return formatType(buffer, fmt, options, writer, max_depth);
|
||||
},
|
||||
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
|
||||
}
|
||||
@@ -492,19 +492,19 @@ fn formatValue(
|
||||
value: var,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
if (comptime std.mem.eql(u8, fmt, "B")) {
|
||||
return formatBytes(value, options, 1000, out_stream);
|
||||
return formatBytes(value, options, 1000, writer);
|
||||
} else if (comptime std.mem.eql(u8, fmt, "Bi")) {
|
||||
return formatBytes(value, options, 1024, out_stream);
|
||||
return formatBytes(value, options, 1024, writer);
|
||||
}
|
||||
|
||||
const T = @TypeOf(value);
|
||||
switch (@typeInfo(T)) {
|
||||
.Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, out_stream),
|
||||
.Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream),
|
||||
.Bool => return formatBuf(if (value) "true" else "false", options, out_stream),
|
||||
.Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, writer),
|
||||
.Int, .ComptimeInt => return formatIntValue(value, fmt, options, writer),
|
||||
.Bool => return formatBuf(if (value) "true" else "false", options, writer),
|
||||
else => comptime unreachable,
|
||||
}
|
||||
}
|
||||
@@ -513,7 +513,7 @@ pub fn formatIntValue(
|
||||
value: var,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
comptime var radix = 10;
|
||||
comptime var uppercase = false;
|
||||
@@ -529,7 +529,7 @@ pub fn formatIntValue(
|
||||
uppercase = false;
|
||||
} else if (comptime std.mem.eql(u8, fmt, "c")) {
|
||||
if (@TypeOf(int_value).bit_count <= 8) {
|
||||
return formatAsciiChar(@as(u8, int_value), options, out_stream);
|
||||
return formatAsciiChar(@as(u8, int_value), options, writer);
|
||||
} else {
|
||||
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
|
||||
}
|
||||
@@ -546,19 +546,19 @@ pub fn formatIntValue(
|
||||
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
||||
}
|
||||
|
||||
return formatInt(int_value, radix, uppercase, options, out_stream);
|
||||
return formatInt(int_value, radix, uppercase, options, writer);
|
||||
}
|
||||
|
||||
fn formatFloatValue(
|
||||
value: var,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
|
||||
return formatFloatScientific(value, options, out_stream);
|
||||
return formatFloatScientific(value, options, writer);
|
||||
} else if (comptime std.mem.eql(u8, fmt, "d")) {
|
||||
return formatFloatDecimal(value, options, out_stream);
|
||||
return formatFloatDecimal(value, options, writer);
|
||||
} else {
|
||||
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
||||
}
|
||||
@@ -568,13 +568,13 @@ pub fn formatText(
|
||||
bytes: []const u8,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) {
|
||||
return formatBuf(bytes, options, out_stream);
|
||||
return formatBuf(bytes, options, writer);
|
||||
} else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
|
||||
for (bytes) |c| {
|
||||
try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream);
|
||||
try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
@@ -585,38 +585,38 @@ pub fn formatText(
|
||||
pub fn formatAsciiChar(
|
||||
c: u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
return out_stream.writeAll(@as(*const [1]u8, &c));
|
||||
return writer.writeAll(@as(*const [1]u8, &c));
|
||||
}
|
||||
|
||||
pub fn formatBuf(
|
||||
buf: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
const width = options.width orelse buf.len;
|
||||
var padding = if (width > buf.len) (width - buf.len) else 0;
|
||||
const pad_byte = [1]u8{options.fill};
|
||||
switch (options.alignment) {
|
||||
.Left => {
|
||||
try out_stream.writeAll(buf);
|
||||
try writer.writeAll(buf);
|
||||
while (padding > 0) : (padding -= 1) {
|
||||
try out_stream.writeAll(&pad_byte);
|
||||
try writer.writeAll(&pad_byte);
|
||||
}
|
||||
},
|
||||
.Center => {
|
||||
const padl = padding / 2;
|
||||
var i: usize = 0;
|
||||
while (i < padl) : (i += 1) try out_stream.writeAll(&pad_byte);
|
||||
try out_stream.writeAll(buf);
|
||||
while (i < padding) : (i += 1) try out_stream.writeAll(&pad_byte);
|
||||
while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);
|
||||
try writer.writeAll(buf);
|
||||
while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);
|
||||
},
|
||||
.Right => {
|
||||
while (padding > 0) : (padding -= 1) {
|
||||
try out_stream.writeAll(&pad_byte);
|
||||
try writer.writeAll(&pad_byte);
|
||||
}
|
||||
try out_stream.writeAll(buf);
|
||||
try writer.writeAll(buf);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -627,38 +627,38 @@ pub fn formatBuf(
|
||||
pub fn formatFloatScientific(
|
||||
value: var,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
var x = @floatCast(f64, value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
if (math.signbit(x)) {
|
||||
try out_stream.writeAll("-");
|
||||
try writer.writeAll("-");
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (math.isNan(x)) {
|
||||
return out_stream.writeAll("nan");
|
||||
return writer.writeAll("nan");
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
return out_stream.writeAll("inf");
|
||||
return writer.writeAll("inf");
|
||||
}
|
||||
if (x == 0.0) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
|
||||
if (options.precision) |precision| {
|
||||
if (precision != 0) {
|
||||
try out_stream.writeAll(".");
|
||||
try writer.writeAll(".");
|
||||
var i: usize = 0;
|
||||
while (i < precision) : (i += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try out_stream.writeAll(".0");
|
||||
try writer.writeAll(".0");
|
||||
}
|
||||
|
||||
try out_stream.writeAll("e+00");
|
||||
try writer.writeAll("e+00");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -668,50 +668,50 @@ pub fn formatFloatScientific(
|
||||
if (options.precision) |precision| {
|
||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
|
||||
|
||||
try out_stream.writeAll(float_decimal.digits[0..1]);
|
||||
try writer.writeAll(float_decimal.digits[0..1]);
|
||||
|
||||
// {e0} case prints no `.`
|
||||
if (precision != 0) {
|
||||
try out_stream.writeAll(".");
|
||||
try writer.writeAll(".");
|
||||
|
||||
var printed: usize = 0;
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = math.min(float_decimal.digits.len, precision + 1);
|
||||
try out_stream.writeAll(float_decimal.digits[1..num_digits]);
|
||||
try writer.writeAll(float_decimal.digits[1..num_digits]);
|
||||
printed += num_digits - 1;
|
||||
}
|
||||
|
||||
while (printed < precision) : (printed += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try out_stream.writeAll(float_decimal.digits[0..1]);
|
||||
try out_stream.writeAll(".");
|
||||
try writer.writeAll(float_decimal.digits[0..1]);
|
||||
try writer.writeAll(".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len;
|
||||
|
||||
try out_stream.writeAll(float_decimal.digits[1..num_digits]);
|
||||
try writer.writeAll(float_decimal.digits[1..num_digits]);
|
||||
} else {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
}
|
||||
|
||||
try out_stream.writeAll("e");
|
||||
try writer.writeAll("e");
|
||||
const exp = float_decimal.exp - 1;
|
||||
|
||||
if (exp >= 0) {
|
||||
try out_stream.writeAll("+");
|
||||
try writer.writeAll("+");
|
||||
if (exp > -10 and exp < 10) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream);
|
||||
try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, writer);
|
||||
} else {
|
||||
try out_stream.writeAll("-");
|
||||
try writer.writeAll("-");
|
||||
if (exp > -10 and exp < 10) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream);
|
||||
try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,34 +720,34 @@ pub fn formatFloatScientific(
|
||||
pub fn formatFloatDecimal(
|
||||
value: var,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
var x = @as(f64, value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
if (math.signbit(x)) {
|
||||
try out_stream.writeAll("-");
|
||||
try writer.writeAll("-");
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (math.isNan(x)) {
|
||||
return out_stream.writeAll("nan");
|
||||
return writer.writeAll("nan");
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
return out_stream.writeAll("inf");
|
||||
return writer.writeAll("inf");
|
||||
}
|
||||
if (x == 0.0) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
|
||||
if (options.precision) |precision| {
|
||||
if (precision != 0) {
|
||||
try out_stream.writeAll(".");
|
||||
try writer.writeAll(".");
|
||||
var i: usize = 0;
|
||||
while (i < precision) : (i += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
} else {
|
||||
try out_stream.writeAll(".0");
|
||||
try writer.writeAll(".0");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,14 +769,14 @@ pub fn formatFloatDecimal(
|
||||
|
||||
if (num_digits_whole > 0) {
|
||||
// We may have to zero pad, for instance 1e4 requires zero padding.
|
||||
try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
|
||||
try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
|
||||
|
||||
var i = num_digits_whole_no_pad;
|
||||
while (i < num_digits_whole) : (i += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
} else {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
|
||||
// {.0} special case doesn't want a trailing '.'
|
||||
@@ -784,7 +784,7 @@ pub fn formatFloatDecimal(
|
||||
return;
|
||||
}
|
||||
|
||||
try out_stream.writeAll(".");
|
||||
try writer.writeAll(".");
|
||||
|
||||
// Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
|
||||
var printed: usize = 0;
|
||||
@@ -796,7 +796,7 @@ pub fn formatFloatDecimal(
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < zeros_to_print) : (i += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
@@ -808,14 +808,14 @@ pub fn formatFloatDecimal(
|
||||
// Remaining fractional portion, zero-padding if insufficient.
|
||||
assert(precision >= printed);
|
||||
if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
|
||||
try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
|
||||
try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
|
||||
return;
|
||||
} else {
|
||||
try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
|
||||
try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
|
||||
printed += float_decimal.digits.len - num_digits_whole_no_pad;
|
||||
|
||||
while (printed < precision) : (printed += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -827,14 +827,14 @@ pub fn formatFloatDecimal(
|
||||
|
||||
if (num_digits_whole > 0) {
|
||||
// We may have to zero pad, for instance 1e4 requires zero padding.
|
||||
try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
|
||||
try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
|
||||
|
||||
var i = num_digits_whole_no_pad;
|
||||
while (i < num_digits_whole) : (i += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
} else {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
|
||||
// Omit `.` if no fractional portion
|
||||
@@ -842,7 +842,7 @@ pub fn formatFloatDecimal(
|
||||
return;
|
||||
}
|
||||
|
||||
try out_stream.writeAll(".");
|
||||
try writer.writeAll(".");
|
||||
|
||||
// Zero-fill until we reach significant digits or run out of precision.
|
||||
if (float_decimal.exp < 0) {
|
||||
@@ -850,11 +850,11 @@ pub fn formatFloatDecimal(
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < zero_digit_count) : (i += 1) {
|
||||
try out_stream.writeAll("0");
|
||||
try writer.writeAll("0");
|
||||
}
|
||||
}
|
||||
|
||||
try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
|
||||
try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,10 +862,10 @@ pub fn formatBytes(
|
||||
value: var,
|
||||
options: FormatOptions,
|
||||
comptime radix: usize,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
if (value == 0) {
|
||||
return out_stream.writeAll("0B");
|
||||
return writer.writeAll("0B");
|
||||
}
|
||||
|
||||
const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value));
|
||||
@@ -885,10 +885,10 @@ pub fn formatBytes(
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
try formatFloatDecimal(new_value, options, out_stream);
|
||||
try formatFloatDecimal(new_value, options, writer);
|
||||
|
||||
if (suffix == ' ') {
|
||||
return out_stream.writeAll("B");
|
||||
return writer.writeAll("B");
|
||||
}
|
||||
|
||||
const buf = switch (radix) {
|
||||
@@ -896,7 +896,7 @@ pub fn formatBytes(
|
||||
1024 => &[_]u8{ suffix, 'i', 'B' },
|
||||
else => unreachable,
|
||||
};
|
||||
return out_stream.writeAll(buf);
|
||||
return writer.writeAll(buf);
|
||||
}
|
||||
|
||||
pub fn formatInt(
|
||||
@@ -904,7 +904,7 @@ pub fn formatInt(
|
||||
base: u8,
|
||||
uppercase: bool,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
const int_value = if (@TypeOf(value) == comptime_int) blk: {
|
||||
const Int = math.IntFittingRange(value, value);
|
||||
@@ -913,9 +913,9 @@ pub fn formatInt(
|
||||
value;
|
||||
|
||||
if (@TypeOf(int_value).is_signed) {
|
||||
return formatIntSigned(int_value, base, uppercase, options, out_stream);
|
||||
return formatIntSigned(int_value, base, uppercase, options, writer);
|
||||
} else {
|
||||
return formatIntUnsigned(int_value, base, uppercase, options, out_stream);
|
||||
return formatIntUnsigned(int_value, base, uppercase, options, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,7 +924,7 @@ fn formatIntSigned(
|
||||
base: u8,
|
||||
uppercase: bool,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
const new_options = FormatOptions{
|
||||
.width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
|
||||
@@ -934,15 +934,15 @@ fn formatIntSigned(
|
||||
const bit_count = @typeInfo(@TypeOf(value)).Int.bits;
|
||||
const Uint = std.meta.Int(false, bit_count);
|
||||
if (value < 0) {
|
||||
try out_stream.writeAll("-");
|
||||
try writer.writeAll("-");
|
||||
const new_value = math.absCast(value);
|
||||
return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);
|
||||
return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
|
||||
} else if (options.width == null or options.width.? == 0) {
|
||||
return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream);
|
||||
return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer);
|
||||
} else {
|
||||
try out_stream.writeAll("+");
|
||||
try writer.writeAll("+");
|
||||
const new_value = @intCast(Uint, value);
|
||||
return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);
|
||||
return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,7 +951,7 @@ fn formatIntUnsigned(
|
||||
base: u8,
|
||||
uppercase: bool,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
assert(base >= 2);
|
||||
var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;
|
||||
@@ -976,22 +976,22 @@ fn formatIntUnsigned(
|
||||
const zero_byte: u8 = options.fill;
|
||||
var leftover_padding = padding - index;
|
||||
while (true) {
|
||||
try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
|
||||
try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
|
||||
leftover_padding -= 1;
|
||||
if (leftover_padding == 0) break;
|
||||
}
|
||||
mem.set(u8, buf[0..index], options.fill);
|
||||
return out_stream.writeAll(&buf);
|
||||
return writer.writeAll(&buf);
|
||||
} else {
|
||||
const padded_buf = buf[index - padding ..];
|
||||
mem.set(u8, padded_buf[0..padding], options.fill);
|
||||
return out_stream.writeAll(padded_buf);
|
||||
return writer.writeAll(padded_buf);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize {
|
||||
var fbs = std.io.fixedBufferStream(out_buf);
|
||||
formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable;
|
||||
formatInt(value, base, uppercase, options, fbs.writer()) catch unreachable;
|
||||
return fbs.pos;
|
||||
}
|
||||
|
||||
@@ -1098,15 +1098,15 @@ pub const BufPrintError = error{
|
||||
};
|
||||
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 {
|
||||
var fbs = std.io.fixedBufferStream(buf);
|
||||
try format(fbs.outStream(), fmt, args);
|
||||
try format(fbs.writer(), fmt, args);
|
||||
return fbs.getWritten();
|
||||
}
|
||||
|
||||
// Count the characters needed for format. Useful for preallocating memory
|
||||
pub fn count(comptime fmt: []const u8, args: var) u64 {
|
||||
var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
|
||||
format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {};
|
||||
return counting_stream.bytes_written;
|
||||
var counting_writer = std.io.countingWriter(std.io.null_writer);
|
||||
format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
|
||||
return counting_writer.bytes_written;
|
||||
}
|
||||
|
||||
pub const AllocPrintError = error{OutOfMemory};
|
||||
@@ -1215,15 +1215,15 @@ test "buffer" {
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf1);
|
||||
try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth);
|
||||
try formatType(1234, "", FormatOptions{}, fbs.writer(), default_max_depth);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));
|
||||
|
||||
fbs.reset();
|
||||
try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth);
|
||||
try formatType('a', "c", FormatOptions{}, fbs.writer(), default_max_depth);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));
|
||||
|
||||
fbs.reset();
|
||||
try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth);
|
||||
try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), default_max_depth);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));
|
||||
}
|
||||
}
|
||||
@@ -1413,12 +1413,12 @@ test "custom" {
|
||||
self: SelfType,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
|
||||
return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });
|
||||
return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
|
||||
} else if (comptime std.mem.eql(u8, fmt, "d")) {
|
||||
return std.fmt.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y });
|
||||
return std.fmt.format(writer, "{d:.3}x{d:.3}", .{ self.x, self.y });
|
||||
} else {
|
||||
@compileError("Unknown format character: '" ++ fmt ++ "'");
|
||||
}
|
||||
@@ -1604,7 +1604,7 @@ test "formatIntValue with comptime_int" {
|
||||
|
||||
var buf: [20]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
try formatIntValue(value, "", FormatOptions{}, fbs.outStream());
|
||||
try formatIntValue(value, "", FormatOptions{}, fbs.writer());
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789"));
|
||||
}
|
||||
|
||||
@@ -1613,7 +1613,7 @@ test "formatFloatValue with comptime_float" {
|
||||
|
||||
var buf: [20]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
try formatFloatValue(value, "", FormatOptions{}, fbs.outStream());
|
||||
try formatFloatValue(value, "", FormatOptions{}, fbs.writer());
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00"));
|
||||
|
||||
try testFmt("1.0e+00", "{}", .{value});
|
||||
@@ -1630,10 +1630,10 @@ test "formatType max_depth" {
|
||||
self: SelfType,
|
||||
comptime fmt: []const u8,
|
||||
options: FormatOptions,
|
||||
out_stream: var,
|
||||
writer: var,
|
||||
) !void {
|
||||
if (fmt.len == 0) {
|
||||
return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });
|
||||
return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
|
||||
} else {
|
||||
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
||||
}
|
||||
@@ -1669,19 +1669,19 @@ test "formatType max_depth" {
|
||||
|
||||
var buf: [1000]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0);
|
||||
try formatType(inst, "", FormatOptions{}, fbs.writer(), 0);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }"));
|
||||
|
||||
fbs.reset();
|
||||
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1);
|
||||
try formatType(inst, "", FormatOptions{}, fbs.writer(), 1);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
|
||||
|
||||
fbs.reset();
|
||||
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2);
|
||||
try formatType(inst, "", FormatOptions{}, fbs.writer(), 2);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
|
||||
|
||||
fbs.reset();
|
||||
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3);
|
||||
try formatType(inst, "", FormatOptions{}, fbs.writer(), 3);
|
||||
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
|
||||
}
|
||||
|
||||
|
||||
+17
-21
@@ -261,17 +261,7 @@ pub const Dir = struct {
|
||||
name: []const u8,
|
||||
kind: Kind,
|
||||
|
||||
pub const Kind = enum {
|
||||
BlockDevice,
|
||||
CharacterDevice,
|
||||
Directory,
|
||||
NamedPipe,
|
||||
SymLink,
|
||||
File,
|
||||
UnixDomainSocket,
|
||||
Whiteout,
|
||||
Unknown,
|
||||
};
|
||||
pub const Kind = File.Kind;
|
||||
};
|
||||
|
||||
const IteratorError = error{AccessDenied} || os.UnexpectedError;
|
||||
@@ -1229,14 +1219,9 @@ pub const Dir = struct {
|
||||
var file = try self.openFile(file_path, .{});
|
||||
defer file.close();
|
||||
|
||||
const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);
|
||||
if (size > max_bytes) return error.FileTooBig;
|
||||
const stat_size = try file.getEndPos();
|
||||
|
||||
const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
|
||||
errdefer allocator.free(buf);
|
||||
|
||||
try file.inStream().readNoEof(buf);
|
||||
return buf;
|
||||
return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel);
|
||||
}
|
||||
|
||||
pub const DeleteTreeError = error{
|
||||
@@ -1532,9 +1517,9 @@ pub const Dir = struct {
|
||||
|
||||
var size: ?u64 = null;
|
||||
const mode = options.override_mode orelse blk: {
|
||||
const stat = try in_file.stat();
|
||||
size = stat.size;
|
||||
break :blk stat.mode;
|
||||
const st = try in_file.stat();
|
||||
size = st.size;
|
||||
break :blk st.mode;
|
||||
};
|
||||
|
||||
var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
|
||||
@@ -1560,6 +1545,17 @@ pub const Dir = struct {
|
||||
return AtomicFile.init(dest_path, options.mode, self, false);
|
||||
}
|
||||
}
|
||||
|
||||
pub const Stat = File.Stat;
|
||||
pub const StatError = File.StatError;
|
||||
|
||||
pub fn stat(self: Dir) StatError!Stat {
|
||||
const file: File = .{
|
||||
.handle = self.fd,
|
||||
.capable_io_mode = .blocking,
|
||||
};
|
||||
return file.stat();
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns an handle to the current working directory. It is not opened with iteration capability.
|
||||
|
||||
+64
-2
@@ -29,6 +29,18 @@ pub const File = struct {
|
||||
pub const Mode = os.mode_t;
|
||||
pub const INode = os.ino_t;
|
||||
|
||||
pub const Kind = enum {
|
||||
BlockDevice,
|
||||
CharacterDevice,
|
||||
Directory,
|
||||
NamedPipe,
|
||||
SymLink,
|
||||
File,
|
||||
UnixDomainSocket,
|
||||
Whiteout,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
pub const default_mode = switch (builtin.os.tag) {
|
||||
.windows => 0,
|
||||
.wasi => 0,
|
||||
@@ -209,7 +221,7 @@ pub const File = struct {
|
||||
/// TODO: integrate with async I/O
|
||||
pub fn mode(self: File) ModeError!Mode {
|
||||
if (builtin.os.tag == .windows) {
|
||||
return {};
|
||||
return 0;
|
||||
}
|
||||
return (try self.stat()).mode;
|
||||
}
|
||||
@@ -219,13 +231,14 @@ pub const File = struct {
|
||||
/// unique across time, as some file systems may reuse an inode after its file has been deleted.
|
||||
/// Some systems may change the inode of a file over time.
|
||||
///
|
||||
/// On Linux, the inode _is_ structure that stores the metadata, and the inode _number_ is what
|
||||
/// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what
|
||||
/// you see here: the index number of the inode.
|
||||
///
|
||||
/// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.
|
||||
inode: INode,
|
||||
size: u64,
|
||||
mode: Mode,
|
||||
kind: Kind,
|
||||
|
||||
/// Access time in nanoseconds, relative to UTC 1970-01-01.
|
||||
atime: i128,
|
||||
@@ -254,6 +267,7 @@ pub const File = struct {
|
||||
.inode = info.InternalInformation.IndexNumber,
|
||||
.size = @bitCast(u64, info.StandardInformation.EndOfFile),
|
||||
.mode = 0,
|
||||
.kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
|
||||
.atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
|
||||
.mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
|
||||
.ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
|
||||
@@ -268,6 +282,27 @@ pub const File = struct {
|
||||
.inode = st.ino,
|
||||
.size = @bitCast(u64, st.size),
|
||||
.mode = st.mode,
|
||||
.kind = switch (builtin.os.tag) {
|
||||
.wasi => switch (st.filetype) {
|
||||
os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
|
||||
os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
|
||||
os.FILETYPE_DIRECTORY => Kind.Directory,
|
||||
os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
|
||||
os.FILETYPE_REGULAR_FILE => Kind.File,
|
||||
os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
|
||||
else => Kind.Unknown,
|
||||
},
|
||||
else => switch (st.mode & os.S_IFMT) {
|
||||
os.S_IFBLK => Kind.BlockDevice,
|
||||
os.S_IFCHR => Kind.CharacterDevice,
|
||||
os.S_IFDIR => Kind.Directory,
|
||||
os.S_IFIFO => Kind.NamedPipe,
|
||||
os.S_IFLNK => Kind.SymLink,
|
||||
os.S_IFREG => Kind.File,
|
||||
os.S_IFSOCK => Kind.UnixDomainSocket,
|
||||
else => Kind.Unknown,
|
||||
},
|
||||
},
|
||||
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
|
||||
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
|
||||
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
|
||||
@@ -306,6 +341,33 @@ pub const File = struct {
|
||||
try os.futimens(self.handle, ×);
|
||||
}
|
||||
|
||||
/// On success, caller owns returned buffer.
|
||||
/// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
|
||||
pub fn readAllAlloc(self: File, allocator: *mem.Allocator, stat_size: u64, max_bytes: usize) ![]u8 {
|
||||
return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);
|
||||
}
|
||||
|
||||
/// On success, caller owns returned buffer.
|
||||
/// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
|
||||
/// Allows specifying alignment and a sentinel value.
|
||||
pub fn readAllAllocOptions(
|
||||
self: File,
|
||||
allocator: *mem.Allocator,
|
||||
stat_size: u64,
|
||||
max_bytes: usize,
|
||||
comptime alignment: u29,
|
||||
comptime optional_sentinel: ?u8,
|
||||
) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
|
||||
const size = math.cast(usize, stat_size) catch math.maxInt(usize);
|
||||
if (size > max_bytes) return error.FileTooBig;
|
||||
|
||||
const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
|
||||
errdefer allocator.free(buf);
|
||||
|
||||
try self.reader().readNoEof(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
pub const ReadError = os.ReadError;
|
||||
pub const PReadError = os.PReadError;
|
||||
|
||||
|
||||
+40
-3
@@ -1,7 +1,44 @@
|
||||
const std = @import("../std.zig");
|
||||
const testing = std.testing;
|
||||
const builtin = std.builtin;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
|
||||
const File = std.fs.File;
|
||||
const tmpDir = testing.tmpDir;
|
||||
|
||||
test "readAllAlloc" {
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
|
||||
defer file.close();
|
||||
|
||||
const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);
|
||||
defer testing.allocator.free(buf1);
|
||||
testing.expect(buf1.len == 0);
|
||||
|
||||
const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
|
||||
try file.writeAll(write_buf);
|
||||
try file.seekTo(0);
|
||||
const file_size = try file.getEndPos();
|
||||
|
||||
// max_bytes > file_size
|
||||
const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);
|
||||
defer testing.allocator.free(buf2);
|
||||
testing.expectEqual(write_buf.len, buf2.len);
|
||||
testing.expect(std.mem.eql(u8, write_buf, buf2));
|
||||
try file.seekTo(0);
|
||||
|
||||
// max_bytes == file_size
|
||||
const buf3 = try file.readAllAlloc(testing.allocator, file_size, write_buf.len);
|
||||
defer testing.allocator.free(buf3);
|
||||
testing.expectEqual(write_buf.len, buf3.len);
|
||||
testing.expect(std.mem.eql(u8, write_buf, buf3));
|
||||
|
||||
// max_bytes < file_size
|
||||
testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, file_size, write_buf.len - 1));
|
||||
}
|
||||
|
||||
test "openSelfExe" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
@@ -116,7 +153,7 @@ test "create file, lock and read from multiple process at once" {
|
||||
test "open file with exclusive nonblocking lock twice (absolute paths)" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
|
||||
const filename = try fs.path.resolve(allocator, &file_paths);
|
||||
@@ -126,7 +163,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
|
||||
|
||||
const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
|
||||
file1.close();
|
||||
std.testing.expectError(error.WouldBlock, file2);
|
||||
testing.expectError(error.WouldBlock, file2);
|
||||
|
||||
try fs.deleteFileAbsolute(filename);
|
||||
}
|
||||
@@ -187,7 +224,7 @@ const FileLockTestContext = struct {
|
||||
};
|
||||
|
||||
fn run_lock_file_test(contexts: []FileLockTestContext) !void {
|
||||
var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
|
||||
var threads = std.ArrayList(*std.Thread).init(testing.allocator);
|
||||
defer {
|
||||
for (threads.items) |thread| {
|
||||
thread.wait();
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter;
|
||||
|
||||
/// Deprecated: use `std.io.buffered_writer.bufferedWriter`
|
||||
pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter
|
||||
pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter;
|
||||
|
||||
@@ -40,8 +40,7 @@ pub fn Reader(
|
||||
return index;
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read would be smaller than buf.len,
|
||||
/// error.EndOfStream is returned instead.
|
||||
/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
|
||||
pub fn readNoEof(self: Self, buf: []u8) !void {
|
||||
const amt_read = try self.readAll(buf);
|
||||
if (amt_read < buf.len) return error.EndOfStream;
|
||||
|
||||
+4
-5
@@ -1535,7 +1535,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
|
||||
const allocator = options.allocator orelse return error.AllocatorRequired;
|
||||
switch (ptrInfo.size) {
|
||||
.One => {
|
||||
const r: T = allocator.create(ptrInfo.child);
|
||||
const r: T = try allocator.create(ptrInfo.child);
|
||||
r.* = try parseInternal(ptrInfo.child, token, tokens, options);
|
||||
return r;
|
||||
},
|
||||
@@ -1629,7 +1629,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
|
||||
switch (ptrInfo.size) {
|
||||
.One => {
|
||||
parseFree(ptrInfo.child, value.*, options);
|
||||
allocator.destroy(v);
|
||||
allocator.destroy(value);
|
||||
},
|
||||
.Slice => {
|
||||
for (value) |v| {
|
||||
@@ -2576,8 +2576,8 @@ pub fn stringify(
|
||||
},
|
||||
.Array => return stringify(&value, options, out_stream),
|
||||
.Vector => |info| {
|
||||
const array: [info.len]info.child = value;
|
||||
return stringify(&array, options, out_stream);
|
||||
const array: [info.len]info.child = value;
|
||||
return stringify(&array, options, out_stream);
|
||||
},
|
||||
else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
|
||||
}
|
||||
@@ -2770,4 +2770,3 @@ test "stringify struct with custom stringifier" {
|
||||
test "stringify vector" {
|
||||
try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});
|
||||
}
|
||||
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
const std = @import("std.zig");
|
||||
const builtin = std.builtin;
|
||||
const root = @import("root");
|
||||
|
||||
//! std.log is standardized interface for logging which allows for the logging
|
||||
//! of programs and libraries using this interface to be formatted and filtered
|
||||
//! by the implementer of the root.log function.
|
||||
//!
|
||||
//! The scope parameter should be used to give context to the logging. For
|
||||
//! example, a library called 'libfoo' might use .libfoo as its scope.
|
||||
//!
|
||||
//! An example root.log might look something like this:
|
||||
//!
|
||||
//! ```
|
||||
//! const std = @import("std");
|
||||
//!
|
||||
//! // Set the log level to warning
|
||||
//! pub const log_level: std.log.Level = .warn;
|
||||
//!
|
||||
//! // Define root.log to override the std implementation
|
||||
//! pub fn log(
|
||||
//! comptime level: std.log.Level,
|
||||
//! comptime scope: @TypeOf(.EnumLiteral),
|
||||
//! comptime format: []const u8,
|
||||
//! args: var,
|
||||
//! ) void {
|
||||
//! // Ignore all non-critical logging from sources other than
|
||||
//! // .my_project and .nice_library
|
||||
//! const scope_prefix = "(" ++ switch (scope) {
|
||||
//! .my_project, .nice_library => @tagName(scope),
|
||||
//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
|
||||
//! @tagName(scope)
|
||||
//! else
|
||||
//! return,
|
||||
//! } ++ "): ";
|
||||
//!
|
||||
//! const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
|
||||
//!
|
||||
//! // Print the message to stderr, silently ignoring any errors
|
||||
//! const held = std.debug.getStderrMutex().acquire();
|
||||
//! defer held.release();
|
||||
//! const stderr = std.debug.getStderrStream();
|
||||
//! nosuspend stderr.print(prefix ++ format, args) catch return;
|
||||
//! }
|
||||
//!
|
||||
//! pub fn main() void {
|
||||
//! // Won't be printed as log_level is .warn
|
||||
//! std.log.info(.my_project, "Starting up.\n", .{});
|
||||
//! std.log.err(.nice_library, "Something went very wrong, sorry.\n", .{});
|
||||
//! // Won't be printed as it gets filtered out by our log function
|
||||
//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1\n", .{});
|
||||
//! }
|
||||
//! ```
|
||||
//! Which produces the following output:
|
||||
//! ```
|
||||
//! [err] (nice_library): Something went very wrong, sorry.
|
||||
//! ```
|
||||
|
||||
pub const Level = enum {
|
||||
/// Emergency: a condition that cannot be handled, usually followed by a
|
||||
/// panic.
|
||||
emerg,
|
||||
/// Alert: a condition that should be corrected immediately (e.g. database
|
||||
/// corruption).
|
||||
alert,
|
||||
/// Critical: A bug has been detected or something has gone wrong and it
|
||||
/// will have an effect on the operation of the program.
|
||||
crit,
|
||||
/// Error: A bug has been detected or something has gone wrong but it is
|
||||
/// recoverable.
|
||||
err,
|
||||
/// Warning: it is uncertain if something has gone wrong or not, but the
|
||||
/// circumstances would be worth investigating.
|
||||
warn,
|
||||
/// Notice: non-error but significant conditions.
|
||||
notice,
|
||||
/// Informational: general messages about the state of the program.
|
||||
info,
|
||||
/// Debug: messages only useful for debugging.
|
||||
debug,
|
||||
};
|
||||
|
||||
/// The default log level is based on build mode. Note that in ReleaseSmall
|
||||
/// builds the default level is emerg but no messages will be stored/logged
|
||||
/// by the default logger to save space.
|
||||
pub const default_level: Level = switch (builtin.mode) {
|
||||
.Debug => .debug,
|
||||
.ReleaseSafe => .notice,
|
||||
.ReleaseFast => .err,
|
||||
.ReleaseSmall => .emerg,
|
||||
};
|
||||
|
||||
/// The current log level. This is set to root.log_level if present, otherwise
|
||||
/// log.default_level.
|
||||
pub const level: Level = if (@hasDecl(root, "log_level"))
|
||||
root.log_level
|
||||
else
|
||||
default_level;
|
||||
|
||||
fn log(
|
||||
comptime message_level: Level,
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
if (@enumToInt(message_level) <= @enumToInt(level)) {
|
||||
if (@hasDecl(root, "log")) {
|
||||
root.log(message_level, scope, format, args);
|
||||
} else if (builtin.mode != .ReleaseSmall) {
|
||||
const held = std.debug.getStderrMutex().acquire();
|
||||
defer held.release();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
nosuspend stderr.print(format, args) catch return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Log an emergency message to stderr. This log level is intended to be used
|
||||
/// for conditions that cannot be handled and is usually followed by a panic.
|
||||
pub fn emerg(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
@setCold(true);
|
||||
log(.emerg, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log an alert message to stderr. This log level is intended to be used for
|
||||
/// conditions that should be corrected immediately (e.g. database corruption).
|
||||
pub fn alert(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
@setCold(true);
|
||||
log(.alert, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log a critical message to stderr. This log level is intended to be used
|
||||
/// when a bug has been detected or something has gone wrong and it will have
|
||||
/// an effect on the operation of the program.
|
||||
pub fn crit(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
@setCold(true);
|
||||
log(.crit, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log an error message to stderr. This log level is intended to be used when
|
||||
/// a bug has been detected or something has gone wrong but it is recoverable.
|
||||
pub fn err(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
@setCold(true);
|
||||
log(.err, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log a warning message to stderr. This log level is intended to be used if
|
||||
/// it is uncertain whether something has gone wrong or not, but the
|
||||
/// circumstances would be worth investigating.
|
||||
pub fn warn(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
log(.warn, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log a notice message to stderr. This log level is intended to be used for
|
||||
/// non-error but significant conditions.
|
||||
pub fn notice(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
log(.notice, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log an info message to stderr. This log level is intended to be used for
|
||||
/// general messages about the state of the program.
|
||||
pub fn info(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
log(.info, scope, format, args);
|
||||
}
|
||||
|
||||
/// Log a debug message to stderr. This log level is intended to be used for
|
||||
/// messages which are only useful for debugging.
|
||||
pub fn debug(
|
||||
comptime scope: @Type(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: var,
|
||||
) void {
|
||||
log(.debug, scope, format, args);
|
||||
}
|
||||
@@ -122,6 +122,11 @@ pub fn forceEval(value: var) void {
|
||||
const p = @ptrCast(*volatile f64, &x);
|
||||
p.* = x;
|
||||
},
|
||||
f128 => {
|
||||
var x: f128 = undefined;
|
||||
const p = @ptrCast(*volatile f128, &x);
|
||||
p.* = x;
|
||||
},
|
||||
else => {
|
||||
@compileError("forceEval not implemented for " ++ @typeName(T));
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ pub fn ceil(x: var) @TypeOf(x) {
|
||||
return switch (T) {
|
||||
f32 => ceil32(x),
|
||||
f64 => ceil64(x),
|
||||
f128 => ceil128(x),
|
||||
else => @compileError("ceil not implemented for " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
@@ -86,9 +87,37 @@ fn ceil64(x: f64) f64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn ceil128(x: f128) f128 {
|
||||
const u = @bitCast(u128, x);
|
||||
const e = (u >> 112) & 0x7FFF;
|
||||
var y: f128 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 112 or x == 0) return x;
|
||||
|
||||
if (u >> 127 != 0) {
|
||||
y = x - math.f128_toint + math.f128_toint - x;
|
||||
} else {
|
||||
y = x + math.f128_toint - math.f128_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FFF - 1) {
|
||||
math.forceEval(y);
|
||||
if (u >> 127 != 0) {
|
||||
return -0.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
} else if (y < 0) {
|
||||
return x + y + 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
test "math.ceil" {
|
||||
expect(ceil(@as(f32, 0.0)) == ceil32(0.0));
|
||||
expect(ceil(@as(f64, 0.0)) == ceil64(0.0));
|
||||
expect(ceil(@as(f128, 0.0)) == ceil128(0.0));
|
||||
}
|
||||
|
||||
test "math.ceil32" {
|
||||
@@ -103,6 +132,12 @@ test "math.ceil64" {
|
||||
expect(ceil64(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "math.ceil128" {
|
||||
expect(ceil128(1.3) == 2.0);
|
||||
expect(ceil128(-1.3) == -1.0);
|
||||
expect(ceil128(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "math.ceil32.special" {
|
||||
expect(ceil32(0.0) == 0.0);
|
||||
expect(ceil32(-0.0) == -0.0);
|
||||
@@ -118,3 +153,11 @@ test "math.ceil64.special" {
|
||||
expect(math.isNegativeInf(ceil64(-math.inf(f64))));
|
||||
expect(math.isNan(ceil64(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "math.ceil128.special" {
|
||||
expect(ceil128(0.0) == 0.0);
|
||||
expect(ceil128(-0.0) == -0.0);
|
||||
expect(math.isPositiveInf(ceil128(math.inf(f128))));
|
||||
expect(math.isNegativeInf(ceil128(-math.inf(f128))));
|
||||
expect(math.isNan(ceil128(math.nan(f128))));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn floor(x: var) @TypeOf(x) {
|
||||
f16 => floor16(x),
|
||||
f32 => floor32(x),
|
||||
f64 => floor64(x),
|
||||
f128 => floor128(x),
|
||||
else => @compileError("floor not implemented for " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
@@ -122,10 +123,38 @@ fn floor64(x: f64) f64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn floor128(x: f128) f128 {
|
||||
const u = @bitCast(u128, x);
|
||||
const e = (u >> 112) & 0x7FFF;
|
||||
var y: f128 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 112 or x == 0) return x;
|
||||
|
||||
if (u >> 127 != 0) {
|
||||
y = x - math.f128_toint + math.f128_toint - x;
|
||||
} else {
|
||||
y = x + math.f128_toint - math.f128_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FFF - 1) {
|
||||
math.forceEval(y);
|
||||
if (u >> 127 != 0) {
|
||||
return -1.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else if (y > 0) {
|
||||
return x + y - 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
test "math.floor" {
|
||||
expect(floor(@as(f16, 1.3)) == floor16(1.3));
|
||||
expect(floor(@as(f32, 1.3)) == floor32(1.3));
|
||||
expect(floor(@as(f64, 1.3)) == floor64(1.3));
|
||||
expect(floor(@as(f128, 1.3)) == floor128(1.3));
|
||||
}
|
||||
|
||||
test "math.floor16" {
|
||||
@@ -146,6 +175,12 @@ test "math.floor64" {
|
||||
expect(floor64(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "math.floor128" {
|
||||
expect(floor128(1.3) == 1.0);
|
||||
expect(floor128(-1.3) == -2.0);
|
||||
expect(floor128(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "math.floor16.special" {
|
||||
expect(floor16(0.0) == 0.0);
|
||||
expect(floor16(-0.0) == -0.0);
|
||||
@@ -169,3 +204,11 @@ test "math.floor64.special" {
|
||||
expect(math.isNegativeInf(floor64(-math.inf(f64))));
|
||||
expect(math.isNan(floor64(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "math.floor128.special" {
|
||||
expect(floor128(0.0) == 0.0);
|
||||
expect(floor128(-0.0) == -0.0);
|
||||
expect(math.isPositiveInf(floor128(math.inf(f128))));
|
||||
expect(math.isNegativeInf(floor128(-math.inf(f128))));
|
||||
expect(math.isNan(floor128(math.nan(f128))));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ pub fn round(x: var) @TypeOf(x) {
|
||||
return switch (T) {
|
||||
f32 => round32(x),
|
||||
f64 => round64(x),
|
||||
f128 => round128(x),
|
||||
else => @compileError("round not implemented for " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
@@ -90,9 +91,43 @@ fn round64(x_: f64) f64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn round128(x_: f128) f128 {
|
||||
var x = x_;
|
||||
const u = @bitCast(u128, x);
|
||||
const e = (u >> 112) & 0x7FFF;
|
||||
var y: f128 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 112) {
|
||||
return x;
|
||||
}
|
||||
if (u >> 127 != 0) {
|
||||
x = -x;
|
||||
}
|
||||
if (e < 0x3FFF - 1) {
|
||||
math.forceEval(x + math.f64_toint);
|
||||
return 0 * @bitCast(f128, u);
|
||||
}
|
||||
|
||||
y = x + math.f128_toint - math.f128_toint - x;
|
||||
if (y > 0.5) {
|
||||
y = y + x - 1;
|
||||
} else if (y <= -0.5) {
|
||||
y = y + x + 1;
|
||||
} else {
|
||||
y = y + x;
|
||||
}
|
||||
|
||||
if (u >> 127 != 0) {
|
||||
return -y;
|
||||
} else {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
test "math.round" {
|
||||
expect(round(@as(f32, 1.3)) == round32(1.3));
|
||||
expect(round(@as(f64, 1.3)) == round64(1.3));
|
||||
expect(round(@as(f128, 1.3)) == round128(1.3));
|
||||
}
|
||||
|
||||
test "math.round32" {
|
||||
@@ -109,6 +144,13 @@ test "math.round64" {
|
||||
expect(round64(1.8) == 2.0);
|
||||
}
|
||||
|
||||
test "math.round128" {
|
||||
expect(round128(1.3) == 1.0);
|
||||
expect(round128(-1.3) == -1.0);
|
||||
expect(round128(0.2) == 0.0);
|
||||
expect(round128(1.8) == 2.0);
|
||||
}
|
||||
|
||||
test "math.round32.special" {
|
||||
expect(round32(0.0) == 0.0);
|
||||
expect(round32(-0.0) == -0.0);
|
||||
@@ -124,3 +166,11 @@ test "math.round64.special" {
|
||||
expect(math.isNegativeInf(round64(-math.inf(f64))));
|
||||
expect(math.isNan(round64(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "math.round128.special" {
|
||||
expect(round128(0.0) == 0.0);
|
||||
expect(round128(-0.0) == -0.0);
|
||||
expect(math.isPositiveInf(round128(math.inf(f128))));
|
||||
expect(math.isNegativeInf(round128(-math.inf(f128))));
|
||||
expect(math.isNan(round128(math.nan(f128))));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ pub fn trunc(x: var) @TypeOf(x) {
|
||||
return switch (T) {
|
||||
f32 => trunc32(x),
|
||||
f64 => trunc64(x),
|
||||
f128 => trunc128(x),
|
||||
else => @compileError("trunc not implemented for " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
@@ -66,9 +67,31 @@ fn trunc64(x: f64) f64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn trunc128(x: f128) f128 {
|
||||
const u = @bitCast(u128, x);
|
||||
var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16;
|
||||
var m: u128 = undefined;
|
||||
|
||||
if (e >= 112 + 16) {
|
||||
return x;
|
||||
}
|
||||
if (e < 16) {
|
||||
e = 1;
|
||||
}
|
||||
|
||||
m = @as(u128, maxInt(u128)) >> @intCast(u7, e);
|
||||
if (u & m == 0) {
|
||||
return x;
|
||||
} else {
|
||||
math.forceEval(x + 0x1p120);
|
||||
return @bitCast(f128, u & ~m);
|
||||
}
|
||||
}
|
||||
|
||||
test "math.trunc" {
|
||||
expect(trunc(@as(f32, 1.3)) == trunc32(1.3));
|
||||
expect(trunc(@as(f64, 1.3)) == trunc64(1.3));
|
||||
expect(trunc(@as(f128, 1.3)) == trunc128(1.3));
|
||||
}
|
||||
|
||||
test "math.trunc32" {
|
||||
@@ -83,6 +106,12 @@ test "math.trunc64" {
|
||||
expect(trunc64(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "math.trunc128" {
|
||||
expect(trunc128(1.3) == 1.0);
|
||||
expect(trunc128(-1.3) == -1.0);
|
||||
expect(trunc128(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "math.trunc32.special" {
|
||||
expect(trunc32(0.0) == 0.0); // 0x3F800000
|
||||
expect(trunc32(-0.0) == -0.0);
|
||||
@@ -98,3 +127,11 @@ test "math.trunc64.special" {
|
||||
expect(math.isNegativeInf(trunc64(-math.inf(f64))));
|
||||
expect(math.isNan(trunc64(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "math.trunc128.special" {
|
||||
expect(trunc128(0.0) == 0.0);
|
||||
expect(trunc128(-0.0) == -0.0);
|
||||
expect(math.isPositiveInf(trunc128(math.inf(f128))));
|
||||
expect(math.isNegativeInf(trunc128(-math.inf(f128))));
|
||||
expect(math.isNan(trunc128(math.nan(f128))));
|
||||
}
|
||||
|
||||
+88
-6
@@ -250,7 +250,7 @@ test "std.meta.containerLayout" {
|
||||
testing.expect(containerLayout(U3) == .Extern);
|
||||
}
|
||||
|
||||
pub fn declarations(comptime T: type) []TypeInfo.Declaration {
|
||||
pub fn declarations(comptime T: type) []const TypeInfo.Declaration {
|
||||
return switch (@typeInfo(T)) {
|
||||
.Struct => |info| info.decls,
|
||||
.Enum => |info| info.decls,
|
||||
@@ -274,7 +274,7 @@ test "std.meta.declarations" {
|
||||
fn a() void {}
|
||||
};
|
||||
|
||||
const decls = comptime [_][]TypeInfo.Declaration{
|
||||
const decls = comptime [_][]const TypeInfo.Declaration{
|
||||
declarations(E1),
|
||||
declarations(S1),
|
||||
declarations(U1),
|
||||
@@ -323,10 +323,10 @@ test "std.meta.declarationInfo" {
|
||||
}
|
||||
|
||||
pub fn fields(comptime T: type) switch (@typeInfo(T)) {
|
||||
.Struct => []TypeInfo.StructField,
|
||||
.Union => []TypeInfo.UnionField,
|
||||
.ErrorSet => []TypeInfo.Error,
|
||||
.Enum => []TypeInfo.EnumField,
|
||||
.Struct => []const TypeInfo.StructField,
|
||||
.Union => []const TypeInfo.UnionField,
|
||||
.ErrorSet => []const TypeInfo.Error,
|
||||
.Enum => []const TypeInfo.EnumField,
|
||||
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
||||
} {
|
||||
return switch (@typeInfo(T)) {
|
||||
@@ -693,3 +693,85 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Given a type and value, cast the value to the type as c would.
|
||||
/// This is for translate-c and is not intended for general use.
|
||||
pub fn cast(comptime DestType: type, target: var) DestType {
|
||||
const TargetType = @TypeOf(target);
|
||||
switch (@typeInfo(DestType)) {
|
||||
.Pointer => {
|
||||
switch (@typeInfo(TargetType)) {
|
||||
.Int, .ComptimeInt => {
|
||||
return @intToPtr(DestType, target);
|
||||
},
|
||||
.Pointer => |ptr| {
|
||||
return @ptrCast(DestType, @alignCast(ptr.alignment, target));
|
||||
},
|
||||
.Optional => |opt| {
|
||||
if (@typeInfo(opt.child) == .Pointer) {
|
||||
return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
.Optional => |opt| {
|
||||
if (@typeInfo(opt.child) == .Pointer) {
|
||||
switch (@typeInfo(TargetType)) {
|
||||
.Int, .ComptimeInt => {
|
||||
return @intToPtr(DestType, target);
|
||||
},
|
||||
.Pointer => |ptr| {
|
||||
return @ptrCast(DestType, @alignCast(ptr.alignment, target));
|
||||
},
|
||||
.Optional => |target_opt| {
|
||||
if (@typeInfo(target_opt.child) == .Pointer) {
|
||||
return @ptrCast(DestType, @alignCast(@alignOf(target_opt.child.Child), target));
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
.Enum, .EnumLiteral => {
|
||||
if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
|
||||
return @intToEnum(DestType, target);
|
||||
}
|
||||
},
|
||||
.Int, .ComptimeInt => {
|
||||
switch (@typeInfo(TargetType)) {
|
||||
.Pointer => {
|
||||
return @as(DestType, @ptrToInt(target));
|
||||
},
|
||||
.Optional => |opt| {
|
||||
if (@typeInfo(opt.child) == .Pointer) {
|
||||
return @as(DestType, @ptrToInt(target));
|
||||
}
|
||||
},
|
||||
.Enum, .EnumLiteral => {
|
||||
return @as(DestType, @enumToInt(target));
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return @as(DestType, target);
|
||||
}
|
||||
|
||||
test "std.meta.cast" {
|
||||
const E = enum(u2) {
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
};
|
||||
|
||||
var i = @as(i64, 10);
|
||||
|
||||
testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
|
||||
testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));
|
||||
testing.expect(cast(u64, @as(u32, 10)) == @as(u64, 10));
|
||||
testing.expect(cast(E, 1) == .One);
|
||||
testing.expect(cast(u8, E.Two) == 2);
|
||||
testing.expect(cast(*u64, &i).* == @as(u64, 10));
|
||||
}
|
||||
|
||||
+100
-6
@@ -1520,15 +1520,17 @@ pub const SymLinkError = error{
|
||||
/// If `sym_link_path` exists, it will not be overwritten.
|
||||
/// See also `symlinkC` and `symlinkW`.
|
||||
pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("symlink is not supported in WASI; use symlinkat instead");
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
const target_path_w = try windows.sliceToPrefixedFileW(target_path);
|
||||
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
|
||||
return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);
|
||||
} else {
|
||||
const target_path_c = try toPosixPath(target_path);
|
||||
const sym_link_path_c = try toPosixPath(sym_link_path);
|
||||
return symlinkZ(&target_path_c, &sym_link_path_c);
|
||||
}
|
||||
const target_path_c = try toPosixPath(target_path);
|
||||
const sym_link_path_c = try toPosixPath(sym_link_path);
|
||||
return symlinkZ(&target_path_c, &sym_link_path_c);
|
||||
}
|
||||
|
||||
pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");
|
||||
@@ -1561,15 +1563,65 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
|
||||
/// `target_path` **relative** to `newdirfd` directory handle.
|
||||
/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
|
||||
/// one; the latter case is known as a dangling link.
|
||||
/// If `sym_link_path` exists, it will not be overwritten.
|
||||
/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
|
||||
pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
return symlinkatWasi(target_path, newdirfd, sym_link_path);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
const target_path_w = try windows.sliceToPrefixedFileW(target_path);
|
||||
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
|
||||
return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path_w.span().ptr);
|
||||
}
|
||||
const target_path_c = try toPosixPath(target_path);
|
||||
const sym_link_path_c = try toPosixPath(sym_link_path);
|
||||
return symlinkatZ(target_path_c, newdirfd, sym_link_path_c);
|
||||
return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
|
||||
}
|
||||
|
||||
pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
|
||||
|
||||
/// WASI-only. The same as `symlinkat` but targeting WASI.
|
||||
/// See also `symlinkat`.
|
||||
pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
|
||||
switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
|
||||
wasi.ESUCCESS => {},
|
||||
wasi.EFAULT => unreachable,
|
||||
wasi.EINVAL => unreachable,
|
||||
wasi.EACCES => return error.AccessDenied,
|
||||
wasi.EPERM => return error.AccessDenied,
|
||||
wasi.EDQUOT => return error.DiskQuota,
|
||||
wasi.EEXIST => return error.PathAlreadyExists,
|
||||
wasi.EIO => return error.FileSystem,
|
||||
wasi.ELOOP => return error.SymLinkLoop,
|
||||
wasi.ENAMETOOLONG => return error.NameTooLong,
|
||||
wasi.ENOENT => return error.FileNotFound,
|
||||
wasi.ENOTDIR => return error.NotDir,
|
||||
wasi.ENOMEM => return error.SystemResources,
|
||||
wasi.ENOSPC => return error.NoSpaceLeft,
|
||||
wasi.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded.
|
||||
/// See also `symlinkat`.
|
||||
pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymlinkError!void {
|
||||
@compileError("TODO implement on Windows");
|
||||
}
|
||||
|
||||
/// The same as `symlinkat` except the parameters are null-terminated pointers.
|
||||
/// See also `symlinkat`.
|
||||
pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const target_path_w = try windows.cStrToPrefixedFileW(target_path);
|
||||
const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
|
||||
return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path.span().ptr);
|
||||
}
|
||||
switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
|
||||
0 => return,
|
||||
EFAULT => unreachable,
|
||||
@@ -2291,12 +2343,54 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
|
||||
/// The return value is a slice of `out_buffer` from index 0.
|
||||
/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
|
||||
pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
return readlinkatWasi(dirfd, file_path, out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return readlinkatZ(dirfd, &file_path_c, out_buffer);
|
||||
}
|
||||
|
||||
pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
|
||||
|
||||
/// WASI-only. Same as `readlinkat` but targets WASI.
|
||||
/// See also `readlinkat`.
|
||||
pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
var bufused: usize = undefined;
|
||||
switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
|
||||
wasi.ESUCCESS => return out_buffer[0..bufused],
|
||||
wasi.EACCES => return error.AccessDenied,
|
||||
wasi.EFAULT => unreachable,
|
||||
wasi.EINVAL => unreachable,
|
||||
wasi.EIO => return error.FileSystem,
|
||||
wasi.ELOOP => return error.SymLinkLoop,
|
||||
wasi.ENAMETOOLONG => return error.NameTooLong,
|
||||
wasi.ENOENT => return error.FileNotFound,
|
||||
wasi.ENOMEM => return error.SystemResources,
|
||||
wasi.ENOTDIR => return error.NotDir,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
|
||||
/// See also `readlinkat`.
|
||||
pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
@compileError("TODO implement on Windows");
|
||||
}
|
||||
|
||||
/// Same as `readlinkat` except `file_path` is null-terminated.
|
||||
/// See also `readlinkat`.
|
||||
pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
@compileError("TODO implement readlink for Windows");
|
||||
return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
|
||||
}
|
||||
const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
|
||||
@@ -18,6 +18,25 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
const tmpDir = std.testing.tmpDir;
|
||||
const Dir = std.fs.Dir;
|
||||
|
||||
test "readlinkat" {
|
||||
// enable when `readlinkat` and `symlinkat` are implemented on Windows
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// create file
|
||||
try tmp.dir.writeFile("file.txt", "nonsense");
|
||||
|
||||
// create a symbolic link
|
||||
try os.symlinkat("file.txt", tmp.dir.fd, "link");
|
||||
|
||||
// read the link
|
||||
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
|
||||
expect(mem.eql(u8, "file.txt", read_link));
|
||||
}
|
||||
|
||||
test "makePath, put some files in it, deleteTree" {
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
+10
-1
@@ -901,7 +901,13 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
|
||||
var wsadata: ws2_32.WSADATA = undefined;
|
||||
return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
|
||||
0 => wsadata,
|
||||
else => |err| unexpectedWSAError(@intToEnum(ws2_32.WinsockError, @intCast(u16, err))),
|
||||
else => |err_int| switch (@intToEnum(ws2_32.WinsockError, @intCast(u16, err_int))) {
|
||||
.WSASYSNOTREADY => return error.SystemNotAvailable,
|
||||
.WSAVERNOTSUPPORTED => return error.VersionNotSupported,
|
||||
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
|
||||
.WSAEPROCLIM => return error.SystemResources,
|
||||
else => |err| return unexpectedWSAError(err),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -909,6 +915,9 @@ pub fn WSACleanup() !void {
|
||||
return switch (ws2_32.WSACleanup()) {
|
||||
0 => {},
|
||||
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => return error.NotInitialized,
|
||||
.WSAENETDOWN => return error.NetworkNotAvailable,
|
||||
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
|
||||
else => |err| return unexpectedWSAError(err),
|
||||
},
|
||||
else => unreachable,
|
||||
|
||||
@@ -163,16 +163,16 @@ pub const IPPROTO_UDP = 17;
|
||||
pub const IPPROTO_ICMPV6 = 58;
|
||||
pub const IPPROTO_RM = 113;
|
||||
|
||||
pub const AI_PASSIVE = 0x00001;
|
||||
pub const AI_CANONNAME = 0x00002;
|
||||
pub const AI_NUMERICHOST = 0x00004;
|
||||
pub const AI_NUMERICSERV = 0x00008;
|
||||
pub const AI_ADDRCONFIG = 0x00400;
|
||||
pub const AI_V4MAPPED = 0x00800;
|
||||
pub const AI_NON_AUTHORITATIVE = 0x04000;
|
||||
pub const AI_SECURE = 0x08000;
|
||||
pub const AI_PASSIVE = 0x00001;
|
||||
pub const AI_CANONNAME = 0x00002;
|
||||
pub const AI_NUMERICHOST = 0x00004;
|
||||
pub const AI_NUMERICSERV = 0x00008;
|
||||
pub const AI_ADDRCONFIG = 0x00400;
|
||||
pub const AI_V4MAPPED = 0x00800;
|
||||
pub const AI_NON_AUTHORITATIVE = 0x04000;
|
||||
pub const AI_SECURE = 0x08000;
|
||||
pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
|
||||
pub const AI_DISABLE_IDN_ENCODING = 0x80000;
|
||||
pub const AI_DISABLE_IDN_ENCODING = 0x80000;
|
||||
|
||||
pub const FIONBIO = -2147195266;
|
||||
|
||||
|
||||
+7
-34
@@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct {
|
||||
pub const ArgIteratorWindows = struct {
|
||||
index: usize,
|
||||
cmd_line: [*]const u8,
|
||||
in_quote: bool,
|
||||
quote_count: usize,
|
||||
seen_quote_count: usize,
|
||||
|
||||
pub const NextError = error{OutOfMemory};
|
||||
|
||||
@@ -295,9 +292,6 @@ pub const ArgIteratorWindows = struct {
|
||||
return ArgIteratorWindows{
|
||||
.index = 0,
|
||||
.cmd_line = cmd_line,
|
||||
.in_quote = false,
|
||||
.quote_count = countQuotes(cmd_line),
|
||||
.seen_quote_count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct {
|
||||
}
|
||||
|
||||
var backslash_count: usize = 0;
|
||||
var in_quote = false;
|
||||
while (true) : (self.index += 1) {
|
||||
const byte = self.cmd_line[self.index];
|
||||
switch (byte) {
|
||||
@@ -335,14 +330,14 @@ pub const ArgIteratorWindows = struct {
|
||||
'"' => {
|
||||
const quote_is_real = backslash_count % 2 == 0;
|
||||
if (quote_is_real) {
|
||||
self.seen_quote_count += 1;
|
||||
in_quote = !in_quote;
|
||||
}
|
||||
},
|
||||
'\\' => {
|
||||
backslash_count += 1;
|
||||
},
|
||||
' ', '\t' => {
|
||||
if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
|
||||
if (!in_quote) {
|
||||
return true;
|
||||
}
|
||||
backslash_count = 0;
|
||||
@@ -360,6 +355,7 @@ pub const ArgIteratorWindows = struct {
|
||||
defer buf.deinit();
|
||||
|
||||
var backslash_count: usize = 0;
|
||||
var in_quote = false;
|
||||
while (true) : (self.index += 1) {
|
||||
const byte = self.cmd_line[self.index];
|
||||
switch (byte) {
|
||||
@@ -370,10 +366,7 @@ pub const ArgIteratorWindows = struct {
|
||||
backslash_count = 0;
|
||||
|
||||
if (quote_is_real) {
|
||||
self.seen_quote_count += 1;
|
||||
if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) {
|
||||
try buf.append('"');
|
||||
}
|
||||
in_quote = !in_quote;
|
||||
} else {
|
||||
try buf.append('"');
|
||||
}
|
||||
@@ -384,7 +377,7 @@ pub const ArgIteratorWindows = struct {
|
||||
' ', '\t' => {
|
||||
try self.emitBackslashes(&buf, backslash_count);
|
||||
backslash_count = 0;
|
||||
if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
|
||||
if (in_quote) {
|
||||
try buf.append(byte);
|
||||
} else {
|
||||
return buf.toOwnedSlice();
|
||||
@@ -405,26 +398,6 @@ pub const ArgIteratorWindows = struct {
|
||||
try buf.append('\\');
|
||||
}
|
||||
}
|
||||
|
||||
fn countQuotes(cmd_line: [*]const u8) usize {
|
||||
var result: usize = 0;
|
||||
var backslash_count: usize = 0;
|
||||
var index: usize = 0;
|
||||
while (true) : (index += 1) {
|
||||
const byte = cmd_line[index];
|
||||
switch (byte) {
|
||||
0 => return result,
|
||||
'\\' => backslash_count += 1,
|
||||
'"' => {
|
||||
result += 1 - (backslash_count % 2);
|
||||
backslash_count = 0;
|
||||
},
|
||||
else => {
|
||||
backslash_count = 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const ArgIterator = struct {
|
||||
@@ -578,7 +551,7 @@ test "windows arg parsing" {
|
||||
testWindowsCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" });
|
||||
testWindowsCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" });
|
||||
testWindowsCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" });
|
||||
testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "\"d", "f" });
|
||||
testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "d f" });
|
||||
|
||||
testWindowsCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{
|
||||
".\\..\\zig-cache\\build",
|
||||
|
||||
@@ -49,6 +49,7 @@ pub const heap = @import("heap.zig");
|
||||
pub const http = @import("http.zig");
|
||||
pub const io = @import("io.zig");
|
||||
pub const json = @import("json.zig");
|
||||
pub const log = @import("log.zig");
|
||||
pub const macho = @import("macho.zig");
|
||||
pub const math = @import("math.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
|
||||
@@ -235,6 +235,22 @@ pub const Utf8Iterator = struct {
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Look ahead at the next n codepoints without advancing the iterator.
|
||||
/// If fewer than n codepoints are available, then return the remainder of the string.
|
||||
pub fn peek(it: *Utf8Iterator, n: usize) []const u8 {
|
||||
const original_i = it.i;
|
||||
defer it.i = original_i;
|
||||
|
||||
var end_ix = original_i;
|
||||
var found: usize = 0;
|
||||
while (found < n) : (found += 1) {
|
||||
const next_codepoint = it.nextCodepointSlice() orelse return it.bytes[original_i..];
|
||||
end_ix += next_codepoint.len;
|
||||
}
|
||||
|
||||
return it.bytes[original_i..end_ix];
|
||||
}
|
||||
};
|
||||
|
||||
pub const Utf16LeIterator = struct {
|
||||
@@ -451,6 +467,31 @@ fn testMiscInvalidUtf8() void {
|
||||
testValid("\xee\x80\x80", 0xe000);
|
||||
}
|
||||
|
||||
test "utf8 iterator peeking" {
|
||||
comptime testUtf8Peeking();
|
||||
testUtf8Peeking();
|
||||
}
|
||||
|
||||
fn testUtf8Peeking() void {
|
||||
const s = Utf8View.initComptime("noël");
|
||||
var it = s.iterator();
|
||||
|
||||
testing.expect(std.mem.eql(u8, "n", it.nextCodepointSlice().?));
|
||||
|
||||
testing.expect(std.mem.eql(u8, "o", it.peek(1)));
|
||||
testing.expect(std.mem.eql(u8, "oë", it.peek(2)));
|
||||
testing.expect(std.mem.eql(u8, "oël", it.peek(3)));
|
||||
testing.expect(std.mem.eql(u8, "oël", it.peek(4)));
|
||||
testing.expect(std.mem.eql(u8, "oël", it.peek(10)));
|
||||
|
||||
testing.expect(std.mem.eql(u8, "o", it.nextCodepointSlice().?));
|
||||
testing.expect(std.mem.eql(u8, "ë", it.nextCodepointSlice().?));
|
||||
testing.expect(std.mem.eql(u8, "l", it.nextCodepointSlice().?));
|
||||
testing.expect(it.nextCodepointSlice() == null);
|
||||
|
||||
testing.expect(std.mem.eql(u8, &[_]u8{}, it.peek(1)));
|
||||
}
|
||||
|
||||
fn testError(bytes: []const u8, expected_err: anyerror) void {
|
||||
testing.expectError(expected_err, testDecode(bytes));
|
||||
}
|
||||
|
||||
@@ -937,7 +937,6 @@ const Parser = struct {
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
while_prefix.body = try p.expectNode(parseAssignExpr, .{
|
||||
.ExpectedBlockOrAssignment = .{ .token = p.tok_i },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user