mirror of
https://github.com/ziglang/zig.git
synced 2025-12-12 17:23:09 +00:00
Merge remote-tracking branch 'origin/master' into copy-elision-3
This commit is contained in:
commit
04c25efe11
@ -479,6 +479,7 @@ set(ZIG_STD_FILES
|
|||||||
"crypto.zig"
|
"crypto.zig"
|
||||||
"crypto/blake2.zig"
|
"crypto/blake2.zig"
|
||||||
"crypto/chacha20.zig"
|
"crypto/chacha20.zig"
|
||||||
|
"crypto/gimli.zig"
|
||||||
"crypto/hmac.zig"
|
"crypto/hmac.zig"
|
||||||
"crypto/md5.zig"
|
"crypto/md5.zig"
|
||||||
"crypto/poly1305.zig"
|
"crypto/poly1305.zig"
|
||||||
|
|||||||
@ -4927,7 +4927,7 @@ test "peer type resolution: *const T and ?*T" {
|
|||||||
<li>The {#link|Integers#} {#syntax#}u0{#endsyntax#} and {#syntax#}i0{#endsyntax#}.</li>
|
<li>The {#link|Integers#} {#syntax#}u0{#endsyntax#} and {#syntax#}i0{#endsyntax#}.</li>
|
||||||
<li>{#link|Arrays#} and {#link|Vectors#} with len 0, or with an element type that is a zero bit type.</li>
|
<li>{#link|Arrays#} and {#link|Vectors#} with len 0, or with an element type that is a zero bit type.</li>
|
||||||
<li>An {#link|enum#} with only 1 tag.</li>
|
<li>An {#link|enum#} with only 1 tag.</li>
|
||||||
<li>An {#link|struct#} with all fields being zero bit types.</li>
|
<li>A {#link|struct#} with all fields being zero bit types.</li>
|
||||||
<li>A {#link|union#} with only 1 field which is a zero bit type.</li>
|
<li>A {#link|union#} with only 1 field which is a zero bit type.</li>
|
||||||
<li>{#link|Pointers to Zero Bit Types#} are themselves zero bit types.</li>
|
<li>{#link|Pointers to Zero Bit Types#} are themselves zero bit types.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -27,11 +27,13 @@ Buf *buf_sprintf(const char *format, ...)
|
|||||||
Buf *buf_vprintf(const char *format, va_list ap);
|
Buf *buf_vprintf(const char *format, va_list ap);
|
||||||
|
|
||||||
static inline size_t buf_len(Buf *buf) {
|
static inline size_t buf_len(Buf *buf) {
|
||||||
|
assert(buf);
|
||||||
assert(buf->list.length);
|
assert(buf->list.length);
|
||||||
return buf->list.length - 1;
|
return buf->list.length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char *buf_ptr(Buf *buf) {
|
static inline char *buf_ptr(Buf *buf) {
|
||||||
|
assert(buf);
|
||||||
assert(buf->list.length);
|
assert(buf->list.length);
|
||||||
return buf->list.items;
|
return buf->list.items;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8732,6 +8732,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
|||||||
|
|
||||||
Termination term;
|
Termination term;
|
||||||
ZigList<const char *> args = {};
|
ZigList<const char *> args = {};
|
||||||
|
args.append(buf_ptr(self_exe_path));
|
||||||
args.append("cc");
|
args.append("cc");
|
||||||
|
|
||||||
Buf *out_dep_path = buf_sprintf("%s.d", buf_ptr(out_obj_path));
|
Buf *out_dep_path = buf_sprintf("%s.d", buf_ptr(out_obj_path));
|
||||||
@ -8750,7 +8751,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
|||||||
if (g->verbose_cc) {
|
if (g->verbose_cc) {
|
||||||
print_zig_cc_cmd("zig", &args);
|
print_zig_cc_cmd("zig", &args);
|
||||||
}
|
}
|
||||||
os_spawn_process(buf_ptr(self_exe_path), args, &term);
|
os_spawn_process(args, &term);
|
||||||
if (term.how != TerminationIdClean || term.code != 0) {
|
if (term.how != TerminationIdClean || term.code != 0) {
|
||||||
fprintf(stderr, "\nThe following command failed:\n");
|
fprintf(stderr, "\nThe following command failed:\n");
|
||||||
print_zig_cc_cmd(buf_ptr(self_exe_path), &args);
|
print_zig_cc_cmd(buf_ptr(self_exe_path), &args);
|
||||||
|
|||||||
@ -18046,7 +18046,9 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
|
|||||||
case ZigTypeIdPromise:
|
case ZigTypeIdPromise:
|
||||||
case ZigTypeIdVector:
|
case ZigTypeIdVector:
|
||||||
{
|
{
|
||||||
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
|
ResolveStatus needed_status = (align_bytes == 0) ?
|
||||||
|
ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown;
|
||||||
|
if ((err = type_resolve(ira->codegen, child_type, needed_status)))
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
||||||
is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero);
|
is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero);
|
||||||
@ -19901,10 +19903,11 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr
|
|||||||
true, false, PtrLenUnknown,
|
true, false, PtrLenUnknown,
|
||||||
0, 0, 0, false);
|
0, 0, 0, false);
|
||||||
fn_decl_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
|
fn_decl_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
|
||||||
if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
|
if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) {
|
||||||
fn_decl_fields[6].data.x_optional = create_const_vals(1);
|
fn_decl_fields[6].data.x_optional = create_const_vals(1);
|
||||||
ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
|
ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
|
||||||
init_const_slice(ira->codegen, fn_decl_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true);
|
init_const_slice(ira->codegen, fn_decl_fields[6].data.x_optional, lib_name, 0,
|
||||||
|
buf_len(fn_node->lib_name), true);
|
||||||
} else {
|
} else {
|
||||||
fn_decl_fields[6].data.x_optional = nullptr;
|
fn_decl_fields[6].data.x_optional = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -153,6 +153,7 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b
|
|||||||
const char *cc_exe = getenv("CC");
|
const char *cc_exe = getenv("CC");
|
||||||
cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe;
|
cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe;
|
||||||
ZigList<const char *> args = {};
|
ZigList<const char *> args = {};
|
||||||
|
args.append(cc_exe);
|
||||||
args.append("-E");
|
args.append("-E");
|
||||||
args.append("-Wp,-v");
|
args.append("-Wp,-v");
|
||||||
args.append("-xc");
|
args.append("-xc");
|
||||||
@ -166,7 +167,7 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b
|
|||||||
Buf *out_stderr = buf_alloc();
|
Buf *out_stderr = buf_alloc();
|
||||||
Buf *out_stdout = buf_alloc();
|
Buf *out_stdout = buf_alloc();
|
||||||
Error err;
|
Error err;
|
||||||
if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) {
|
if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err));
|
fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err));
|
||||||
}
|
}
|
||||||
@ -277,12 +278,13 @@ Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirnam
|
|||||||
const char *cc_exe = getenv("CC");
|
const char *cc_exe = getenv("CC");
|
||||||
cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe;
|
cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe;
|
||||||
ZigList<const char *> args = {};
|
ZigList<const char *> args = {};
|
||||||
|
args.append(cc_exe);
|
||||||
args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file)));
|
args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file)));
|
||||||
Termination term;
|
Termination term;
|
||||||
Buf *out_stderr = buf_alloc();
|
Buf *out_stderr = buf_alloc();
|
||||||
Buf *out_stdout = buf_alloc();
|
Buf *out_stdout = buf_alloc();
|
||||||
Error err;
|
Error err;
|
||||||
if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) {
|
if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) {
|
||||||
if (err == ErrorFileNotFound)
|
if (err == ErrorFileNotFound)
|
||||||
return ErrorNoCCompilerInstalled;
|
return ErrorNoCCompilerInstalled;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
|||||||
@ -1721,10 +1721,11 @@ void codegen_link(CodeGen *g) {
|
|||||||
if (g->system_linker_hack && g->zig_target->os == OsMacOSX) {
|
if (g->system_linker_hack && g->zig_target->os == OsMacOSX) {
|
||||||
Termination term;
|
Termination term;
|
||||||
ZigList<const char *> args = {};
|
ZigList<const char *> args = {};
|
||||||
|
args.append("ld");
|
||||||
for (size_t i = 1; i < lj.args.length; i += 1) {
|
for (size_t i = 1; i < lj.args.length; i += 1) {
|
||||||
args.append(lj.args.at(i));
|
args.append(lj.args.at(i));
|
||||||
}
|
}
|
||||||
os_spawn_process("ld", args, &term);
|
os_spawn_process(args, &term);
|
||||||
if (term.how != TerminationIdClean || term.code != 0) {
|
if (term.how != TerminationIdClean || term.code != 0) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/main.cpp
28
src/main.cpp
@ -467,6 +467,7 @@ int main(int argc, char **argv) {
|
|||||||
init_all_targets();
|
init_all_targets();
|
||||||
|
|
||||||
ZigList<const char *> args = {0};
|
ZigList<const char *> args = {0};
|
||||||
|
args.append(NULL); // placeholder
|
||||||
args.append(zig_exe_path);
|
args.append(zig_exe_path);
|
||||||
args.append(NULL); // placeholder
|
args.append(NULL); // placeholder
|
||||||
args.append(NULL); // placeholder
|
args.append(NULL); // placeholder
|
||||||
@ -525,8 +526,8 @@ int main(int argc, char **argv) {
|
|||||||
g->enable_time_report = timing_info;
|
g->enable_time_report = timing_info;
|
||||||
codegen_set_out_name(g, buf_create_from_str("build"));
|
codegen_set_out_name(g, buf_create_from_str("build"));
|
||||||
|
|
||||||
args.items[1] = buf_ptr(&build_file_dirname);
|
args.items[2] = buf_ptr(&build_file_dirname);
|
||||||
args.items[2] = buf_ptr(&full_cache_dir);
|
args.items[3] = buf_ptr(&full_cache_dir);
|
||||||
|
|
||||||
bool build_file_exists;
|
bool build_file_exists;
|
||||||
if ((err = os_file_exists(&build_file_abs, &build_file_exists))) {
|
if ((err = os_file_exists(&build_file_abs, &build_file_exists))) {
|
||||||
@ -580,12 +581,14 @@ int main(int argc, char **argv) {
|
|||||||
codegen_build_and_link(g);
|
codegen_build_and_link(g);
|
||||||
|
|
||||||
Termination term;
|
Termination term;
|
||||||
os_spawn_process(buf_ptr(&g->output_file_path), args, &term);
|
args.items[0] = buf_ptr(&g->output_file_path);
|
||||||
|
os_spawn_process(args, &term);
|
||||||
if (term.how != TerminationIdClean || term.code != 0) {
|
if (term.how != TerminationIdClean || term.code != 0) {
|
||||||
fprintf(stderr, "\nBuild failed. The following command failed:\n");
|
fprintf(stderr, "\nBuild failed. The following command failed:\n");
|
||||||
fprintf(stderr, "%s", buf_ptr(&g->output_file_path));
|
const char *prefix = "";
|
||||||
for (size_t i = 0; i < args.length; i += 1) {
|
for (size_t i = 0; i < args.length; i += 1) {
|
||||||
fprintf(stderr, " %s", args.at(i));
|
fprintf(stderr, "%s%s", prefix, args.at(i));
|
||||||
|
prefix = " ";
|
||||||
}
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
@ -1161,7 +1164,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
args.pop();
|
args.pop();
|
||||||
Termination term;
|
Termination term;
|
||||||
os_spawn_process(exec_path, args, &term);
|
os_spawn_process(args, &term);
|
||||||
return term.code;
|
return term.code;
|
||||||
} else if (cmd == CmdBuild) {
|
} else if (cmd == CmdBuild) {
|
||||||
if (g->enable_cache) {
|
if (g->enable_cache) {
|
||||||
@ -1213,17 +1216,10 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Termination term;
|
Termination term;
|
||||||
if (test_exec_args.length > 0) {
|
if (test_exec_args.length == 0) {
|
||||||
ZigList<const char *> rest_args = {0};
|
test_exec_args.append(buf_ptr(test_exe_path));
|
||||||
for (size_t i = 1; i < test_exec_args.length; i += 1) {
|
|
||||||
rest_args.append(test_exec_args.at(i));
|
|
||||||
}
|
|
||||||
os_spawn_process(test_exec_args.items[0], rest_args, &term);
|
|
||||||
} else {
|
|
||||||
ZigList<const char *> no_args = {0};
|
|
||||||
os_spawn_process(buf_ptr(test_exe_path), no_args, &term);
|
|
||||||
}
|
}
|
||||||
|
os_spawn_process(test_exec_args, &term);
|
||||||
if (term.how != TerminationIdClean || term.code != 0) {
|
if (term.how != TerminationIdClean || term.code != 0) {
|
||||||
fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
|
fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
|
||||||
fprintf(stderr, "%s\n", buf_ptr(test_exe_path));
|
fprintf(stderr, "%s\n", buf_ptr(test_exe_path));
|
||||||
|
|||||||
56
src/os.cpp
56
src/os.cpp
@ -105,16 +105,15 @@ static void populate_termination(Termination *term, int status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, Termination *term) {
|
static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) {
|
||||||
const char **argv = allocate<const char *>(args.length + 2);
|
const char **argv = allocate<const char *>(args.length + 1);
|
||||||
argv[0] = exe;
|
|
||||||
argv[args.length + 1] = nullptr;
|
|
||||||
for (size_t i = 0; i < args.length; i += 1) {
|
for (size_t i = 0; i < args.length; i += 1) {
|
||||||
argv[i + 1] = args.at(i);
|
argv[i] = args.at(i);
|
||||||
}
|
}
|
||||||
|
argv[args.length] = nullptr;
|
||||||
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int rc = posix_spawnp(&pid, exe, nullptr, nullptr, const_cast<char *const*>(argv), environ);
|
int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast<char *const*>(argv), environ);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
zig_panic("posix_spawn failed: %s", strerror(rc));
|
zig_panic("posix_spawn failed: %s", strerror(rc));
|
||||||
}
|
}
|
||||||
@ -126,16 +125,14 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ZIG_OS_WINDOWS)
|
#if defined(ZIG_OS_WINDOWS)
|
||||||
static void os_windows_create_command_line(Buf *command_line, const char *exe, ZigList<const char *> &args) {
|
|
||||||
|
static void os_windows_create_command_line(Buf *command_line, ZigList<const char *> &args) {
|
||||||
buf_resize(command_line, 0);
|
buf_resize(command_line, 0);
|
||||||
|
char *prefix = "\"";
|
||||||
buf_append_char(command_line, '\"');
|
|
||||||
buf_append_str(command_line, exe);
|
|
||||||
buf_append_char(command_line, '\"');
|
|
||||||
|
|
||||||
for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) {
|
for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) {
|
||||||
buf_append_str(command_line, " \"");
|
|
||||||
const char *arg = args.at(arg_i);
|
const char *arg = args.at(arg_i);
|
||||||
|
buf_append_str(command_line, prefix);
|
||||||
|
prefix = " \"";
|
||||||
size_t arg_len = strlen(arg);
|
size_t arg_len = strlen(arg);
|
||||||
for (size_t c_i = 0; c_i < arg_len; c_i += 1) {
|
for (size_t c_i = 0; c_i < arg_len; c_i += 1) {
|
||||||
if (arg[c_i] == '\"') {
|
if (arg[c_i] == '\"') {
|
||||||
@ -147,14 +144,15 @@ static void os_windows_create_command_line(Buf *command_line, const char *exe, Z
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void os_spawn_process_windows(const char *exe, ZigList<const char *> &args, Termination *term) {
|
static void os_spawn_process_windows(ZigList<const char *> &args, Termination *term) {
|
||||||
Buf command_line = BUF_INIT;
|
Buf command_line = BUF_INIT;
|
||||||
os_windows_create_command_line(&command_line, exe, args);
|
os_windows_create_command_line(&command_line, args);
|
||||||
|
|
||||||
PROCESS_INFORMATION piProcInfo = {0};
|
PROCESS_INFORMATION piProcInfo = {0};
|
||||||
STARTUPINFO siStartInfo = {0};
|
STARTUPINFO siStartInfo = {0};
|
||||||
siStartInfo.cb = sizeof(STARTUPINFO);
|
siStartInfo.cb = sizeof(STARTUPINFO);
|
||||||
|
|
||||||
|
const char *exe = args.at(0);
|
||||||
BOOL success = CreateProcessA(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
|
BOOL success = CreateProcessA(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
|
||||||
&siStartInfo, &piProcInfo);
|
&siStartInfo, &piProcInfo);
|
||||||
|
|
||||||
@ -173,11 +171,11 @@ static void os_spawn_process_windows(const char *exe, ZigList<const char *> &arg
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term) {
|
void os_spawn_process(ZigList<const char *> &args, Termination *term) {
|
||||||
#if defined(ZIG_OS_WINDOWS)
|
#if defined(ZIG_OS_WINDOWS)
|
||||||
os_spawn_process_windows(exe, args, term);
|
os_spawn_process_windows(args, term);
|
||||||
#elif defined(ZIG_OS_POSIX)
|
#elif defined(ZIG_OS_POSIX)
|
||||||
os_spawn_process_posix(exe, args, term);
|
os_spawn_process_posix(args, term);
|
||||||
#else
|
#else
|
||||||
#error "missing os_spawn_process implementation"
|
#error "missing os_spawn_process implementation"
|
||||||
#endif
|
#endif
|
||||||
@ -785,7 +783,7 @@ Error os_file_exists(Buf *full_path, bool *result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ZIG_OS_POSIX)
|
#if defined(ZIG_OS_POSIX)
|
||||||
static Error os_exec_process_posix(const char *exe, ZigList<const char *> &args,
|
static Error os_exec_process_posix(ZigList<const char *> &args,
|
||||||
Termination *term, Buf *out_stderr, Buf *out_stdout)
|
Termination *term, Buf *out_stderr, Buf *out_stdout)
|
||||||
{
|
{
|
||||||
int stdin_pipe[2];
|
int stdin_pipe[2];
|
||||||
@ -817,13 +815,12 @@ static Error os_exec_process_posix(const char *exe, ZigList<const char *> &args,
|
|||||||
if (dup2(stderr_pipe[1], STDERR_FILENO) == -1)
|
if (dup2(stderr_pipe[1], STDERR_FILENO) == -1)
|
||||||
zig_panic("dup2 failed");
|
zig_panic("dup2 failed");
|
||||||
|
|
||||||
const char **argv = allocate<const char *>(args.length + 2);
|
const char **argv = allocate<const char *>(args.length + 1);
|
||||||
argv[0] = exe;
|
argv[args.length] = nullptr;
|
||||||
argv[args.length + 1] = nullptr;
|
|
||||||
for (size_t i = 0; i < args.length; i += 1) {
|
for (size_t i = 0; i < args.length; i += 1) {
|
||||||
argv[i + 1] = args.at(i);
|
argv[i] = args.at(i);
|
||||||
}
|
}
|
||||||
execvp(exe, const_cast<char * const *>(argv));
|
execvp(argv[0], const_cast<char * const *>(argv));
|
||||||
Error report_err = ErrorUnexpected;
|
Error report_err = ErrorUnexpected;
|
||||||
if (errno == ENOENT) {
|
if (errno == ENOENT) {
|
||||||
report_err = ErrorFileNotFound;
|
report_err = ErrorFileNotFound;
|
||||||
@ -874,11 +871,11 @@ static Error os_exec_process_posix(const char *exe, ZigList<const char *> &args,
|
|||||||
// LocalFree(messageBuffer);
|
// LocalFree(messageBuffer);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
static Error os_exec_process_windows(const char *exe, ZigList<const char *> &args,
|
static Error os_exec_process_windows(ZigList<const char *> &args,
|
||||||
Termination *term, Buf *out_stderr, Buf *out_stdout)
|
Termination *term, Buf *out_stderr, Buf *out_stdout)
|
||||||
{
|
{
|
||||||
Buf command_line = BUF_INIT;
|
Buf command_line = BUF_INIT;
|
||||||
os_windows_create_command_line(&command_line, exe, args);
|
os_windows_create_command_line(&command_line, args);
|
||||||
|
|
||||||
HANDLE g_hChildStd_IN_Rd = NULL;
|
HANDLE g_hChildStd_IN_Rd = NULL;
|
||||||
HANDLE g_hChildStd_IN_Wr = NULL;
|
HANDLE g_hChildStd_IN_Wr = NULL;
|
||||||
@ -925,6 +922,7 @@ static Error os_exec_process_windows(const char *exe, ZigList<const char *> &arg
|
|||||||
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
|
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
|
||||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
|
const char *exe = args.at(0);
|
||||||
BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
|
BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
|
||||||
&siStartInfo, &piProcInfo);
|
&siStartInfo, &piProcInfo);
|
||||||
|
|
||||||
@ -1005,13 +1003,13 @@ Error os_execv(const char *exe, const char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Error os_exec_process(const char *exe, ZigList<const char *> &args,
|
Error os_exec_process(ZigList<const char *> &args,
|
||||||
Termination *term, Buf *out_stderr, Buf *out_stdout)
|
Termination *term, Buf *out_stderr, Buf *out_stdout)
|
||||||
{
|
{
|
||||||
#if defined(ZIG_OS_WINDOWS)
|
#if defined(ZIG_OS_WINDOWS)
|
||||||
return os_exec_process_windows(exe, args, term, out_stderr, out_stdout);
|
return os_exec_process_windows(args, term, out_stderr, out_stdout);
|
||||||
#elif defined(ZIG_OS_POSIX)
|
#elif defined(ZIG_OS_POSIX)
|
||||||
return os_exec_process_posix(exe, args, term, out_stderr, out_stdout);
|
return os_exec_process_posix(args, term, out_stderr, out_stdout);
|
||||||
#else
|
#else
|
||||||
#error "missing os_exec_process implementation"
|
#error "missing os_exec_process implementation"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -100,8 +100,8 @@ struct OsFileAttr {
|
|||||||
|
|
||||||
int os_init(void);
|
int os_init(void);
|
||||||
|
|
||||||
void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term);
|
void os_spawn_process(ZigList<const char *> &args, Termination *term);
|
||||||
Error os_exec_process(const char *exe, ZigList<const char *> &args,
|
Error os_exec_process(ZigList<const char *> &args,
|
||||||
Termination *term, Buf *out_stderr, Buf *out_stdout);
|
Termination *term, Buf *out_stderr, Buf *out_stdout);
|
||||||
Error os_execv(const char *exe, const char **argv);
|
Error os_execv(const char *exe, const char **argv);
|
||||||
|
|
||||||
|
|||||||
@ -100,7 +100,7 @@ pub fn Queue(comptime T: type) type {
|
|||||||
pub fn isEmpty(self: *Self) bool {
|
pub fn isEmpty(self: *Self) bool {
|
||||||
const held = self.mutex.acquire();
|
const held = self.mutex.acquire();
|
||||||
defer held.release();
|
defer held.release();
|
||||||
return self.head != null;
|
return self.head == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump(self: *Self) void {
|
pub fn dump(self: *Self) void {
|
||||||
@ -172,12 +172,14 @@ test "std.atomic.Queue" {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (builtin.single_threaded) {
|
if (builtin.single_threaded) {
|
||||||
|
expect(context.queue.isEmpty());
|
||||||
{
|
{
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < put_thread_count) : (i += 1) {
|
while (i < put_thread_count) : (i += 1) {
|
||||||
expect(startPuts(&context) == 0);
|
expect(startPuts(&context) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expect(!context.queue.isEmpty());
|
||||||
context.puts_done = 1;
|
context.puts_done = 1;
|
||||||
{
|
{
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@ -185,7 +187,10 @@ test "std.atomic.Queue" {
|
|||||||
expect(startGets(&context) == 0);
|
expect(startGets(&context) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expect(context.queue.isEmpty());
|
||||||
} else {
|
} else {
|
||||||
|
expect(context.queue.isEmpty());
|
||||||
|
|
||||||
var putters: [put_thread_count]*std.Thread = undefined;
|
var putters: [put_thread_count]*std.Thread = undefined;
|
||||||
for (putters) |*t| {
|
for (putters) |*t| {
|
||||||
t.* = try std.Thread.spawn(&context, startPuts);
|
t.* = try std.Thread.spawn(&context, startPuts);
|
||||||
@ -200,6 +205,8 @@ test "std.atomic.Queue" {
|
|||||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||||
for (getters) |t|
|
for (getters) |t|
|
||||||
t.wait();
|
t.wait();
|
||||||
|
|
||||||
|
expect(context.queue.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.put_sum != context.get_sum) {
|
if (context.put_sum != context.get_sum) {
|
||||||
@ -250,6 +257,7 @@ fn startGets(ctx: *Context) u8 {
|
|||||||
|
|
||||||
test "std.atomic.Queue single-threaded" {
|
test "std.atomic.Queue single-threaded" {
|
||||||
var queue = Queue(i32).init();
|
var queue = Queue(i32).init();
|
||||||
|
expect(queue.isEmpty());
|
||||||
|
|
||||||
var node_0 = Queue(i32).Node{
|
var node_0 = Queue(i32).Node{
|
||||||
.data = 0,
|
.data = 0,
|
||||||
@ -257,6 +265,7 @@ test "std.atomic.Queue single-threaded" {
|
|||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
};
|
};
|
||||||
queue.put(&node_0);
|
queue.put(&node_0);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
var node_1 = Queue(i32).Node{
|
var node_1 = Queue(i32).Node{
|
||||||
.data = 1,
|
.data = 1,
|
||||||
@ -264,8 +273,10 @@ test "std.atomic.Queue single-threaded" {
|
|||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
};
|
};
|
||||||
queue.put(&node_1);
|
queue.put(&node_1);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
expect(queue.get().?.data == 0);
|
expect(queue.get().?.data == 0);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
var node_2 = Queue(i32).Node{
|
var node_2 = Queue(i32).Node{
|
||||||
.data = 2,
|
.data = 2,
|
||||||
@ -273,6 +284,7 @@ test "std.atomic.Queue single-threaded" {
|
|||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
};
|
};
|
||||||
queue.put(&node_2);
|
queue.put(&node_2);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
var node_3 = Queue(i32).Node{
|
var node_3 = Queue(i32).Node{
|
||||||
.data = 3,
|
.data = 3,
|
||||||
@ -280,10 +292,13 @@ test "std.atomic.Queue single-threaded" {
|
|||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
};
|
};
|
||||||
queue.put(&node_3);
|
queue.put(&node_3);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
expect(queue.get().?.data == 1);
|
expect(queue.get().?.data == 1);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
expect(queue.get().?.data == 2);
|
expect(queue.get().?.data == 2);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
var node_4 = Queue(i32).Node{
|
var node_4 = Queue(i32).Node{
|
||||||
.data = 4,
|
.data = 4,
|
||||||
@ -291,13 +306,17 @@ test "std.atomic.Queue single-threaded" {
|
|||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
};
|
};
|
||||||
queue.put(&node_4);
|
queue.put(&node_4);
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
expect(queue.get().?.data == 3);
|
expect(queue.get().?.data == 3);
|
||||||
node_3.next = null;
|
node_3.next = null;
|
||||||
|
expect(!queue.isEmpty());
|
||||||
|
|
||||||
expect(queue.get().?.data == 4);
|
expect(queue.get().?.data == 4);
|
||||||
|
expect(queue.isEmpty());
|
||||||
|
|
||||||
expect(queue.get() == null);
|
expect(queue.get() == null);
|
||||||
|
expect(queue.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
test "std.atomic.Queue dump" {
|
test "std.atomic.Queue dump" {
|
||||||
|
|||||||
@ -543,25 +543,32 @@ pub const ChildProcess = struct {
|
|||||||
|
|
||||||
const PATH = try process.getEnvVarOwned(self.allocator, "PATH");
|
const PATH = try process.getEnvVarOwned(self.allocator, "PATH");
|
||||||
defer self.allocator.free(PATH);
|
defer self.allocator.free(PATH);
|
||||||
|
const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT");
|
||||||
|
defer self.allocator.free(PATHEXT);
|
||||||
|
|
||||||
var it = mem.tokenize(PATH, ";");
|
var it = mem.tokenize(PATH, ";");
|
||||||
while (it.next()) |search_path| {
|
retry: while (it.next()) |search_path| {
|
||||||
const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_name });
|
var ext_it = mem.tokenize(PATHEXT, ";");
|
||||||
defer self.allocator.free(joined_path);
|
while (ext_it.next()) |app_ext| {
|
||||||
|
const app_basename = try mem.concat(self.allocator, u8, [_][]const u8{app_name[0..app_name.len - 1], app_ext});
|
||||||
|
defer self.allocator.free(app_basename);
|
||||||
|
|
||||||
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
|
const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_basename });
|
||||||
defer self.allocator.free(joined_path_w);
|
defer self.allocator.free(joined_path);
|
||||||
|
|
||||||
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
|
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
|
||||||
break;
|
defer self.allocator.free(joined_path_w);
|
||||||
} else |err| if (err == error.FileNotFound) {
|
|
||||||
continue;
|
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
|
||||||
} else {
|
break :retry;
|
||||||
return err;
|
} else |err| switch (err) {
|
||||||
|
error.FileNotFound => { continue; },
|
||||||
|
error.AccessDenied => { continue; },
|
||||||
|
else => { return err; },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Every other error would have been returned earlier.
|
return no_path_err; // return the original error
|
||||||
return error.FileNotFound;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,8 @@ pub const Sha3_256 = sha3.Sha3_256;
|
|||||||
pub const Sha3_384 = sha3.Sha3_384;
|
pub const Sha3_384 = sha3.Sha3_384;
|
||||||
pub const Sha3_512 = sha3.Sha3_512;
|
pub const Sha3_512 = sha3.Sha3_512;
|
||||||
|
|
||||||
|
pub const gimli = @import("crypto/gimli.zig");
|
||||||
|
|
||||||
const blake2 = @import("crypto/blake2.zig");
|
const blake2 = @import("crypto/blake2.zig");
|
||||||
pub const Blake2s224 = blake2.Blake2s224;
|
pub const Blake2s224 = blake2.Blake2s224;
|
||||||
pub const Blake2s256 = blake2.Blake2s256;
|
pub const Blake2s256 = blake2.Blake2s256;
|
||||||
@ -38,6 +40,7 @@ pub const randomBytes = std.os.getrandom;
|
|||||||
test "crypto" {
|
test "crypto" {
|
||||||
_ = @import("crypto/blake2.zig");
|
_ = @import("crypto/blake2.zig");
|
||||||
_ = @import("crypto/chacha20.zig");
|
_ = @import("crypto/chacha20.zig");
|
||||||
|
_ = @import("crypto/gimli.zig");
|
||||||
_ = @import("crypto/hmac.zig");
|
_ = @import("crypto/hmac.zig");
|
||||||
_ = @import("crypto/md5.zig");
|
_ = @import("crypto/md5.zig");
|
||||||
_ = @import("crypto/poly1305.zig");
|
_ = @import("crypto/poly1305.zig");
|
||||||
|
|||||||
168
std/crypto/gimli.zig
Normal file
168
std/crypto/gimli.zig
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Gimli is a 384-bit permutation designed to achieve high security with high
|
||||||
|
// performance across a broad range of platforms, including 64-bit Intel/AMD
|
||||||
|
// server CPUs, 64-bit and 32-bit ARM smartphone CPUs, 32-bit ARM
|
||||||
|
// microcontrollers, 8-bit AVR microcontrollers, FPGAs, ASICs without
|
||||||
|
// side-channel protection, and ASICs with side-channel protection.
|
||||||
|
//
|
||||||
|
// https://gimli.cr.yp.to/
|
||||||
|
// https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/gimli-spec.pdf
|
||||||
|
|
||||||
|
const std = @import("../std.zig");
|
||||||
|
const mem = std.mem;
|
||||||
|
const math = std.math;
|
||||||
|
const debug = std.debug;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const testing = std.testing;
|
||||||
|
const htest = @import("test.zig");
|
||||||
|
|
||||||
|
pub const State = struct {
|
||||||
|
pub const BLOCKBYTES = 48;
|
||||||
|
pub const RATE = 16;
|
||||||
|
|
||||||
|
// TODO: https://github.com/ziglang/zig/issues/2673#issuecomment-501763017
|
||||||
|
data: [BLOCKBYTES / 4]u32,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn toSlice(self: *Self) []u8 {
|
||||||
|
return @sliceToBytes(self.data[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toSliceConst(self: *Self) []const u8 {
|
||||||
|
return @sliceToBytes(self.data[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn permute(self: *Self) void {
|
||||||
|
const state = &self.data;
|
||||||
|
var round = u32(24);
|
||||||
|
while (round > 0) : (round -= 1) {
|
||||||
|
var column = usize(0);
|
||||||
|
while (column < 4) : (column += 1) {
|
||||||
|
const x = math.rotl(u32, state[column], 24);
|
||||||
|
const y = math.rotl(u32, state[4 + column], 9);
|
||||||
|
const z = state[8 + column];
|
||||||
|
state[8 + column] = ((x ^ (z << 1)) ^ ((y & z) << 2));
|
||||||
|
state[4 + column] = ((y ^ x) ^ ((x | z) << 1));
|
||||||
|
state[column] = ((z ^ y) ^ ((x & y) << 3));
|
||||||
|
}
|
||||||
|
switch (round & 3) {
|
||||||
|
0 => {
|
||||||
|
mem.swap(u32, &state[0], &state[1]);
|
||||||
|
mem.swap(u32, &state[2], &state[3]);
|
||||||
|
state[0] ^= round | 0x9e377900;
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
mem.swap(u32, &state[0], &state[2]);
|
||||||
|
mem.swap(u32, &state[1], &state[3]);
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn squeeze(self: *Self, out: []u8) void {
|
||||||
|
var i = usize(0);
|
||||||
|
while (i + RATE <= out.len) : (i += RATE) {
|
||||||
|
self.permute();
|
||||||
|
mem.copy(u8, out[i..], self.toSliceConst()[0..RATE]);
|
||||||
|
}
|
||||||
|
const leftover = out.len - i;
|
||||||
|
if (leftover != 0) {
|
||||||
|
self.permute();
|
||||||
|
mem.copy(u8, out[i..], self.toSliceConst()[0..leftover]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "permute" {
|
||||||
|
// test vector from gimli-20170627
|
||||||
|
var state = State{
|
||||||
|
.data = blk: {
|
||||||
|
var input: [12]u32 = undefined;
|
||||||
|
var i = u32(0);
|
||||||
|
while (i < 12) : (i += 1) {
|
||||||
|
input[i] = i * i * i + i *% 0x9e3779b9;
|
||||||
|
}
|
||||||
|
testing.expectEqualSlices(u32, input, [_]u32{
|
||||||
|
0x00000000, 0x9e3779ba, 0x3c6ef37a, 0xdaa66d46,
|
||||||
|
0x78dde724, 0x1715611a, 0xb54cdb2e, 0x53845566,
|
||||||
|
0xf1bbcfc8, 0x8ff34a5a, 0x2e2ac522, 0xcc624026,
|
||||||
|
});
|
||||||
|
break :blk input;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
state.permute();
|
||||||
|
testing.expectEqualSlices(u32, state.data, [_]u32{
|
||||||
|
0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68,
|
||||||
|
0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8,
|
||||||
|
0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Hash = struct {
|
||||||
|
state: State,
|
||||||
|
buf_off: usize,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init() Self {
|
||||||
|
return Self{
|
||||||
|
.state = State{
|
||||||
|
.data = [_]u32{0} ** (State.BLOCKBYTES / 4),
|
||||||
|
},
|
||||||
|
.buf_off = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Also known as 'absorb'
|
||||||
|
pub fn update(self: *Self, data: []const u8) void {
|
||||||
|
const buf = self.state.toSlice();
|
||||||
|
var in = data;
|
||||||
|
while (in.len > 0) {
|
||||||
|
var left = State.RATE - self.buf_off;
|
||||||
|
if (left == 0) {
|
||||||
|
self.state.permute();
|
||||||
|
self.buf_off = 0;
|
||||||
|
left = State.RATE;
|
||||||
|
}
|
||||||
|
const ps = math.min(in.len, left);
|
||||||
|
for (buf[self.buf_off .. self.buf_off + ps]) |*p, i| {
|
||||||
|
p.* ^= in[i];
|
||||||
|
}
|
||||||
|
self.buf_off += ps;
|
||||||
|
in = in[ps..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finish the current hashing operation, writing the hash to `out`
|
||||||
|
///
|
||||||
|
/// From 4.9 "Application to hashing"
|
||||||
|
/// By default, Gimli-Hash provides a fixed-length output of 32 bytes
|
||||||
|
/// (the concatenation of two 16-byte blocks). However, Gimli-Hash can
|
||||||
|
/// be used as an “extendable one-way function” (XOF).
|
||||||
|
pub fn final(self: *Self, out: []u8) void {
|
||||||
|
const buf = self.state.toSlice();
|
||||||
|
|
||||||
|
// XOR 1 into the next byte of the state
|
||||||
|
buf[self.buf_off] ^= 1;
|
||||||
|
// XOR 1 into the last byte of the state, position 47.
|
||||||
|
buf[buf.len - 1] ^= 1;
|
||||||
|
|
||||||
|
self.state.squeeze(out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn hash(out: []u8, in: []const u8) void {
|
||||||
|
var st = Hash.init();
|
||||||
|
st.update(in);
|
||||||
|
st.final(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "hash" {
|
||||||
|
// a test vector (30) from NIST KAT submission.
|
||||||
|
var msg: [58 / 2]u8 = undefined;
|
||||||
|
try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C");
|
||||||
|
var md: [32]u8 = undefined;
|
||||||
|
hash(&md, msg);
|
||||||
|
htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", md);
|
||||||
|
}
|
||||||
453
std/fmt.zig
453
std/fmt.zig
@ -13,7 +13,13 @@ pub const default_max_depth = 3;
|
|||||||
/// Renders fmt string with args, calling output with slices of bytes.
|
/// Renders fmt string with args, calling output with slices of bytes.
|
||||||
/// If `output` returns an error, the error is returned from `format` and
|
/// If `output` returns an error, the error is returned from `format` and
|
||||||
/// `output` is not called again.
|
/// `output` is not called again.
|
||||||
pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void {
|
pub fn format(
|
||||||
|
context: var,
|
||||||
|
comptime Errors: type,
|
||||||
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
args: ...,
|
||||||
|
) Errors!void {
|
||||||
const State = enum {
|
const State = enum {
|
||||||
Start,
|
Start,
|
||||||
OpenBrace,
|
OpenBrace,
|
||||||
@ -28,7 +34,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
|
|
||||||
inline for (fmt) |c, i| {
|
inline for (fmt) |c, i| {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
State.Start => switch (c) {
|
.Start => switch (c) {
|
||||||
'{' => {
|
'{' => {
|
||||||
if (start_index < i) {
|
if (start_index < i) {
|
||||||
try output(context, fmt[start_index..i]);
|
try output(context, fmt[start_index..i]);
|
||||||
@ -45,7 +51,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
State.OpenBrace => switch (c) {
|
.OpenBrace => switch (c) {
|
||||||
'{' => {
|
'{' => {
|
||||||
state = State.Start;
|
state = State.Start;
|
||||||
start_index = i;
|
start_index = i;
|
||||||
@ -61,14 +67,14 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
state = State.FormatString;
|
state = State.FormatString;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
State.CloseBrace => switch (c) {
|
.CloseBrace => switch (c) {
|
||||||
'}' => {
|
'}' => {
|
||||||
state = State.Start;
|
state = State.Start;
|
||||||
start_index = i;
|
start_index = i;
|
||||||
},
|
},
|
||||||
else => @compileError("Single '}' encountered in format string"),
|
else => @compileError("Single '}' encountered in format string"),
|
||||||
},
|
},
|
||||||
State.FormatString => switch (c) {
|
.FormatString => switch (c) {
|
||||||
'}' => {
|
'}' => {
|
||||||
const s = start_index + 1;
|
const s = start_index + 1;
|
||||||
try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth);
|
try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth);
|
||||||
@ -78,7 +84,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
State.Pointer => switch (c) {
|
.Pointer => switch (c) {
|
||||||
'}' => {
|
'}' => {
|
||||||
try output(context, @typeName(@typeOf(args[next_arg]).Child));
|
try output(context, @typeName(@typeOf(args[next_arg]).Child));
|
||||||
try output(context, "@");
|
try output(context, "@");
|
||||||
@ -114,88 +120,93 @@ pub fn formatType(
|
|||||||
) Errors!void {
|
) Errors!void {
|
||||||
const T = @typeOf(value);
|
const T = @typeOf(value);
|
||||||
switch (@typeInfo(T)) {
|
switch (@typeInfo(T)) {
|
||||||
builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => {
|
.ComptimeInt, .Int, .Float => {
|
||||||
return formatValue(value, fmt, context, Errors, output);
|
return formatValue(value, fmt, context, Errors, output);
|
||||||
},
|
},
|
||||||
builtin.TypeId.Void => {
|
.Void => {
|
||||||
return output(context, "void");
|
return output(context, "void");
|
||||||
},
|
},
|
||||||
builtin.TypeId.Bool => {
|
.Bool => {
|
||||||
return output(context, if (value) "true" else "false");
|
return output(context, if (value) "true" else "false");
|
||||||
},
|
},
|
||||||
builtin.TypeId.Optional => {
|
.Optional => {
|
||||||
if (value) |payload| {
|
if (value) |payload| {
|
||||||
return formatType(payload, fmt, context, Errors, output, max_depth);
|
return formatType(payload, fmt, context, Errors, output, max_depth);
|
||||||
} else {
|
} else {
|
||||||
return output(context, "null");
|
return output(context, "null");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builtin.TypeId.ErrorUnion => {
|
.ErrorUnion => {
|
||||||
if (value) |payload| {
|
if (value) |payload| {
|
||||||
return formatType(payload, fmt, context, Errors, output, max_depth);
|
return formatType(payload, fmt, context, Errors, output, max_depth);
|
||||||
} else |err| {
|
} else |err| {
|
||||||
return formatType(err, fmt, context, Errors, output, max_depth);
|
return formatType(err, fmt, context, Errors, output, max_depth);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builtin.TypeId.ErrorSet => {
|
.ErrorSet => {
|
||||||
try output(context, "error.");
|
try output(context, "error.");
|
||||||
return output(context, @errorName(value));
|
return output(context, @errorName(value));
|
||||||
},
|
},
|
||||||
builtin.TypeId.Promise => {
|
.Promise => {
|
||||||
return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
|
return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
|
||||||
},
|
},
|
||||||
builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
|
.Enum => {
|
||||||
if (comptime std.meta.trait.hasFn("format")(T)) return value.format(fmt, context, Errors, output);
|
if (comptime std.meta.trait.hasFn("format")(T)) {
|
||||||
|
return value.format(fmt, context, Errors, output);
|
||||||
|
}
|
||||||
|
|
||||||
try output(context, @typeName(T));
|
try output(context, @typeName(T));
|
||||||
switch (comptime @typeId(T)) {
|
try output(context, ".");
|
||||||
builtin.TypeId.Enum => {
|
return formatType(@tagName(value), "", context, Errors, output, max_depth);
|
||||||
try output(context, ".");
|
|
||||||
try formatType(@tagName(value), "", context, Errors, output, max_depth);
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
builtin.TypeId.Struct => {
|
|
||||||
if (max_depth == 0) {
|
|
||||||
return output(context, "{ ... }");
|
|
||||||
}
|
|
||||||
comptime var field_i = 0;
|
|
||||||
inline while (field_i < @memberCount(T)) : (field_i += 1) {
|
|
||||||
if (field_i == 0) {
|
|
||||||
try output(context, "{ .");
|
|
||||||
} else {
|
|
||||||
try output(context, ", .");
|
|
||||||
}
|
|
||||||
try output(context, @memberName(T, field_i));
|
|
||||||
try output(context, " = ");
|
|
||||||
try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1);
|
|
||||||
}
|
|
||||||
try output(context, " }");
|
|
||||||
},
|
|
||||||
builtin.TypeId.Union => {
|
|
||||||
if (max_depth == 0) {
|
|
||||||
return output(context, "{ ... }");
|
|
||||||
}
|
|
||||||
const info = @typeInfo(T).Union;
|
|
||||||
if (info.tag_type) |UnionTagType| {
|
|
||||||
try output(context, "{ .");
|
|
||||||
try output(context, @tagName(UnionTagType(value)));
|
|
||||||
try output(context, " = ");
|
|
||||||
inline for (info.fields) |u_field| {
|
|
||||||
if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
|
|
||||||
try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try output(context, " }");
|
|
||||||
} else {
|
|
||||||
try format(context, Errors, output, "@{x}", @ptrToInt(&value));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
|
.Union => {
|
||||||
builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
|
if (comptime std.meta.trait.hasFn("format")(T)) {
|
||||||
|
return value.format(fmt, context, Errors, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
try output(context, @typeName(T));
|
||||||
|
if (max_depth == 0) {
|
||||||
|
return output(context, "{ ... }");
|
||||||
|
}
|
||||||
|
const info = @typeInfo(T).Union;
|
||||||
|
if (info.tag_type) |UnionTagType| {
|
||||||
|
try output(context, "{ .");
|
||||||
|
try output(context, @tagName(UnionTagType(value)));
|
||||||
|
try output(context, " = ");
|
||||||
|
inline for (info.fields) |u_field| {
|
||||||
|
if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
|
||||||
|
try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try output(context, " }");
|
||||||
|
} else {
|
||||||
|
try format(context, Errors, output, "@{x}", @ptrToInt(&value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Struct => {
|
||||||
|
if (comptime std.meta.trait.hasFn("format")(T)) {
|
||||||
|
return value.format(fmt, context, Errors, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
try output(context, @typeName(T));
|
||||||
|
if (max_depth == 0) {
|
||||||
|
return output(context, "{ ... }");
|
||||||
|
}
|
||||||
|
comptime var field_i = 0;
|
||||||
|
inline while (field_i < @memberCount(T)) : (field_i += 1) {
|
||||||
|
if (field_i == 0) {
|
||||||
|
try output(context, "{ .");
|
||||||
|
} else {
|
||||||
|
try output(context, ", .");
|
||||||
|
}
|
||||||
|
try output(context, @memberName(T, field_i));
|
||||||
|
try output(context, " = ");
|
||||||
|
try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1);
|
||||||
|
}
|
||||||
|
try output(context, " }");
|
||||||
|
},
|
||||||
|
.Pointer => |ptr_info| switch (ptr_info.size) {
|
||||||
|
.One => switch (@typeInfo(ptr_info.child)) {
|
||||||
builtin.TypeId.Array => |info| {
|
builtin.TypeId.Array => |info| {
|
||||||
if (info.child == u8) {
|
if (info.child == u8) {
|
||||||
return formatText(value, fmt, context, Errors, output);
|
return formatText(value, fmt, context, Errors, output);
|
||||||
@ -207,7 +218,7 @@ pub fn formatType(
|
|||||||
},
|
},
|
||||||
else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
|
else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
|
||||||
},
|
},
|
||||||
builtin.TypeInfo.Pointer.Size.Many => {
|
.Many => {
|
||||||
if (ptr_info.child == u8) {
|
if (ptr_info.child == u8) {
|
||||||
if (fmt.len > 0 and fmt[0] == 's') {
|
if (fmt.len > 0 and fmt[0] == 's') {
|
||||||
const len = mem.len(u8, value);
|
const len = mem.len(u8, value);
|
||||||
@ -216,7 +227,7 @@ pub fn formatType(
|
|||||||
}
|
}
|
||||||
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
|
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
|
||||||
},
|
},
|
||||||
builtin.TypeInfo.Pointer.Size.Slice => {
|
.Slice => {
|
||||||
if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
|
if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
|
||||||
return formatText(value, fmt, context, Errors, output);
|
return formatText(value, fmt, context, Errors, output);
|
||||||
}
|
}
|
||||||
@ -225,17 +236,17 @@ pub fn formatType(
|
|||||||
}
|
}
|
||||||
return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr));
|
return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr));
|
||||||
},
|
},
|
||||||
builtin.TypeInfo.Pointer.Size.C => {
|
.C => {
|
||||||
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
|
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
builtin.TypeId.Array => |info| {
|
.Array => |info| {
|
||||||
if (info.child == u8) {
|
if (info.child == u8) {
|
||||||
return formatText(value, fmt, context, Errors, output);
|
return formatText(value, fmt, context, Errors, output);
|
||||||
}
|
}
|
||||||
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value));
|
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value));
|
||||||
},
|
},
|
||||||
builtin.TypeId.Fn => {
|
.Fn => {
|
||||||
return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value));
|
return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value));
|
||||||
},
|
},
|
||||||
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
|
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
|
||||||
@ -249,24 +260,24 @@ fn formatValue(
|
|||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
) Errors!void {
|
) Errors!void {
|
||||||
if (fmt.len > 0) {
|
if (fmt.len > 0 and fmt[0] == 'B') {
|
||||||
if (fmt[0] == 'B') {
|
comptime var width: ?usize = null;
|
||||||
comptime var width: ?usize = null;
|
if (fmt.len > 1) {
|
||||||
if (fmt.len > 1) {
|
if (fmt[1] == 'i') {
|
||||||
if (fmt[1] == 'i') {
|
if (fmt.len > 2) {
|
||||||
if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
|
width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
|
||||||
return formatBytes(value, width, 1024, context, Errors, output);
|
|
||||||
}
|
}
|
||||||
width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
|
return formatBytes(value, width, 1024, context, Errors, output);
|
||||||
}
|
}
|
||||||
return formatBytes(value, width, 1000, context, Errors, output);
|
width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
|
||||||
}
|
}
|
||||||
|
return formatBytes(value, width, 1000, context, Errors, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const T = @typeOf(value);
|
const T = @typeOf(value);
|
||||||
switch (@typeId(T)) {
|
switch (@typeId(T)) {
|
||||||
builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output),
|
.Float => return formatFloatValue(value, fmt, context, Errors, output),
|
||||||
builtin.TypeId.Int, builtin.TypeId.ComptimeInt => return formatIntValue(value, fmt, context, Errors, output),
|
.Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output),
|
||||||
else => comptime unreachable,
|
else => comptime unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -797,7 +808,7 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "fmt.parseInt" {
|
test "parseInt" {
|
||||||
testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10);
|
testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10);
|
||||||
testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10);
|
testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10);
|
||||||
testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
|
testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
|
||||||
@ -828,7 +839,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "fmt.parseUnsigned" {
|
test "parseUnsigned" {
|
||||||
testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
|
testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
|
||||||
testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
|
testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
|
||||||
testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10));
|
testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10));
|
||||||
@ -913,7 +924,7 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
|
|||||||
size.* += bytes.len;
|
size.* += bytes.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "buf print int" {
|
test "bufPrintInt" {
|
||||||
var buffer: [100]u8 = undefined;
|
var buffer: [100]u8 = undefined;
|
||||||
const buf = buffer[0..];
|
const buf = buffer[0..];
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
|
||||||
@ -949,7 +960,7 @@ test "parse unsigned comptime" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "fmt.format" {
|
test "fmt.optional" {
|
||||||
{
|
{
|
||||||
const value: ?i32 = 1234;
|
const value: ?i32 = 1234;
|
||||||
try testFmt("optional: 1234\n", "optional: {}\n", value);
|
try testFmt("optional: 1234\n", "optional: {}\n", value);
|
||||||
@ -958,6 +969,9 @@ test "fmt.format" {
|
|||||||
const value: ?i32 = null;
|
const value: ?i32 = null;
|
||||||
try testFmt("optional: null\n", "optional: {}\n", value);
|
try testFmt("optional: null\n", "optional: {}\n", value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.error" {
|
||||||
{
|
{
|
||||||
const value: anyerror!i32 = 1234;
|
const value: anyerror!i32 = 1234;
|
||||||
try testFmt("error union: 1234\n", "error union: {}\n", value);
|
try testFmt("error union: 1234\n", "error union: {}\n", value);
|
||||||
@ -966,10 +980,16 @@ test "fmt.format" {
|
|||||||
const value: anyerror!i32 = error.InvalidChar;
|
const value: anyerror!i32 = error.InvalidChar;
|
||||||
try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value);
|
try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.int.small" {
|
||||||
{
|
{
|
||||||
const value: u3 = 0b101;
|
const value: u3 = 0b101;
|
||||||
try testFmt("u3: 5\n", "u3: {}\n", value);
|
try testFmt("u3: 5\n", "u3: {}\n", value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.int.specifier" {
|
||||||
{
|
{
|
||||||
const value: u8 = 'a';
|
const value: u8 = 'a';
|
||||||
try testFmt("u8: a\n", "u8: {c}\n", value);
|
try testFmt("u8: a\n", "u8: {c}\n", value);
|
||||||
@ -978,6 +998,9 @@ test "fmt.format" {
|
|||||||
const value: u8 = 0b1100;
|
const value: u8 = 0b1100;
|
||||||
try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value);
|
try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.buffer" {
|
||||||
{
|
{
|
||||||
var buf1: [32]u8 = undefined;
|
var buf1: [32]u8 = undefined;
|
||||||
var context = BufPrintContext{ .remaining = buf1[0..] };
|
var context = BufPrintContext{ .remaining = buf1[0..] };
|
||||||
@ -995,6 +1018,9 @@ test "fmt.format" {
|
|||||||
res = buf1[0 .. buf1.len - context.remaining.len];
|
res = buf1[0 .. buf1.len - context.remaining.len];
|
||||||
testing.expect(mem.eql(u8, res, "1100"));
|
testing.expect(mem.eql(u8, res, "1100"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.array" {
|
||||||
{
|
{
|
||||||
const value: [3]u8 = "abc";
|
const value: [3]u8 = "abc";
|
||||||
try testFmt("array: abc\n", "array: {}\n", value);
|
try testFmt("array: abc\n", "array: {}\n", value);
|
||||||
@ -1007,6 +1033,9 @@ test "fmt.format" {
|
|||||||
&value,
|
&value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.slice" {
|
||||||
{
|
{
|
||||||
const value: []const u8 = "abc";
|
const value: []const u8 = "abc";
|
||||||
try testFmt("slice: abc\n", "slice: {}\n", value);
|
try testFmt("slice: abc\n", "slice: {}\n", value);
|
||||||
@ -1015,6 +1044,12 @@ test "fmt.format" {
|
|||||||
const value = @intToPtr([*]const []const u8, 0xdeadbeef)[0..0];
|
const value = @intToPtr([*]const []const u8, 0xdeadbeef)[0..0];
|
||||||
try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value);
|
try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
|
||||||
|
try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.pointer" {
|
||||||
{
|
{
|
||||||
const value = @intToPtr(*i32, 0xdeadbeef);
|
const value = @intToPtr(*i32, 0xdeadbeef);
|
||||||
try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
|
try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
|
||||||
@ -1028,12 +1063,19 @@ test "fmt.format" {
|
|||||||
const value = @intToPtr(fn () void, 0xdeadbeef);
|
const value = @intToPtr(fn () void, 0xdeadbeef);
|
||||||
try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value);
|
try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value);
|
||||||
}
|
}
|
||||||
try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
|
}
|
||||||
try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
|
|
||||||
|
test "fmt.cstr" {
|
||||||
try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
|
try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
|
||||||
try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C");
|
try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.filesize" {
|
||||||
try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
|
try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
|
||||||
try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
|
try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.struct" {
|
||||||
{
|
{
|
||||||
const Struct = struct {
|
const Struct = struct {
|
||||||
field: u8,
|
field: u8,
|
||||||
@ -1050,15 +1092,19 @@ test "fmt.format" {
|
|||||||
const value = Struct{ .a = 0, .b = 1 };
|
const value = Struct{ .a = 0, .b = 1 };
|
||||||
try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value);
|
try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value);
|
||||||
}
|
}
|
||||||
{
|
}
|
||||||
const Enum = enum {
|
|
||||||
One,
|
test "fmt.enum" {
|
||||||
Two,
|
const Enum = enum {
|
||||||
};
|
One,
|
||||||
const value = Enum.Two;
|
Two,
|
||||||
try testFmt("enum: Enum.Two\n", "enum: {}\n", value);
|
};
|
||||||
try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
|
const value = Enum.Two;
|
||||||
}
|
try testFmt("enum: Enum.Two\n", "enum: {}\n", value);
|
||||||
|
try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.float.scientific" {
|
||||||
{
|
{
|
||||||
var buf1: [32]u8 = undefined;
|
var buf1: [32]u8 = undefined;
|
||||||
const value: f32 = 1.34;
|
const value: f32 = 1.34;
|
||||||
@ -1088,6 +1134,9 @@ test "fmt.format" {
|
|||||||
testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n"));
|
testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.float.scientific.precision" {
|
||||||
{
|
{
|
||||||
var buf1: [32]u8 = undefined;
|
var buf1: [32]u8 = undefined;
|
||||||
const value: f64 = 1.409706e-42;
|
const value: f64 = 1.409706e-42;
|
||||||
@ -1114,6 +1163,9 @@ test "fmt.format" {
|
|||||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||||
testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n"));
|
testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.float.special" {
|
||||||
{
|
{
|
||||||
var buf1: [32]u8 = undefined;
|
var buf1: [32]u8 = undefined;
|
||||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||||
@ -1136,6 +1188,9 @@ test "fmt.format" {
|
|||||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||||
testing.expect(mem.eql(u8, result, "f64: -inf\n"));
|
testing.expect(mem.eql(u8, result, "f64: -inf\n"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.float.decimal" {
|
||||||
{
|
{
|
||||||
var buf1: [64]u8 = undefined;
|
var buf1: [64]u8 = undefined;
|
||||||
const value: f64 = 1.52314e+29;
|
const value: f64 = 1.52314e+29;
|
||||||
@ -1216,7 +1271,9 @@ test "fmt.format" {
|
|||||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||||
testing.expect(mem.eql(u8, result, "f64: 0.00000\n"));
|
testing.expect(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||||
}
|
}
|
||||||
// libc checks
|
}
|
||||||
|
|
||||||
|
test "fmt.float.libc.sanity" {
|
||||||
{
|
{
|
||||||
var buf1: [32]u8 = undefined;
|
var buf1: [32]u8 = undefined;
|
||||||
const value: f64 = f64(@bitCast(f32, u32(916964781)));
|
const value: f64 = f64(@bitCast(f32, u32(916964781)));
|
||||||
@ -1267,127 +1324,127 @@ test "fmt.format" {
|
|||||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||||
testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
|
testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
|
||||||
}
|
}
|
||||||
//custom type format
|
}
|
||||||
{
|
|
||||||
const Vec2 = struct {
|
|
||||||
const SelfType = @This();
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
|
|
||||||
pub fn format(
|
test "fmt.custom" {
|
||||||
self: SelfType,
|
const Vec2 = struct {
|
||||||
comptime fmt: []const u8,
|
const SelfType = @This();
|
||||||
context: var,
|
x: f32,
|
||||||
comptime Errors: type,
|
y: f32,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
|
||||||
) Errors!void {
|
pub fn format(
|
||||||
switch (fmt.len) {
|
self: SelfType,
|
||||||
0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
|
comptime fmt: []const u8,
|
||||||
1 => switch (fmt[0]) {
|
context: var,
|
||||||
//point format
|
comptime Errors: type,
|
||||||
'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
//dimension format
|
) Errors!void {
|
||||||
'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
|
switch (fmt.len) {
|
||||||
else => unreachable,
|
0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
|
||||||
},
|
1 => switch (fmt[0]) {
|
||||||
|
//point format
|
||||||
|
'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
|
||||||
|
//dimension format
|
||||||
|
'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
},
|
||||||
|
else => unreachable,
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var buf1: [32]u8 = undefined;
|
var buf1: [32]u8 = undefined;
|
||||||
var value = Vec2{
|
var value = Vec2{
|
||||||
.x = 10.2,
|
.x = 10.2,
|
||||||
.y = 2.22,
|
.y = 2.22,
|
||||||
};
|
};
|
||||||
try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
|
try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
|
||||||
try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
|
try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
|
||||||
|
|
||||||
// same thing but not passing a pointer
|
// same thing but not passing a pointer
|
||||||
try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
|
try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
|
||||||
try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
|
try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
|
||||||
}
|
}
|
||||||
//struct format
|
|
||||||
{
|
|
||||||
const S = struct {
|
|
||||||
a: u32,
|
|
||||||
b: anyerror,
|
|
||||||
};
|
|
||||||
|
|
||||||
const inst = S{
|
test "fmt.struct" {
|
||||||
.a = 456,
|
const S = struct {
|
||||||
.b = error.Unused,
|
a: u32,
|
||||||
};
|
b: anyerror,
|
||||||
|
};
|
||||||
|
|
||||||
try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
|
const inst = S{
|
||||||
}
|
.a = 456,
|
||||||
//union format
|
.b = error.Unused,
|
||||||
{
|
};
|
||||||
const TU = union(enum) {
|
|
||||||
float: f32,
|
|
||||||
int: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
const UU = union {
|
try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
|
||||||
float: f32,
|
}
|
||||||
int: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EU = extern union {
|
test "fmt.union" {
|
||||||
float: f32,
|
const TU = union(enum) {
|
||||||
int: u32,
|
float: f32,
|
||||||
};
|
int: u32,
|
||||||
|
};
|
||||||
|
|
||||||
const tu_inst = TU{ .int = 123 };
|
const UU = union {
|
||||||
const uu_inst = UU{ .int = 456 };
|
float: f32,
|
||||||
const eu_inst = EU{ .float = 321.123 };
|
int: u32,
|
||||||
|
};
|
||||||
|
|
||||||
try testFmt("TU{ .int = 123 }", "{}", tu_inst);
|
const EU = extern union {
|
||||||
|
float: f32,
|
||||||
|
int: u32,
|
||||||
|
};
|
||||||
|
|
||||||
var buf: [100]u8 = undefined;
|
const tu_inst = TU{ .int = 123 };
|
||||||
const uu_result = try bufPrint(buf[0..], "{}", uu_inst);
|
const uu_inst = UU{ .int = 456 };
|
||||||
testing.expect(mem.eql(u8, uu_result[0..3], "UU@"));
|
const eu_inst = EU{ .float = 321.123 };
|
||||||
|
|
||||||
const eu_result = try bufPrint(buf[0..], "{}", eu_inst);
|
try testFmt("TU{ .int = 123 }", "{}", tu_inst);
|
||||||
testing.expect(mem.eql(u8, uu_result[0..3], "EU@"));
|
|
||||||
}
|
|
||||||
//enum format
|
|
||||||
{
|
|
||||||
const E = enum {
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
Three,
|
|
||||||
};
|
|
||||||
|
|
||||||
const inst = E.Two;
|
var buf: [100]u8 = undefined;
|
||||||
|
const uu_result = try bufPrint(buf[0..], "{}", uu_inst);
|
||||||
|
testing.expect(mem.eql(u8, uu_result[0..3], "UU@"));
|
||||||
|
|
||||||
try testFmt("E.Two", "{}", inst);
|
const eu_result = try bufPrint(buf[0..], "{}", eu_inst);
|
||||||
}
|
testing.expect(mem.eql(u8, uu_result[0..3], "EU@"));
|
||||||
//self-referential struct format
|
}
|
||||||
{
|
|
||||||
const S = struct {
|
|
||||||
const SelfType = @This();
|
|
||||||
a: ?*SelfType,
|
|
||||||
};
|
|
||||||
|
|
||||||
var inst = S{
|
test "fmt.enum" {
|
||||||
.a = null,
|
const E = enum {
|
||||||
};
|
One,
|
||||||
inst.a = &inst;
|
Two,
|
||||||
|
Three,
|
||||||
|
};
|
||||||
|
|
||||||
try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
|
const inst = E.Two;
|
||||||
}
|
|
||||||
//print bytes as hex
|
try testFmt("E.Two", "{}", inst);
|
||||||
{
|
}
|
||||||
const some_bytes = "\xCA\xFE\xBA\xBE";
|
|
||||||
try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
|
test "fmt.struct.self-referential" {
|
||||||
try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
|
const S = struct {
|
||||||
//Test Slices
|
const SelfType = @This();
|
||||||
try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]);
|
a: ?*SelfType,
|
||||||
try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]);
|
};
|
||||||
const bytes_with_zeros = "\x00\x0E\xBA\xBE";
|
|
||||||
try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros);
|
var inst = S{
|
||||||
}
|
.a = null,
|
||||||
|
};
|
||||||
|
inst.a = &inst;
|
||||||
|
|
||||||
|
try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fmt.bytes.hex" {
|
||||||
|
const some_bytes = "\xCA\xFE\xBA\xBE";
|
||||||
|
try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
|
||||||
|
try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
|
||||||
|
//Test Slices
|
||||||
|
try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]);
|
||||||
|
try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]);
|
||||||
|
const bytes_with_zeros = "\x00\x0E\xBA\xBE";
|
||||||
|
try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {
|
fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {
|
||||||
|
|||||||
@ -564,12 +564,12 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
|
|||||||
},
|
},
|
||||||
|
|
||||||
builtin.TypeId.Float => |info| {
|
builtin.TypeId.Float => |info| {
|
||||||
return autoHash(@bitCast(@IntType(false, info.bits), key), rng);
|
return autoHash(@bitCast(@IntType(false, info.bits), key), rng, HashInt);
|
||||||
},
|
},
|
||||||
builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng),
|
builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng, HashInt),
|
||||||
builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng),
|
builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng, HashInt),
|
||||||
builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng),
|
builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng, HashInt),
|
||||||
builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng),
|
builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng, HashInt),
|
||||||
|
|
||||||
builtin.TypeId.BoundFn,
|
builtin.TypeId.BoundFn,
|
||||||
builtin.TypeId.ComptimeFloat,
|
builtin.TypeId.ComptimeFloat,
|
||||||
|
|||||||
37
std/mem.zig
37
std/mem.zig
@ -996,6 +996,43 @@ test "mem.join" {
|
|||||||
testing.expect(eql(u8, try join(a, ",", [_][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c"));
|
testing.expect(eql(u8, try join(a, ",", [_][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copies each T from slices into a new slice that exactly holds all the elements.
|
||||||
|
pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {
|
||||||
|
if (slices.len == 0) return (([*]T)(undefined))[0..0];
|
||||||
|
|
||||||
|
const total_len = blk: {
|
||||||
|
var sum: usize = 0;
|
||||||
|
for (slices) |slice| {
|
||||||
|
sum += slice.len;
|
||||||
|
}
|
||||||
|
break :blk sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buf = try allocator.alloc(T, total_len);
|
||||||
|
errdefer allocator.free(buf);
|
||||||
|
|
||||||
|
var buf_index: usize = 0;
|
||||||
|
for (slices) |slice| {
|
||||||
|
copy(T, buf[buf_index..], slice);
|
||||||
|
buf_index += slice.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need for shrink since buf is exactly the correct size.
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "concat" {
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
|
||||||
|
testing.expect(eql(u8, try concat(a, u8, [_][]const u8{ "abc", "def", "ghi" }), "abcdefghi"));
|
||||||
|
testing.expect(eql(u32, try concat(a, u32, [_][]const u32{
|
||||||
|
[_]u32{ 0, 1 },
|
||||||
|
[_]u32{ 2, 3, 4 },
|
||||||
|
[_]u32{},
|
||||||
|
[_]u32{5},
|
||||||
|
}), [_]u32{ 0, 1, 2, 3, 4, 5 }));
|
||||||
|
}
|
||||||
|
|
||||||
test "testStringEquality" {
|
test "testStringEquality" {
|
||||||
testing.expect(eql(u8, "abcd", "abcd"));
|
testing.expect(eql(u8, "abcd", "abcd"));
|
||||||
testing.expect(!eql(u8, "abcdef", "abZdef"));
|
testing.expect(!eql(u8, "abcdef", "abZdef"));
|
||||||
|
|||||||
@ -632,6 +632,7 @@ pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) G
|
|||||||
|
|
||||||
pub const CreateProcessError = error{
|
pub const CreateProcessError = error{
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
|
AccessDenied,
|
||||||
InvalidName,
|
InvalidName,
|
||||||
Unexpected,
|
Unexpected,
|
||||||
};
|
};
|
||||||
@ -663,6 +664,7 @@ pub fn CreateProcessW(
|
|||||||
switch (kernel32.GetLastError()) {
|
switch (kernel32.GetLastError()) {
|
||||||
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
||||||
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||||
|
ERROR.ACCESS_DENIED => return error.AccessDenied,
|
||||||
ERROR.INVALID_PARAMETER => unreachable,
|
ERROR.INVALID_PARAMETER => unreachable,
|
||||||
ERROR.INVALID_NAME => return error.InvalidName,
|
ERROR.INVALID_NAME => return error.InvalidName,
|
||||||
else => |err| return unexpectedError(err),
|
else => |err| return unexpectedError(err),
|
||||||
|
|||||||
@ -54,3 +54,14 @@ test "comptime slices are disambiguated" {
|
|||||||
expect(sliceSum([_]u8{ 1, 2 }) == 3);
|
expect(sliceSum([_]u8{ 1, 2 }) == 3);
|
||||||
expect(sliceSum([_]u8{ 3, 4 }) == 7);
|
expect(sliceSum([_]u8{ 3, 4 }) == 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "slice type with custom alignment" {
|
||||||
|
const LazilyResolvedType = struct {
|
||||||
|
anything: i32,
|
||||||
|
};
|
||||||
|
var slice: []align(32) LazilyResolvedType = undefined;
|
||||||
|
var array: [10]LazilyResolvedType align(32) = undefined;
|
||||||
|
slice = &array;
|
||||||
|
slice[1].anything = 42;
|
||||||
|
expect(array[1].anything == 42);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
const expect = @import("std").testing.expect;
|
const std = @import("std");
|
||||||
const mem = @import("std").mem;
|
const expect = std.testing.expect;
|
||||||
const TypeInfo = @import("builtin").TypeInfo;
|
const mem = std.mem;
|
||||||
const TypeId = @import("builtin").TypeId;
|
const builtin = @import("builtin");
|
||||||
|
const TypeInfo = builtin.TypeInfo;
|
||||||
|
const TypeId = builtin.TypeId;
|
||||||
|
|
||||||
test "type info: tag type, void info" {
|
test "type info: tag type, void info" {
|
||||||
testBasic();
|
testBasic();
|
||||||
@ -317,3 +319,20 @@ test "type info: TypeId -> TypeInfo impl cast" {
|
|||||||
_ = passTypeInfo(TypeId.Void);
|
_ = passTypeInfo(TypeId.Void);
|
||||||
_ = comptime passTypeInfo(TypeId.Void);
|
_ = comptime passTypeInfo(TypeId.Void);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "type info: extern fns with and without lib names" {
|
||||||
|
const S = struct {
|
||||||
|
extern fn bar1() void;
|
||||||
|
extern "cool" fn bar2() void;
|
||||||
|
};
|
||||||
|
const info = @typeInfo(S);
|
||||||
|
comptime {
|
||||||
|
for (info.Struct.decls) |decl| {
|
||||||
|
if (std.mem.eql(u8, decl.name, "bar1")) {
|
||||||
|
expect(decl.data.Fn.lib_name == null);
|
||||||
|
} else {
|
||||||
|
std.testing.expectEqual(([]const u8)("cool"), decl.data.Fn.lib_name.?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user