mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
Merge remote-tracking branch 'origin/master' into llvm13
Conflicts:
lib/libcxx/include/__config
d57c0cc3bf added support for DragonFlyBSD
to libc++ by updating some ifdefs. This needed to be synced with llvm13.
This commit is contained in:
commit
6aeab0f323
599 changed files with 8694 additions and 8075 deletions
|
|
@ -426,7 +426,7 @@ set(ZIG_STAGE2_SOURCES
|
|||
"${CMAKE_SOURCE_DIR}/lib/std/os.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux/errno-generic.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux/errno/generic.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux/netlink.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux/prctl.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux/securebits.zig"
|
||||
|
|
|
|||
68
build.zig
68
build.zig
|
|
@ -17,8 +17,10 @@ pub fn build(b: *Builder) !void {
|
|||
b.setPreferredReleaseMode(.ReleaseFast);
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode") orelse false;
|
||||
|
||||
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
|
||||
docgen_exe.single_threaded = single_threaded;
|
||||
|
||||
const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);
|
||||
const langref_out_path = fs.path.join(
|
||||
|
|
@ -41,6 +43,7 @@ pub fn build(b: *Builder) !void {
|
|||
var test_stage2 = b.addTest("src/test.zig");
|
||||
test_stage2.setBuildMode(mode);
|
||||
test_stage2.addPackagePath("test_cases", "test/cases.zig");
|
||||
test_stage2.single_threaded = single_threaded;
|
||||
|
||||
const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
|
||||
|
||||
|
|
@ -104,10 +107,15 @@ pub fn build(b: *Builder) !void {
|
|||
exe.setTarget(target);
|
||||
toolchain_step.dependOn(&exe.step);
|
||||
b.default_step.dependOn(&exe.step);
|
||||
exe.single_threaded = single_threaded;
|
||||
|
||||
const exe_options = b.addOptions();
|
||||
exe.addOptions("build_options", exe_options);
|
||||
|
||||
exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
|
||||
exe_options.addOption(bool, "skip_non_native", skip_non_native);
|
||||
exe_options.addOption(bool, "have_llvm", enable_llvm);
|
||||
|
||||
exe.addBuildOption(u32, "mem_leak_frames", mem_leak_frames);
|
||||
exe.addBuildOption(bool, "skip_non_native", skip_non_native);
|
||||
exe.addBuildOption(bool, "have_llvm", enable_llvm);
|
||||
if (enable_llvm) {
|
||||
const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
|
||||
|
||||
|
|
@ -131,6 +139,7 @@ pub fn build(b: *Builder) !void {
|
|||
softfloat.addIncludeDir("deps/SoftFloat-3e/source/8086");
|
||||
softfloat.addIncludeDir("deps/SoftFloat-3e/source/include");
|
||||
softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" });
|
||||
softfloat.single_threaded = single_threaded;
|
||||
|
||||
exe.linkLibrary(softfloat);
|
||||
test_stage2.linkLibrary(softfloat);
|
||||
|
|
@ -213,15 +222,15 @@ pub fn build(b: *Builder) !void {
|
|||
},
|
||||
}
|
||||
};
|
||||
exe.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||
exe_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||
|
||||
const semver = try std.SemanticVersion.parse(version);
|
||||
exe.addBuildOption(std.SemanticVersion, "semver", semver);
|
||||
exe_options.addOption(std.SemanticVersion, "semver", semver);
|
||||
|
||||
exe.addBuildOption(bool, "enable_logging", enable_logging);
|
||||
exe.addBuildOption(bool, "enable_tracy", tracy != null);
|
||||
exe.addBuildOption(bool, "is_stage1", is_stage1);
|
||||
exe.addBuildOption(bool, "omit_stage2", omit_stage2);
|
||||
exe_options.addOption(bool, "enable_logging", enable_logging);
|
||||
exe_options.addOption(bool, "enable_tracy", tracy != null);
|
||||
exe_options.addOption(bool, "is_stage1", is_stage1);
|
||||
exe_options.addOption(bool, "omit_stage2", omit_stage2);
|
||||
if (tracy) |tracy_path| {
|
||||
const client_cpp = fs.path.join(
|
||||
b.allocator,
|
||||
|
|
@ -243,20 +252,23 @@ pub fn build(b: *Builder) !void {
|
|||
const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false;
|
||||
const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
|
||||
|
||||
test_stage2.addBuildOption(bool, "enable_logging", enable_logging);
|
||||
test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native);
|
||||
test_stage2.addBuildOption(bool, "skip_compile_errors", skip_compile_errors);
|
||||
test_stage2.addBuildOption(bool, "is_stage1", is_stage1);
|
||||
test_stage2.addBuildOption(bool, "omit_stage2", omit_stage2);
|
||||
test_stage2.addBuildOption(bool, "have_llvm", enable_llvm);
|
||||
test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
|
||||
test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
|
||||
test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
|
||||
test_stage2.addBuildOption(u32, "mem_leak_frames", mem_leak_frames * 2);
|
||||
test_stage2.addBuildOption(bool, "enable_darling", is_darling_enabled);
|
||||
test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
|
||||
test_stage2.addBuildOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||
test_stage2.addBuildOption(std.SemanticVersion, "semver", semver);
|
||||
const test_stage2_options = b.addOptions();
|
||||
test_stage2.addOptions("build_options", test_stage2_options);
|
||||
|
||||
test_stage2_options.addOption(bool, "enable_logging", enable_logging);
|
||||
test_stage2_options.addOption(bool, "skip_non_native", skip_non_native);
|
||||
test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors);
|
||||
test_stage2_options.addOption(bool, "is_stage1", is_stage1);
|
||||
test_stage2_options.addOption(bool, "omit_stage2", omit_stage2);
|
||||
test_stage2_options.addOption(bool, "have_llvm", enable_llvm);
|
||||
test_stage2_options.addOption(bool, "enable_qemu", is_qemu_enabled);
|
||||
test_stage2_options.addOption(bool, "enable_wine", is_wine_enabled);
|
||||
test_stage2_options.addOption(bool, "enable_wasmtime", is_wasmtime_enabled);
|
||||
test_stage2_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2);
|
||||
test_stage2_options.addOption(bool, "enable_darling", is_darling_enabled);
|
||||
test_stage2_options.addOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
|
||||
test_stage2_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||
test_stage2_options.addOption(std.SemanticVersion, "semver", semver);
|
||||
|
||||
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
|
||||
test_stage2_step.dependOn(&test_stage2.step);
|
||||
|
|
@ -296,7 +308,7 @@ pub fn build(b: *Builder) !void {
|
|||
"behavior",
|
||||
"Run the behavior tests",
|
||||
modes,
|
||||
false,
|
||||
false, // skip_single_threaded
|
||||
skip_non_native,
|
||||
skip_libc,
|
||||
is_wine_enabled,
|
||||
|
|
@ -313,9 +325,9 @@ pub fn build(b: *Builder) !void {
|
|||
"compiler-rt",
|
||||
"Run the compiler_rt tests",
|
||||
modes,
|
||||
true,
|
||||
true, // skip_single_threaded
|
||||
skip_non_native,
|
||||
true,
|
||||
true, // skip_libc
|
||||
is_wine_enabled,
|
||||
is_qemu_enabled,
|
||||
is_wasmtime_enabled,
|
||||
|
|
@ -330,9 +342,9 @@ pub fn build(b: *Builder) !void {
|
|||
"minilibc",
|
||||
"Run the mini libc tests",
|
||||
modes,
|
||||
true,
|
||||
true, // skip_single_threaded
|
||||
skip_non_native,
|
||||
true,
|
||||
true, // skip_libc
|
||||
is_wine_enabled,
|
||||
is_qemu_enabled,
|
||||
is_wasmtime_enabled,
|
||||
|
|
|
|||
|
|
@ -887,16 +887,6 @@ fn tokenizeAndPrintRaw(
|
|||
next_tok_is_fn = true;
|
||||
},
|
||||
|
||||
.keyword_undefined,
|
||||
.keyword_null,
|
||||
.keyword_true,
|
||||
.keyword_false,
|
||||
=> {
|
||||
try out.writeAll("<span class=\"tok-null\">");
|
||||
try writeEscaped(out, src[token.loc.start..token.loc.end]);
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.string_literal,
|
||||
.multiline_string_literal_line,
|
||||
.char_literal,
|
||||
|
|
@ -921,9 +911,18 @@ fn tokenizeAndPrintRaw(
|
|||
},
|
||||
|
||||
.identifier => {
|
||||
if (prev_tok_was_fn) {
|
||||
const tok_bytes = src[token.loc.start..token.loc.end];
|
||||
if (mem.eql(u8, tok_bytes, "undefined") or
|
||||
mem.eql(u8, tok_bytes, "null") or
|
||||
mem.eql(u8, tok_bytes, "true") or
|
||||
mem.eql(u8, tok_bytes, "false"))
|
||||
{
|
||||
try out.writeAll("<span class=\"tok-null\">");
|
||||
try writeEscaped(out, tok_bytes);
|
||||
try out.writeAll("</span>");
|
||||
} else if (prev_tok_was_fn) {
|
||||
try out.writeAll("<span class=\"tok-fn\">");
|
||||
try writeEscaped(out, src[token.loc.start..token.loc.end]);
|
||||
try writeEscaped(out, tok_bytes);
|
||||
try out.writeAll("</span>");
|
||||
} else {
|
||||
const is_int = blk: {
|
||||
|
|
@ -938,12 +937,12 @@ fn tokenizeAndPrintRaw(
|
|||
}
|
||||
break :blk true;
|
||||
};
|
||||
if (is_int or isType(src[token.loc.start..token.loc.end])) {
|
||||
if (is_int or isType(tok_bytes)) {
|
||||
try out.writeAll("<span class=\"tok-type\">");
|
||||
try writeEscaped(out, src[token.loc.start..token.loc.end]);
|
||||
try writeEscaped(out, tok_bytes);
|
||||
try out.writeAll("</span>");
|
||||
} else {
|
||||
try writeEscaped(out, src[token.loc.start..token.loc.end]);
|
||||
try writeEscaped(out, tok_bytes);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -38,15 +38,20 @@
|
|||
.file {
|
||||
text-decoration: underline;
|
||||
}
|
||||
pre,code {
|
||||
font-size: 12pt;
|
||||
}
|
||||
pre > code {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
border: 1px dotted silver;
|
||||
line-height: normal;
|
||||
}
|
||||
code {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px dotted silver;
|
||||
padding-left: 0.3em;
|
||||
padding-right: 0.3em;
|
||||
}
|
||||
.table-wrapper {
|
||||
width: 100%;
|
||||
|
|
@ -95,6 +100,7 @@
|
|||
#contents {
|
||||
max-width: 60em;
|
||||
margin: auto;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#toc {
|
||||
|
|
@ -153,6 +159,11 @@
|
|||
pre > code {
|
||||
color: #ccc;
|
||||
background: #222;
|
||||
border-color: #444;
|
||||
}
|
||||
code {
|
||||
background-color: #222;
|
||||
border-color: #444;
|
||||
}
|
||||
.tok-kw {
|
||||
color: #eee;
|
||||
|
|
@ -3152,7 +3163,9 @@ test "switch using enum literals" {
|
|||
It must specify a tag type and cannot consume every enumeration value.
|
||||
</p>
|
||||
<p>
|
||||
{#link|@intToEnum#} on a non-exhaustive enum cannot fail.
|
||||
{#link|@intToEnum#} on a non-exhaustive enum involves the safety semantics
|
||||
of {#link|@intCast#} to the integer tag type, but beyond that always results in
|
||||
a well-defined enum value.
|
||||
</p>
|
||||
<p>
|
||||
A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong
|
||||
|
|
@ -6634,14 +6647,21 @@ test "global assembly" {
|
|||
<p>
|
||||
When a function is called, a frame is pushed to the stack,
|
||||
the function runs until it reaches a return statement, and then the frame is popped from the stack.
|
||||
At the callsite, the following code does not run until the function returns.
|
||||
The code following the callsite does not run until the function returns.
|
||||
</p>
|
||||
<p>
|
||||
An async function is a function whose callsite is split into an {#syntax#}async{#endsyntax#} initiation,
|
||||
An async function is a function whose execution is split into an {#syntax#}async{#endsyntax#} initiation,
|
||||
followed by an {#syntax#}await{#endsyntax#} completion. Its frame is
|
||||
provided explicitly by the caller, and it can be suspended and resumed any number of times.
|
||||
</p>
|
||||
<p>
|
||||
The code following the {#syntax#}async{#endsyntax#} callsite runs immediately after the async
|
||||
function first suspends. When the return value of the async function is needed,
|
||||
the calling code can {#syntax#}await{#endsyntax#} on the async function frame.
|
||||
This will suspend the calling code until the async function completes, at which point
|
||||
execution resumes just after the {#syntax#}await{#endsyntax#} callsite.
|
||||
</p>
|
||||
<p>
|
||||
Zig infers that a function is {#syntax#}async{#endsyntax#} when it observes that the function contains
|
||||
a <strong>suspension point</strong>. Async functions can be called the same as normal functions. A
|
||||
function call of an async function is a suspend point.
|
||||
|
|
@ -6744,7 +6764,14 @@ fn testResumeFromSuspend(my_result: *i32) void {
|
|||
{#header_open|Async and Await#}
|
||||
<p>
|
||||
In the same way that every {#syntax#}suspend{#endsyntax#} has a matching
|
||||
{#syntax#}resume{#endsyntax#}, every {#syntax#}async{#endsyntax#} has a matching {#syntax#}await{#endsyntax#}.
|
||||
{#syntax#}resume{#endsyntax#}, every {#syntax#}async{#endsyntax#} has a matching {#syntax#}await{#endsyntax#}
|
||||
in standard code.
|
||||
</p>
|
||||
<p>
|
||||
However, it is possible to have an {#syntax#}async{#endsyntax#} call
|
||||
without a matching {#syntax#}await{#endsyntax#}. Upon completion of the async function,
|
||||
execution would continue at the most recent {#syntax#}async{#endsyntax#} callsite or {#syntax#}resume{#endsyntax#} callsite,
|
||||
and the return value of the async function would be lost.
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
|
|
@ -6779,7 +6806,9 @@ fn func() void {
|
|||
</p>
|
||||
<p>
|
||||
{#syntax#}await{#endsyntax#} is a suspend point, and takes as an operand anything that
|
||||
coerces to {#syntax#}anyframe->T{#endsyntax#}.
|
||||
coerces to {#syntax#}anyframe->T{#endsyntax#}. Calling {#syntax#}await{#endsyntax#} on
|
||||
the frame of an async function will cause execution to continue at the
|
||||
{#syntax#}await{#endsyntax#} callsite once the target function completes.
|
||||
</p>
|
||||
<p>
|
||||
There is a common misconception that {#syntax#}await{#endsyntax#} resumes the target function.
|
||||
|
|
@ -7945,7 +7974,7 @@ test "@hasDecl" {
|
|||
{#header_close#}
|
||||
|
||||
{#header_open|@intToEnum#}
|
||||
<pre>{#syntax#}@intToEnum(comptime DestType: type, int_value: std.meta.Tag(DestType)) DestType{#endsyntax#}</pre>
|
||||
<pre>{#syntax#}@intToEnum(comptime DestType: type, integer: anytype) DestType{#endsyntax#}</pre>
|
||||
<p>
|
||||
Converts an integer into an {#link|enum#} value.
|
||||
</p>
|
||||
|
|
@ -11535,11 +11564,7 @@ PrimaryTypeExpr
|
|||
/ INTEGER
|
||||
/ KEYWORD_comptime TypeExpr
|
||||
/ KEYWORD_error DOT IDENTIFIER
|
||||
/ KEYWORD_false
|
||||
/ KEYWORD_null
|
||||
/ KEYWORD_anyframe
|
||||
/ KEYWORD_true
|
||||
/ KEYWORD_undefined
|
||||
/ KEYWORD_unreachable
|
||||
/ STRINGLITERAL
|
||||
/ SwitchExpr
|
||||
|
|
@ -11908,7 +11933,6 @@ KEYWORD_errdefer <- 'errdefer' end_of_word
|
|||
KEYWORD_error <- 'error' end_of_word
|
||||
KEYWORD_export <- 'export' end_of_word
|
||||
KEYWORD_extern <- 'extern' end_of_word
|
||||
KEYWORD_false <- 'false' end_of_word
|
||||
KEYWORD_fn <- 'fn' end_of_word
|
||||
KEYWORD_for <- 'for' end_of_word
|
||||
KEYWORD_if <- 'if' end_of_word
|
||||
|
|
@ -11916,7 +11940,6 @@ KEYWORD_inline <- 'inline' end_of_word
|
|||
KEYWORD_noalias <- 'noalias' end_of_word
|
||||
KEYWORD_nosuspend <- 'nosuspend' end_of_word
|
||||
KEYWORD_noinline <- 'noinline' end_of_word
|
||||
KEYWORD_null <- 'null' end_of_word
|
||||
KEYWORD_opaque <- 'opaque' end_of_word
|
||||
KEYWORD_or <- 'or' end_of_word
|
||||
KEYWORD_orelse <- 'orelse' end_of_word
|
||||
|
|
@ -11930,9 +11953,7 @@ KEYWORD_suspend <- 'suspend' end_of_word
|
|||
KEYWORD_switch <- 'switch' end_of_word
|
||||
KEYWORD_test <- 'test' end_of_word
|
||||
KEYWORD_threadlocal <- 'threadlocal' end_of_word
|
||||
KEYWORD_true <- 'true' end_of_word
|
||||
KEYWORD_try <- 'try' end_of_word
|
||||
KEYWORD_undefined <- 'undefined' end_of_word
|
||||
KEYWORD_union <- 'union' end_of_word
|
||||
KEYWORD_unreachable <- 'unreachable' end_of_word
|
||||
KEYWORD_usingnamespace <- 'usingnamespace' end_of_word
|
||||
|
|
@ -11945,13 +11966,13 @@ keyword <- KEYWORD_align / KEYWORD_allowzero / KEYWORD_and / KEYWORD_anyframe
|
|||
/ KEYWORD_break / KEYWORD_callconv / KEYWORD_catch / KEYWORD_comptime
|
||||
/ KEYWORD_const / KEYWORD_continue / KEYWORD_defer / KEYWORD_else
|
||||
/ KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
|
||||
/ KEYWORD_extern / KEYWORD_false / KEYWORD_fn / KEYWORD_for / KEYWORD_if
|
||||
/ KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if
|
||||
/ KEYWORD_inline / KEYWORD_noalias / KEYWORD_nosuspend / KEYWORD_noinline
|
||||
/ KEYWORD_null / KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed
|
||||
/ KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed
|
||||
/ KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
|
||||
/ KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch
|
||||
/ KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try
|
||||
/ KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable
|
||||
/ KEYWORD_test / KEYWORD_threadlocal / KEYWORD_try
|
||||
/ KEYWORD_union / KEYWORD_unreachable
|
||||
/ KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
|
||||
</code></pre>
|
||||
{#header_close#}
|
||||
|
|
|
|||
12
lib/libc/mingw/lib-common/compstui.def
vendored
Normal file
12
lib/libc/mingw/lib-common/compstui.def
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
;
|
||||
; Exports of file COMPSTUI.dll
|
||||
;
|
||||
; Autogenerated by gen_exportdef
|
||||
; Written by Kai Tietz, 2007
|
||||
;
|
||||
LIBRARY COMPSTUI.dll
|
||||
EXPORTS
|
||||
CommonPropertySheetUIA
|
||||
CommonPropertySheetUIW
|
||||
GetCPSUIUserData
|
||||
SetCPSUIUserData
|
||||
13
lib/libcxx/include/__config
vendored
13
lib/libcxx/include/__config
vendored
|
|
@ -125,7 +125,7 @@
|
|||
# endif
|
||||
// Feature macros for disabling pre ABI v1 features. All of these options
|
||||
// are deprecated.
|
||||
# if defined(__FreeBSD__)
|
||||
# if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
# define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
|
||||
# endif
|
||||
#endif
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
# if __ANDROID_API__ >= 29
|
||||
# define _LIBCPP_HAS_TIMESPEC_GET
|
||||
# endif
|
||||
# elif defined(__Fuchsia__) || defined(__wasi__) || defined(__NetBSD__)
|
||||
# elif defined(__Fuchsia__) || defined(__wasi__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||
# define _LIBCPP_HAS_ALIGNED_ALLOC
|
||||
# define _LIBCPP_HAS_QUICK_EXIT
|
||||
# define _LIBCPP_HAS_TIMESPEC_GET
|
||||
|
|
@ -938,11 +938,11 @@ typedef unsigned int char32_t;
|
|||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_LIBCPP_MSVCRT_LIKE) || \
|
||||
defined(__sun__) || defined(__NetBSD__) || defined(__CloudABI__)
|
||||
defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__CloudABI__)
|
||||
#define _LIBCPP_LOCALE__L_EXTENSIONS 1
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#define _DECLARE_C99_LDBL_MATH 1
|
||||
#endif
|
||||
|
||||
|
|
@ -970,11 +970,11 @@ typedef unsigned int char32_t;
|
|||
# define _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#define _LIBCPP_HAS_DEFAULTRUNELOCALE
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun__)
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__sun__)
|
||||
#define _LIBCPP_WCTYPE_IS_MASK
|
||||
#endif
|
||||
|
||||
|
|
@ -1138,6 +1138,7 @@ extern "C" _LIBCPP_FUNC_VIS void __sanitizer_annotate_contiguous_container(
|
|||
defined(__wasi__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__NuttX__) || \
|
||||
defined(__linux__) || \
|
||||
defined(__GNU__) || \
|
||||
|
|
|
|||
6
lib/libcxx/include/__locale
vendored
6
lib/libcxx/include/__locale
vendored
|
|
@ -35,7 +35,7 @@
|
|||
# include <__support/newlib/xlocale.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
# include <__support/openbsd/xlocale.h>
|
||||
#elif (defined(__APPLE__) || defined(__FreeBSD__) \
|
||||
#elif (defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__EMSCRIPTEN__) || defined(__IBMCPP__))
|
||||
# include <xlocale.h>
|
||||
#elif defined(__Fuchsia__)
|
||||
|
|
@ -450,10 +450,10 @@ public:
|
|||
static const mask blank = _BLANK;
|
||||
static const mask __regex_word = 0x80;
|
||||
# define _LIBCPP_CTYPE_MASK_IS_COMPOSITE_PRINT
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||
# ifdef __APPLE__
|
||||
typedef __uint32_t mask;
|
||||
# elif defined(__FreeBSD__)
|
||||
# elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
typedef unsigned long mask;
|
||||
# elif defined(__EMSCRIPTEN__) || defined(__NetBSD__)
|
||||
typedef unsigned short mask;
|
||||
|
|
|
|||
2
lib/libcxx/include/locale
vendored
2
lib/libcxx/include/locale
vendored
|
|
@ -228,7 +228,7 @@ _LIBCPP_PUSH_MACROS
|
|||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
# define _LIBCPP_GET_C_LOCALE 0
|
||||
#elif defined(__CloudABI__) || defined(__NetBSD__)
|
||||
# define _LIBCPP_GET_C_LOCALE LC_C_LOCALE
|
||||
|
|
|
|||
2
lib/libcxx/src/locale.cpp
vendored
2
lib/libcxx/src/locale.cpp
vendored
|
|
@ -1133,7 +1133,7 @@ ctype<char>::classic_table() noexcept
|
|||
const ctype<char>::mask*
|
||||
ctype<char>::classic_table() noexcept
|
||||
{
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
return _DefaultRuneLocale.__runetype;
|
||||
#elif defined(__NetBSD__)
|
||||
return _C_ctype_tab_ + 1;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! This API non-allocating, non-fallible, and thread-safe.
|
||||
//! The tradeoff is that users of this API must provide the storage
|
||||
//! for each `Progress.Node`.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! A software version formatted according to the Semantic Version 2 specification.
|
||||
//!
|
||||
//! See: https://semver.org
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! This struct represents a kernel thread, and acts as a namespace for concurrency
|
||||
//! primitives that operate on kernel threads. For concurrency primitives that support
|
||||
//! both evented I/O and async I/O, see the respective names in the top level std namespace.
|
||||
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const os = std.os;
|
||||
const assert = std.debug.assert;
|
||||
const target = std.Target.current;
|
||||
const target = builtin.target;
|
||||
const Atomic = std.atomic.Atomic;
|
||||
|
||||
pub const AutoResetEvent = @import("Thread/AutoResetEvent.zig");
|
||||
|
|
@ -24,7 +19,8 @@ pub const Condition = @import("Thread/Condition.zig");
|
|||
|
||||
pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint");
|
||||
|
||||
pub const use_pthreads = target.os.tag != .windows and std.Target.current.os.tag != .wasi and std.builtin.link_libc;
|
||||
pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc;
|
||||
const is_gnu = target.abi.isGnu();
|
||||
|
||||
const Thread = @This();
|
||||
const Impl = if (target.os.tag == .windows)
|
||||
|
|
@ -38,7 +34,7 @@ else
|
|||
|
||||
impl: Impl,
|
||||
|
||||
pub const max_name_len = switch (std.Target.current.os.tag) {
|
||||
pub const max_name_len = switch (target.os.tag) {
|
||||
.linux => 15,
|
||||
.windows => 31,
|
||||
.macos, .ios, .watchos, .tvos => 63,
|
||||
|
|
@ -64,20 +60,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
|
|||
break :blk name_buf[0..name.len :0];
|
||||
};
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
switch (target.os.tag) {
|
||||
.linux => if (use_pthreads) {
|
||||
const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr);
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
os.ERANGE => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return,
|
||||
.RANGE => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else if (use_pthreads and self.getHandle() == std.c.pthread_self()) {
|
||||
// TODO: this is dead code. what did the author of this code intend to happen here?
|
||||
const err = try os.prctl(.SET_NAME, .{@ptrToInt(name_with_terminator.ptr)});
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (@intToEnum(os.E, err)) {
|
||||
.SUCCESS => return,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else {
|
||||
var buf: [32]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
|
||||
|
|
@ -87,7 +84,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
|
|||
|
||||
try file.writer().writeAll(name);
|
||||
},
|
||||
.windows => if (std.Target.current.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
.windows => if (target.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
// SetThreadDescription is only available since version 1607, which is 10.0.14393.795
|
||||
// See https://en.wikipedia.org/wiki/Microsoft_Windows_SDK
|
||||
if (!res) {
|
||||
|
|
@ -110,24 +107,25 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
|
|||
if (self.getHandle() != std.c.pthread_self()) return error.Unsupported;
|
||||
|
||||
const err = std.c.pthread_setname_np(name_with_terminator.ptr);
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.netbsd => if (use_pthreads) {
|
||||
const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null);
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
os.EINVAL => unreachable,
|
||||
os.ESRCH => unreachable,
|
||||
os.ENOMEM => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
.NOMEM => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.freebsd, .openbsd => if (use_pthreads) {
|
||||
// Use pthread_set_name_np for FreeBSD because pthread_setname_np is FreeBSD 12.2+ only.
|
||||
// TODO maybe revisit this if depending on FreeBSD 12.2+ is acceptable because pthread_setname_np can return an error.
|
||||
// TODO maybe revisit this if depending on FreeBSD 12.2+ is acceptable because
|
||||
// pthread_setname_np can return an error.
|
||||
|
||||
std.c.pthread_set_name_np(self.getHandle(), name_with_terminator.ptr);
|
||||
},
|
||||
|
|
@ -151,20 +149,20 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
|||
buffer_ptr[max_name_len] = 0;
|
||||
var buffer = std.mem.span(buffer_ptr);
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
.linux => if (use_pthreads and comptime std.Target.current.abi.isGnu()) {
|
||||
switch (target.os.tag) {
|
||||
.linux => if (use_pthreads and is_gnu) {
|
||||
const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
os.ERANGE => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
.RANGE => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else if (use_pthreads and self.getHandle() == std.c.pthread_self()) {
|
||||
const err = try os.prctl(.GET_NAME, .{@ptrToInt(buffer.ptr)});
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (@intToEnum(os.E, err)) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else if (!use_pthreads) {
|
||||
var buf: [32]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
|
||||
|
|
@ -179,7 +177,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
|||
// musl doesn't provide pthread_getname_np and there's no way to retrieve the thread id of an arbitrary thread.
|
||||
return error.Unsupported;
|
||||
},
|
||||
.windows => if (std.Target.current.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
.windows => if (target.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
// GetThreadDescription is only available since version 1607, which is 10.0.14393.795
|
||||
// See https://en.wikipedia.org/wiki/Microsoft_Windows_SDK
|
||||
if (!res) {
|
||||
|
|
@ -198,20 +196,20 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
|||
},
|
||||
.macos, .ios, .watchos, .tvos => if (use_pthreads) {
|
||||
const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
os.ESRCH => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
.SRCH => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.netbsd => if (use_pthreads) {
|
||||
const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
os.EINVAL => unreachable,
|
||||
os.ESRCH => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.freebsd, .openbsd => if (use_pthreads) {
|
||||
// Use pthread_get_name_np for FreeBSD because pthread_getname_np is FreeBSD 12.2+ only.
|
||||
|
|
@ -288,7 +286,7 @@ pub const SpawnError = error{
|
|||
/// The caller must eventually either call `join()` to wait for the thread to finish and free its resources
|
||||
/// or call `detach()` to excuse the caller from calling `join()` and have the thread clean up its resources on completion`.
|
||||
pub fn spawn(config: SpawnConfig, comptime function: anytype, args: anytype) SpawnError!Thread {
|
||||
if (std.builtin.single_threaded) {
|
||||
if (builtin.single_threaded) {
|
||||
@compileError("Cannot spawn thread when building in single-threaded mode");
|
||||
}
|
||||
|
||||
|
|
@ -611,13 +609,13 @@ const PosixThreadImpl = struct {
|
|||
errdefer allocator.destroy(args_ptr);
|
||||
|
||||
var attr: c.pthread_attr_t = undefined;
|
||||
if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
|
||||
defer assert(c.pthread_attr_destroy(&attr) == 0);
|
||||
if (c.pthread_attr_init(&attr) != .SUCCESS) return error.SystemResources;
|
||||
defer assert(c.pthread_attr_destroy(&attr) == .SUCCESS);
|
||||
|
||||
// Use the same set of parameters used by the libc-less impl.
|
||||
const stack_size = std.math.max(config.stack_size, 16 * 1024);
|
||||
assert(c.pthread_attr_setstacksize(&attr, stack_size) == 0);
|
||||
assert(c.pthread_attr_setguardsize(&attr, std.mem.page_size) == 0);
|
||||
assert(c.pthread_attr_setstacksize(&attr, stack_size) == .SUCCESS);
|
||||
assert(c.pthread_attr_setguardsize(&attr, std.mem.page_size) == .SUCCESS);
|
||||
|
||||
var handle: c.pthread_t = undefined;
|
||||
switch (c.pthread_create(
|
||||
|
|
@ -626,10 +624,10 @@ const PosixThreadImpl = struct {
|
|||
Instance.entryFn,
|
||||
if (@sizeOf(Args) > 1) @ptrCast(*c_void, args_ptr) else undefined,
|
||||
)) {
|
||||
0 => return Impl{ .handle = handle },
|
||||
os.EAGAIN => return error.SystemResources,
|
||||
os.EPERM => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
.SUCCESS => return Impl{ .handle = handle },
|
||||
.AGAIN => return error.SystemResources,
|
||||
.PERM => unreachable,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
|
@ -640,19 +638,19 @@ const PosixThreadImpl = struct {
|
|||
|
||||
fn detach(self: Impl) void {
|
||||
switch (c.pthread_detach(self.handle)) {
|
||||
0 => {},
|
||||
os.EINVAL => unreachable, // thread handle is not joinable
|
||||
os.ESRCH => unreachable, // thread handle is invalid
|
||||
.SUCCESS => {},
|
||||
.INVAL => unreachable, // thread handle is not joinable
|
||||
.SRCH => unreachable, // thread handle is invalid
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn join(self: Impl) void {
|
||||
switch (c.pthread_join(self.handle, null)) {
|
||||
0 => {},
|
||||
os.EINVAL => unreachable, // thread handle is not joinable (or another thread is already joining in)
|
||||
os.ESRCH => unreachable, // thread handle is invalid
|
||||
os.EDEADLK => unreachable, // two threads tried to join each other
|
||||
.SUCCESS => {},
|
||||
.INVAL => unreachable, // thread handle is not joinable (or another thread is already joining in)
|
||||
.SRCH => unreachable, // thread handle is invalid
|
||||
.DEADLK => unreachable, // two threads tried to join each other
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -806,8 +804,10 @@ const LinuxThreadImpl = struct {
|
|||
\\ 1:
|
||||
\\ cmp %%sp, 0
|
||||
\\ beq 2f
|
||||
\\ nop
|
||||
\\ restore
|
||||
\\ ba 1f
|
||||
\\ nop
|
||||
\\ 2:
|
||||
\\ mov 73, %%g1
|
||||
\\ mov %[ptr], %%o0
|
||||
|
|
@ -937,13 +937,13 @@ const LinuxThreadImpl = struct {
|
|||
tls_ptr,
|
||||
&instance.thread.child_tid.value,
|
||||
))) {
|
||||
0 => return Impl{ .thread = &instance.thread },
|
||||
os.EAGAIN => return error.ThreadQuotaExceeded,
|
||||
os.EINVAL => unreachable,
|
||||
os.ENOMEM => return error.SystemResources,
|
||||
os.ENOSPC => unreachable,
|
||||
os.EPERM => unreachable,
|
||||
os.EUSERS => unreachable,
|
||||
.SUCCESS => return Impl{ .thread = &instance.thread },
|
||||
.AGAIN => return error.ThreadQuotaExceeded,
|
||||
.INVAL => unreachable,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOSPC => unreachable,
|
||||
.PERM => unreachable,
|
||||
.USERS => unreachable,
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
|
@ -982,9 +982,9 @@ const LinuxThreadImpl = struct {
|
|||
tid,
|
||||
null,
|
||||
))) {
|
||||
0 => continue,
|
||||
os.EINTR => continue,
|
||||
os.EAGAIN => continue,
|
||||
.SUCCESS => continue,
|
||||
.INTR => continue,
|
||||
.AGAIN => continue,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -1011,7 +1011,7 @@ fn testThreadName(thread: *Thread) !void {
|
|||
}
|
||||
|
||||
test "setName, getName" {
|
||||
if (std.builtin.single_threaded) return error.SkipZigTest;
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
const Context = struct {
|
||||
start_wait_event: ResetEvent = undefined,
|
||||
|
|
@ -1029,7 +1029,7 @@ test "setName, getName" {
|
|||
// Wait for the main thread to have set the thread field in the context.
|
||||
ctx.start_wait_event.wait();
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
switch (target.os.tag) {
|
||||
.windows => testThreadName(&ctx.thread) catch |err| switch (err) {
|
||||
error.Unsupported => return error.SkipZigTest,
|
||||
else => return err,
|
||||
|
|
@ -1054,7 +1054,7 @@ test "setName, getName" {
|
|||
context.start_wait_event.set();
|
||||
context.test_done_event.wait();
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
switch (target.os.tag) {
|
||||
.macos, .ios, .watchos, .tvos => {
|
||||
const res = thread.setName("foobar");
|
||||
try std.testing.expectError(error.Unsupported, res);
|
||||
|
|
@ -1063,7 +1063,7 @@ test "setName, getName" {
|
|||
error.Unsupported => return error.SkipZigTest,
|
||||
else => return err,
|
||||
},
|
||||
else => |tag| if (tag == .linux and use_pthreads and comptime std.Target.current.abi.isMusl()) {
|
||||
else => |tag| if (tag == .linux and use_pthreads and comptime target.abi.isMusl()) {
|
||||
try thread.setName("foobar");
|
||||
|
||||
var name_buffer: [max_name_len:0]u8 = undefined;
|
||||
|
|
@ -1096,7 +1096,7 @@ fn testIncrementNotify(value: *usize, event: *ResetEvent) void {
|
|||
}
|
||||
|
||||
test "Thread.join" {
|
||||
if (std.builtin.single_threaded) return error.SkipZigTest;
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var value: usize = 0;
|
||||
var event: ResetEvent = undefined;
|
||||
|
|
@ -1110,7 +1110,7 @@ test "Thread.join" {
|
|||
}
|
||||
|
||||
test "Thread.detach" {
|
||||
if (std.builtin.single_threaded) return error.SkipZigTest;
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var value: usize = 0;
|
||||
var event: ResetEvent = undefined;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! Similar to `StaticResetEvent` but on `set()` it also (atomically) does `reset()`.
|
||||
//! Unlike StaticResetEvent, `wait()` can only be called by one thread (MPSC-like).
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! A condition provides a way for a kernel thread to block until it is signaled
|
||||
//! to wake up. Spurious wakeups are possible.
|
||||
//! This API supports static initialization and does not require deinitialization.
|
||||
|
|
@ -81,17 +75,17 @@ pub const PthreadCondition = struct {
|
|||
|
||||
pub fn wait(cond: *PthreadCondition, mutex: *Mutex) void {
|
||||
const rc = std.c.pthread_cond_wait(&cond.cond, &mutex.impl.pthread_mutex);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn signal(cond: *PthreadCondition) void {
|
||||
const rc = std.c.pthread_cond_signal(&cond.cond);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn broadcast(cond: *PthreadCondition) void {
|
||||
const rc = std.c.pthread_cond_broadcast(&cond.cond);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -115,9 +109,9 @@ pub const AtomicCondition = struct {
|
|||
0,
|
||||
null,
|
||||
))) {
|
||||
0 => {},
|
||||
std.os.EINTR => {},
|
||||
std.os.EAGAIN => {},
|
||||
.SUCCESS => {},
|
||||
.INTR => {},
|
||||
.AGAIN => {},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
|
|
@ -136,8 +130,8 @@ pub const AtomicCondition = struct {
|
|||
linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
|
||||
1,
|
||||
))) {
|
||||
0 => {},
|
||||
std.os.EFAULT => {},
|
||||
.SUCCESS => {},
|
||||
.FAULT => {},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints.
|
||||
//! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value.
|
||||
//! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`.
|
||||
|
|
@ -152,12 +146,12 @@ const LinuxFutex = struct {
|
|||
@bitCast(i32, expect),
|
||||
ts_ptr,
|
||||
))) {
|
||||
0 => {}, // notified by `wake()`
|
||||
std.os.EINTR => {}, // spurious wakeup
|
||||
std.os.EAGAIN => {}, // ptr.* != expect
|
||||
std.os.ETIMEDOUT => return error.TimedOut,
|
||||
std.os.EINVAL => {}, // possibly timeout overflow
|
||||
std.os.EFAULT => unreachable,
|
||||
.SUCCESS => {}, // notified by `wake()`
|
||||
.INTR => {}, // spurious wakeup
|
||||
.AGAIN => {}, // ptr.* != expect
|
||||
.TIMEDOUT => return error.TimedOut,
|
||||
.INVAL => {}, // possibly timeout overflow
|
||||
.FAULT => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -168,9 +162,9 @@ const LinuxFutex = struct {
|
|||
linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
|
||||
std.math.cast(i32, num_waiters) catch std.math.maxInt(i32),
|
||||
))) {
|
||||
0 => {}, // successful wake up
|
||||
std.os.EINVAL => {}, // invalid futex_wait() on ptr done elsewhere
|
||||
std.os.EFAULT => {}, // pointer became invalid while doing the wake
|
||||
.SUCCESS => {}, // successful wake up
|
||||
.INVAL => {}, // invalid futex_wait() on ptr done elsewhere
|
||||
.FAULT => {}, // pointer became invalid while doing the wake
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -215,13 +209,13 @@ const DarwinFutex = struct {
|
|||
};
|
||||
|
||||
if (status >= 0) return;
|
||||
switch (-status) {
|
||||
darwin.EINTR => {},
|
||||
switch (@intToEnum(std.os.E, -status)) {
|
||||
.INTR => {},
|
||||
// Address of the futex is paged out. This is unlikely, but possible in theory, and
|
||||
// pthread/libdispatch on darwin bother to handle it. In this case we'll return
|
||||
// without waiting, but the caller should retry anyway.
|
||||
darwin.EFAULT => {},
|
||||
darwin.ETIMEDOUT => if (!timeout_overflowed) return error.TimedOut,
|
||||
.FAULT => {},
|
||||
.TIMEDOUT => if (!timeout_overflowed) return error.TimedOut,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -237,11 +231,11 @@ const DarwinFutex = struct {
|
|||
const status = darwin.__ulock_wake(flags, addr, 0);
|
||||
|
||||
if (status >= 0) return;
|
||||
switch (-status) {
|
||||
darwin.EINTR => continue, // spurious wake()
|
||||
darwin.EFAULT => continue, // address of the lock was paged out
|
||||
darwin.ENOENT => return, // nothing was woken up
|
||||
darwin.EALREADY => unreachable, // only for ULF_WAKE_THREAD
|
||||
switch (@intToEnum(std.os.E, -status)) {
|
||||
.INTR => continue, // spurious wake()
|
||||
.FAULT => continue, // address of the lock was paged out
|
||||
.NOENT => return, // nothing was woken up
|
||||
.ALREADY => unreachable, // only for ULF_WAKE_THREAD
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -255,8 +249,8 @@ const PosixFutex = struct {
|
|||
var waiter: List.Node = undefined;
|
||||
|
||||
{
|
||||
assert(std.c.pthread_mutex_lock(&bucket.mutex) == 0);
|
||||
defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == 0);
|
||||
assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
|
||||
defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
|
||||
|
||||
if (ptr.load(.SeqCst) != expect) {
|
||||
return;
|
||||
|
|
@ -272,8 +266,8 @@ const PosixFutex = struct {
|
|||
waiter.data.wait(null) catch unreachable;
|
||||
};
|
||||
|
||||
assert(std.c.pthread_mutex_lock(&bucket.mutex) == 0);
|
||||
defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == 0);
|
||||
assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
|
||||
defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
|
||||
|
||||
if (waiter.data.address == address) {
|
||||
timed_out = true;
|
||||
|
|
@ -297,8 +291,8 @@ const PosixFutex = struct {
|
|||
waiter.data.notify();
|
||||
};
|
||||
|
||||
assert(std.c.pthread_mutex_lock(&bucket.mutex) == 0);
|
||||
defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == 0);
|
||||
assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
|
||||
defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
|
||||
|
||||
var waiters = bucket.list.first;
|
||||
while (waiters) |waiter| {
|
||||
|
|
@ -340,16 +334,13 @@ const PosixFutex = struct {
|
|||
};
|
||||
|
||||
fn deinit(self: *Self) void {
|
||||
const rc = std.c.pthread_cond_destroy(&self.cond);
|
||||
assert(rc == 0 or rc == std.os.EINVAL);
|
||||
|
||||
const rm = std.c.pthread_mutex_destroy(&self.mutex);
|
||||
assert(rm == 0 or rm == std.os.EINVAL);
|
||||
_ = std.c.pthread_cond_destroy(&self.cond);
|
||||
_ = std.c.pthread_mutex_destroy(&self.mutex);
|
||||
}
|
||||
|
||||
fn wait(self: *Self, timeout: ?u64) error{TimedOut}!void {
|
||||
assert(std.c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(std.c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
|
||||
defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
|
||||
|
||||
switch (self.state) {
|
||||
.empty => self.state = .waiting,
|
||||
|
|
@ -378,28 +369,31 @@ const PosixFutex = struct {
|
|||
}
|
||||
|
||||
const ts_ref = ts_ptr orelse {
|
||||
assert(std.c.pthread_cond_wait(&self.cond, &self.mutex) == 0);
|
||||
assert(std.c.pthread_cond_wait(&self.cond, &self.mutex) == .SUCCESS);
|
||||
continue;
|
||||
};
|
||||
|
||||
const rc = std.c.pthread_cond_timedwait(&self.cond, &self.mutex, ts_ref);
|
||||
assert(rc == 0 or rc == std.os.ETIMEDOUT);
|
||||
if (rc == std.os.ETIMEDOUT) {
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.TIMEDOUT => {
|
||||
self.state = .empty;
|
||||
return error.TimedOut;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(self: *Self) void {
|
||||
assert(std.c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(std.c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
|
||||
defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
|
||||
|
||||
switch (self.state) {
|
||||
.empty => self.state = .notified,
|
||||
.waiting => {
|
||||
self.state = .notified;
|
||||
assert(std.c.pthread_cond_signal(&self.cond) == 0);
|
||||
assert(std.c.pthread_cond_signal(&self.cond) == .SUCCESS);
|
||||
},
|
||||
.notified => unreachable,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! Lock may be held only once. If the same thread tries to acquire
|
||||
//! the same mutex twice, it deadlocks. This type supports static
|
||||
//! initialization and is at most `@sizeOf(usize)` in size. When an
|
||||
|
|
@ -143,9 +137,9 @@ pub const AtomicMutex = struct {
|
|||
@enumToInt(new_state),
|
||||
null,
|
||||
))) {
|
||||
0 => {},
|
||||
std.os.EINTR => {},
|
||||
std.os.EAGAIN => {},
|
||||
.SUCCESS => {},
|
||||
.INTR => {},
|
||||
.AGAIN => {},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
|
|
@ -164,8 +158,8 @@ pub const AtomicMutex = struct {
|
|||
linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
|
||||
1,
|
||||
))) {
|
||||
0 => {},
|
||||
std.os.EFAULT => {},
|
||||
.SUCCESS => {},
|
||||
.FAULT => unreachable, // invalid pointer passed to futex_wake
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
|
|
@ -182,10 +176,10 @@ pub const PthreadMutex = struct {
|
|||
|
||||
pub fn release(held: Held) void {
|
||||
switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) {
|
||||
0 => return,
|
||||
std.c.EINVAL => unreachable,
|
||||
std.c.EAGAIN => unreachable,
|
||||
std.c.EPERM => unreachable,
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable,
|
||||
.AGAIN => unreachable,
|
||||
.PERM => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +189,7 @@ pub const PthreadMutex = struct {
|
|||
/// the mutex is unavailable. Otherwise returns Held. Call
|
||||
/// release on Held.
|
||||
pub fn tryAcquire(m: *PthreadMutex) ?Held {
|
||||
if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == 0) {
|
||||
if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS) {
|
||||
return Held{ .mutex = m };
|
||||
} else {
|
||||
return null;
|
||||
|
|
@ -206,12 +200,12 @@ pub const PthreadMutex = struct {
|
|||
/// held by the calling thread.
|
||||
pub fn acquire(m: *PthreadMutex) Held {
|
||||
switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {
|
||||
0 => return Held{ .mutex = m },
|
||||
std.c.EINVAL => unreachable,
|
||||
std.c.EBUSY => unreachable,
|
||||
std.c.EAGAIN => unreachable,
|
||||
std.c.EDEADLK => unreachable,
|
||||
std.c.EPERM => unreachable,
|
||||
.SUCCESS => return Held{ .mutex = m },
|
||||
.INVAL => unreachable,
|
||||
.BUSY => unreachable,
|
||||
.AGAIN => unreachable,
|
||||
.DEADLK => unreachable,
|
||||
.PERM => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! A thread-safe resource which supports blocking until signaled.
|
||||
//! This API is for kernel threads, not evented I/O.
|
||||
//! This API requires being initialized at runtime, and initialization
|
||||
|
|
@ -130,7 +124,7 @@ pub const PosixEvent = struct {
|
|||
|
||||
pub fn init(ev: *PosixEvent) !void {
|
||||
switch (c.getErrno(c.sem_init(&ev.sem, 0, 0))) {
|
||||
0 => return,
|
||||
.SUCCESS => return,
|
||||
else => return error.SystemResources,
|
||||
}
|
||||
}
|
||||
|
|
@ -147,9 +141,9 @@ pub const PosixEvent = struct {
|
|||
pub fn wait(ev: *PosixEvent) void {
|
||||
while (true) {
|
||||
switch (c.getErrno(c.sem_wait(&ev.sem))) {
|
||||
0 => return,
|
||||
c.EINTR => continue,
|
||||
c.EINVAL => unreachable,
|
||||
.SUCCESS => return,
|
||||
.INTR => continue,
|
||||
.INVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -165,10 +159,10 @@ pub const PosixEvent = struct {
|
|||
ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
|
||||
while (true) {
|
||||
switch (c.getErrno(c.sem_timedwait(&ev.sem, &ts))) {
|
||||
0 => return .event_set,
|
||||
c.EINTR => continue,
|
||||
c.EINVAL => unreachable,
|
||||
c.ETIMEDOUT => return .timed_out,
|
||||
.SUCCESS => return .event_set,
|
||||
.INTR => continue,
|
||||
.INVAL => unreachable,
|
||||
.TIMEDOUT => return .timed_out,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -177,10 +171,10 @@ pub const PosixEvent = struct {
|
|||
pub fn reset(ev: *PosixEvent) void {
|
||||
while (true) {
|
||||
switch (c.getErrno(c.sem_trywait(&ev.sem))) {
|
||||
0 => continue, // Need to make it go to zero.
|
||||
c.EINTR => continue,
|
||||
c.EINVAL => unreachable,
|
||||
c.EAGAIN => return, // The semaphore currently has the value zero.
|
||||
.SUCCESS => continue, // Need to make it go to zero.
|
||||
.INTR => continue,
|
||||
.INVAL => unreachable,
|
||||
.AGAIN => return, // The semaphore currently has the value zero.
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! A lock that supports one writer or many readers.
|
||||
//! This API is for kernel threads, not evented I/O.
|
||||
//! This API requires being initialized at runtime, and initialization
|
||||
|
|
@ -13,7 +7,7 @@ impl: Impl,
|
|||
|
||||
const RwLock = @This();
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const Mutex = std.Thread.Mutex;
|
||||
const Semaphore = std.Semaphore;
|
||||
|
|
@ -165,43 +159,41 @@ pub const PthreadRwLock = struct {
|
|||
}
|
||||
|
||||
pub fn deinit(rwl: *PthreadRwLock) void {
|
||||
const safe_rc = switch (std.builtin.os.tag) {
|
||||
.dragonfly, .netbsd => std.os.EAGAIN,
|
||||
else => 0,
|
||||
const safe_rc: std.os.E = switch (builtin.os.tag) {
|
||||
.dragonfly, .netbsd => .AGAIN,
|
||||
else => .SUCCESS,
|
||||
};
|
||||
|
||||
const rc = std.c.pthread_rwlock_destroy(&rwl.rwlock);
|
||||
assert(rc == 0 or rc == safe_rc);
|
||||
|
||||
assert(rc == .SUCCESS or rc == safe_rc);
|
||||
rwl.* = undefined;
|
||||
}
|
||||
|
||||
pub fn tryLock(rwl: *PthreadRwLock) bool {
|
||||
return pthread_rwlock_trywrlock(&rwl.rwlock) == 0;
|
||||
return pthread_rwlock_trywrlock(&rwl.rwlock) == .SUCCESS;
|
||||
}
|
||||
|
||||
pub fn lock(rwl: *PthreadRwLock) void {
|
||||
const rc = pthread_rwlock_wrlock(&rwl.rwlock);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn unlock(rwl: *PthreadRwLock) void {
|
||||
const rc = pthread_rwlock_unlock(&rwl.rwlock);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn tryLockShared(rwl: *PthreadRwLock) bool {
|
||||
return pthread_rwlock_tryrdlock(&rwl.rwlock) == 0;
|
||||
return pthread_rwlock_tryrdlock(&rwl.rwlock) == .SUCCESS;
|
||||
}
|
||||
|
||||
pub fn lockShared(rwl: *PthreadRwLock) void {
|
||||
const rc = pthread_rwlock_rdlock(&rwl.rwlock);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn unlockShared(rwl: *PthreadRwLock) void {
|
||||
const rc = pthread_rwlock_unlock(&rwl.rwlock);
|
||||
assert(rc == 0);
|
||||
assert(rc == .SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! A semaphore is an unsigned integer that blocks the kernel thread if
|
||||
//! the number would become negative.
|
||||
//! This API supports static initialization and does not require deinitialization.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! A thread-safe resource which supports blocking until signaled.
|
||||
//! This API is for kernel threads, not evented I/O.
|
||||
//! This API is statically initializable. It cannot fail to be initialized
|
||||
|
|
@ -201,7 +195,7 @@ pub const AtomicEvent = struct {
|
|||
const waiting = std.math.maxInt(i32); // wake_count
|
||||
const ptr = @ptrCast(*const i32, waiters);
|
||||
const rc = linux.futex_wake(ptr, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, waiting);
|
||||
assert(linux.getErrno(rc) == 0);
|
||||
assert(linux.getErrno(rc) == .SUCCESS);
|
||||
}
|
||||
|
||||
fn wait(waiters: *u32, timeout: ?u64) !void {
|
||||
|
|
@ -221,10 +215,10 @@ pub const AtomicEvent = struct {
|
|||
const ptr = @ptrCast(*const i32, waiters);
|
||||
const rc = linux.futex_wait(ptr, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, expected, ts_ptr);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0 => continue,
|
||||
os.ETIMEDOUT => return error.TimedOut,
|
||||
os.EINTR => continue,
|
||||
os.EAGAIN => return,
|
||||
.SUCCESS => continue,
|
||||
.TIMEDOUT => return error.TimedOut,
|
||||
.INTR => continue,
|
||||
.AGAIN => return,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// Does NOT look at the locale the way C89's toupper(3), isspace() et cetera does.
|
||||
// I could have taken only a u7 to make this clear, but it would be slower
|
||||
// It is my opinion that encodings other than UTF-8 should not be supported.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std.zig");
|
||||
const target = std.Target.current;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("../std.zig");
|
||||
|
||||
const testing = std.testing;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const assert = std.debug.assert;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const assert = std.debug.assert;
|
||||
const builtin = std.builtin;
|
||||
const expect = std.testing.expect;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//! This file defines several variants of bit sets. A bit set
|
||||
//! is a densely stored set of integers with a known maximum,
|
||||
//! in which each integer gets a single bit. Bit sets have very
|
||||
|
|
|
|||
315
lib/std/bounded_array.zig
Normal file
315
lib/std/bounded_array.zig
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
const std = @import("std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
|
||||
/// A structure with an array and a length, that can be used as a slice.
|
||||
///
|
||||
/// Useful to pass around small arrays whose exact size is only known at
|
||||
/// runtime, but whose maximum size is known at comptime, without requiring
|
||||
/// an `Allocator`.
|
||||
///
|
||||
/// ```zig
|
||||
/// var actual_size = 32;
|
||||
/// var a = try BoundedArray(u8, 64).init(actual_size);
|
||||
/// var slice = a.slice(); // a slice of the 64-byte array
|
||||
/// var a_clone = a; // creates a copy - the structure doesn't use any internal pointers
|
||||
/// ```
|
||||
pub fn BoundedArray(comptime T: type, comptime capacity: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
buffer: [capacity]T,
|
||||
len: usize = 0,
|
||||
|
||||
/// Set the actual length of the slice.
|
||||
/// Returns error.Overflow if it exceeds the length of the backing array.
|
||||
pub fn init(len: usize) !Self {
|
||||
if (len > capacity) return error.Overflow;
|
||||
return Self{ .buffer = undefined, .len = len };
|
||||
}
|
||||
|
||||
/// View the internal array as a mutable slice whose size was previously set.
|
||||
pub fn slice(self: *Self) []T {
|
||||
return self.buffer[0..self.len];
|
||||
}
|
||||
|
||||
/// View the internal array as a constant slice whose size was previously set.
|
||||
pub fn constSlice(self: Self) []const T {
|
||||
return self.buffer[0..self.len];
|
||||
}
|
||||
|
||||
/// Adjust the slice's length to `len`.
|
||||
/// Does not initialize added items if any.
|
||||
pub fn resize(self: *Self, len: usize) !void {
|
||||
if (len > capacity) return error.Overflow;
|
||||
self.len = len;
|
||||
}
|
||||
|
||||
/// Copy the content of an existing slice.
|
||||
pub fn fromSlice(m: []const T) !Self {
|
||||
var list = try init(m.len);
|
||||
std.mem.copy(T, list.slice(), m);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// Return the element at index `i` of the slice.
|
||||
pub fn get(self: Self, i: usize) T {
|
||||
return self.constSlice()[i];
|
||||
}
|
||||
|
||||
/// Set the value of the element at index `i` of the slice.
|
||||
pub fn set(self: *Self, i: usize, item: T) void {
|
||||
self.slice()[i] = item;
|
||||
}
|
||||
|
||||
/// Return the maximum length of a slice.
|
||||
pub fn capacity(self: Self) usize {
|
||||
return self.buffer.len;
|
||||
}
|
||||
|
||||
/// Check that the slice can hold at least `additional_count` items.
|
||||
pub fn ensureUnusedCapacity(self: Self, additional_count: usize) !void {
|
||||
if (self.len + additional_count > capacity) {
|
||||
return error.Overflow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Increase length by 1, returning a pointer to the new item.
|
||||
pub fn addOne(self: *Self) !*T {
|
||||
try self.ensureUnusedCapacity(1);
|
||||
return self.addOneAssumeCapacity();
|
||||
}
|
||||
|
||||
/// Increase length by 1, returning pointer to the new item.
|
||||
/// Asserts that there is space for the new item.
|
||||
pub fn addOneAssumeCapacity(self: *Self) *T {
|
||||
assert(self.len < capacity);
|
||||
self.len += 1;
|
||||
return &self.slice()[self.len - 1];
|
||||
}
|
||||
|
||||
/// Resize the slice, adding `n` new elements, which have `undefined` values.
|
||||
/// The return value is a slice pointing to the uninitialized elements.
|
||||
pub fn addManyAsArray(self: *Self, comptime n: usize) !*[n]T {
|
||||
const prev_len = self.len;
|
||||
try self.resize(self.len + n);
|
||||
return self.slice()[prev_len..][0..n];
|
||||
}
|
||||
|
||||
/// Remove and return the last element from the slice.
|
||||
/// Asserts the slice has at least one item.
|
||||
pub fn pop(self: *Self) T {
|
||||
const item = self.get(self.len - 1);
|
||||
self.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
/// Remove and return the last element from the slice, or
|
||||
/// return `null` if the slice is empty.
|
||||
pub fn popOrNull(self: *Self) ?T {
|
||||
return if (self.len == 0) null else self.pop();
|
||||
}
|
||||
|
||||
/// Return a slice of only the extra capacity after items.
|
||||
/// This can be useful for writing directly into it.
|
||||
/// Note that such an operation must be followed up with a
|
||||
/// call to `resize()`
|
||||
pub fn unusedCapacitySlice(self: *Self) []T {
|
||||
return self.buffer[self.len..];
|
||||
}
|
||||
|
||||
/// Insert `item` at index `i` by moving `slice[n .. slice.len]` to make room.
|
||||
/// This operation is O(N).
|
||||
pub fn insert(self: *Self, i: usize, item: T) !void {
|
||||
if (i >= self.len) {
|
||||
return error.IndexOutOfBounds;
|
||||
}
|
||||
_ = try self.addOne();
|
||||
var s = self.slice();
|
||||
mem.copyBackwards(T, s[i + 1 .. s.len], s[i .. s.len - 1]);
|
||||
self.buffer[i] = item;
|
||||
}
|
||||
|
||||
/// Insert slice `items` at index `i` by moving `slice[i .. slice.len]` to make room.
|
||||
/// This operation is O(N).
|
||||
pub fn insertSlice(self: *Self, i: usize, items: []const T) !void {
|
||||
try self.ensureUnusedCapacity(items.len);
|
||||
self.len += items.len;
|
||||
mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]);
|
||||
mem.copy(T, self.slice()[i .. i + items.len], items);
|
||||
}
|
||||
|
||||
/// Replace range of elements `slice[start..start+len]` with `new_items`.
|
||||
/// Grows slice if `len < new_items.len`.
|
||||
/// Shrinks slice if `len > new_items.len`.
|
||||
pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: []const T) !void {
|
||||
const after_range = start + len;
|
||||
var range = self.slice()[start..after_range];
|
||||
|
||||
if (range.len == new_items.len) {
|
||||
mem.copy(T, range, new_items);
|
||||
} else if (range.len < new_items.len) {
|
||||
const first = new_items[0..range.len];
|
||||
const rest = new_items[range.len..];
|
||||
mem.copy(T, range, first);
|
||||
try self.insertSlice(after_range, rest);
|
||||
} else {
|
||||
mem.copy(T, range, new_items);
|
||||
const after_subrange = start + new_items.len;
|
||||
for (self.constSlice()[after_range..]) |item, i| {
|
||||
self.slice()[after_subrange..][i] = item;
|
||||
}
|
||||
self.len -= len - new_items.len;
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the slice by 1 element.
|
||||
pub fn append(self: *Self, item: T) !void {
|
||||
const new_item_ptr = try self.addOne();
|
||||
new_item_ptr.* = item;
|
||||
}
|
||||
|
||||
/// Remove the element at index `i`, shift elements after index
|
||||
/// `i` forward, and return the removed element.
|
||||
/// Asserts the slice has at least one item.
|
||||
/// This operation is O(N).
|
||||
pub fn orderedRemove(self: *Self, i: usize) T {
|
||||
const newlen = self.len - 1;
|
||||
if (newlen == i) return self.pop();
|
||||
const old_item = self.get(i);
|
||||
for (self.slice()[i..newlen]) |*b, j| b.* = self.get(i + 1 + j);
|
||||
self.set(newlen, undefined);
|
||||
self.len = newlen;
|
||||
return old_item;
|
||||
}
|
||||
|
||||
/// Remove the element at the specified index and return it.
|
||||
/// The empty slot is filled from the end of the slice.
|
||||
/// This operation is O(1).
|
||||
pub fn swapRemove(self: *Self, i: usize) T {
|
||||
if (self.len - 1 == i) return self.pop();
|
||||
const old_item = self.get(i);
|
||||
self.set(i, self.pop());
|
||||
return old_item;
|
||||
}
|
||||
|
||||
/// Append the slice of items to the slice.
|
||||
pub fn appendSlice(self: *Self, items: []const T) !void {
|
||||
try self.ensureUnusedCapacity(items.len);
|
||||
self.appendSliceAssumeCapacity(items);
|
||||
}
|
||||
|
||||
/// Append the slice of items to the slice, asserting the capacity is already
|
||||
/// enough to store the new items.
|
||||
pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
|
||||
const oldlen = self.len;
|
||||
self.len += items.len;
|
||||
mem.copy(T, self.slice()[oldlen..], items);
|
||||
}
|
||||
|
||||
/// Append a value to the slice `n` times.
|
||||
/// Allocates more memory as necessary.
|
||||
pub fn appendNTimes(self: *Self, value: T, n: usize) !void {
|
||||
const old_len = self.len;
|
||||
try self.resize(old_len + n);
|
||||
mem.set(T, self.slice()[old_len..self.len], value);
|
||||
}
|
||||
|
||||
/// Append a value to the slice `n` times.
|
||||
/// Asserts the capacity is enough.
|
||||
pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
|
||||
const old_len = self.len;
|
||||
self.len += n;
|
||||
assert(self.len <= capacity);
|
||||
mem.set(T, self.slice()[old_len..self.len], value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "BoundedArray" {
|
||||
var a = try BoundedArray(u8, 64).init(32);
|
||||
|
||||
try testing.expectEqual(a.capacity(), 64);
|
||||
try testing.expectEqual(a.slice().len, 32);
|
||||
try testing.expectEqual(a.constSlice().len, 32);
|
||||
|
||||
try a.resize(48);
|
||||
try testing.expectEqual(a.len, 48);
|
||||
|
||||
const x = [_]u8{1} ** 10;
|
||||
a = try BoundedArray(u8, 64).fromSlice(&x);
|
||||
try testing.expectEqualSlices(u8, &x, a.constSlice());
|
||||
|
||||
var a2 = a;
|
||||
try testing.expectEqualSlices(u8, a.constSlice(), a.constSlice());
|
||||
a2.set(0, 0);
|
||||
try testing.expect(a.get(0) != a2.get(0));
|
||||
|
||||
try testing.expectError(error.Overflow, a.resize(100));
|
||||
try testing.expectError(error.Overflow, BoundedArray(u8, x.len - 1).fromSlice(&x));
|
||||
|
||||
try a.resize(0);
|
||||
try a.ensureUnusedCapacity(a.capacity());
|
||||
(try a.addOne()).* = 0;
|
||||
try a.ensureUnusedCapacity(a.capacity() - 1);
|
||||
try testing.expectEqual(a.len, 1);
|
||||
|
||||
const uninitialized = try a.addManyAsArray(4);
|
||||
try testing.expectEqual(uninitialized.len, 4);
|
||||
try testing.expectEqual(a.len, 5);
|
||||
|
||||
try a.append(0xff);
|
||||
try testing.expectEqual(a.len, 6);
|
||||
try testing.expectEqual(a.pop(), 0xff);
|
||||
|
||||
try a.resize(1);
|
||||
try testing.expectEqual(a.popOrNull(), 0);
|
||||
try testing.expectEqual(a.popOrNull(), null);
|
||||
var unused = a.unusedCapacitySlice();
|
||||
mem.set(u8, unused[0..8], 2);
|
||||
unused[8] = 3;
|
||||
unused[9] = 4;
|
||||
try testing.expectEqual(unused.len, a.capacity());
|
||||
try a.resize(10);
|
||||
|
||||
try a.insert(5, 0xaa);
|
||||
try testing.expectEqual(a.len, 11);
|
||||
try testing.expectEqual(a.get(5), 0xaa);
|
||||
try testing.expectEqual(a.get(9), 3);
|
||||
try testing.expectEqual(a.get(10), 4);
|
||||
|
||||
try a.appendSlice(&x);
|
||||
try testing.expectEqual(a.len, 11 + x.len);
|
||||
|
||||
try a.appendNTimes(0xbb, 5);
|
||||
try testing.expectEqual(a.len, 11 + x.len + 5);
|
||||
try testing.expectEqual(a.pop(), 0xbb);
|
||||
|
||||
a.appendNTimesAssumeCapacity(0xcc, 5);
|
||||
try testing.expectEqual(a.len, 11 + x.len + 5 - 1 + 5);
|
||||
try testing.expectEqual(a.pop(), 0xcc);
|
||||
|
||||
try testing.expectEqual(a.len, 29);
|
||||
try a.replaceRange(1, 20, &x);
|
||||
try testing.expectEqual(a.len, 29 + x.len - 20);
|
||||
|
||||
try a.insertSlice(0, &x);
|
||||
try testing.expectEqual(a.len, 29 + x.len - 20 + x.len);
|
||||
|
||||
try a.replaceRange(1, 5, &x);
|
||||
try testing.expectEqual(a.len, 29 + x.len - 20 + x.len + x.len - 5);
|
||||
|
||||
try a.append(10);
|
||||
try testing.expectEqual(a.pop(), 10);
|
||||
|
||||
try a.append(20);
|
||||
const removed = a.orderedRemove(5);
|
||||
try testing.expectEqual(removed, 1);
|
||||
try testing.expectEqual(a.len, 34);
|
||||
|
||||
a.set(0, 0xdd);
|
||||
a.set(a.len - 1, 0xee);
|
||||
const swapped = a.swapRemove(0);
|
||||
try testing.expectEqual(swapped, 0xdd);
|
||||
try testing.expectEqual(a.get(0), 0xee);
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const StringHashMap = std.StringHashMap;
|
||||
const mem = std.mem;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const StringHashMap = std.StringHashMap;
|
||||
const mem = @import("mem.zig");
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
|
|
@ -28,6 +23,7 @@ pub const WriteFileStep = @import("build/WriteFileStep.zig");
|
|||
pub const RunStep = @import("build/RunStep.zig");
|
||||
pub const CheckFileStep = @import("build/CheckFileStep.zig");
|
||||
pub const InstallRawStep = @import("build/InstallRawStep.zig");
|
||||
pub const OptionsStep = @import("build/OptionsStep.zig");
|
||||
|
||||
pub const Builder = struct {
|
||||
install_tls: TopLevelStep,
|
||||
|
|
@ -252,6 +248,10 @@ pub const Builder = struct {
|
|||
return LibExeObjStep.createExecutable(builder, name, root_src);
|
||||
}
|
||||
|
||||
pub fn addOptions(self: *Builder) *OptionsStep {
|
||||
return OptionsStep.create(self);
|
||||
}
|
||||
|
||||
pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
|
||||
return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
|
||||
}
|
||||
|
|
@ -1380,16 +1380,6 @@ pub const FileSource = union(enum) {
|
|||
}
|
||||
};
|
||||
|
||||
const BuildOptionArtifactArg = struct {
|
||||
name: []const u8,
|
||||
artifact: *LibExeObjStep,
|
||||
};
|
||||
|
||||
const BuildOptionFileSourceArg = struct {
|
||||
name: []const u8,
|
||||
source: FileSource,
|
||||
};
|
||||
|
||||
pub const LibExeObjStep = struct {
|
||||
pub const base_id = .lib_exe_obj;
|
||||
|
||||
|
|
@ -1432,15 +1422,13 @@ pub const LibExeObjStep = struct {
|
|||
single_threaded: bool,
|
||||
test_evented_io: bool = false,
|
||||
code_model: builtin.CodeModel = .default,
|
||||
wasi_exec_model: ?builtin.WasiExecModel = null,
|
||||
|
||||
root_src: ?FileSource,
|
||||
out_h_filename: []const u8,
|
||||
out_lib_filename: []const u8,
|
||||
out_pdb_filename: []const u8,
|
||||
packages: ArrayList(Pkg),
|
||||
build_options_contents: std.ArrayList(u8),
|
||||
build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
|
||||
build_options_file_source_args: std.ArrayList(BuildOptionFileSourceArg),
|
||||
|
||||
object_src: []const u8,
|
||||
|
||||
|
|
@ -1607,9 +1595,6 @@ pub const LibExeObjStep = struct {
|
|||
.rpaths = ArrayList([]const u8).init(builder.allocator),
|
||||
.framework_dirs = ArrayList([]const u8).init(builder.allocator),
|
||||
.object_src = undefined,
|
||||
.build_options_contents = std.ArrayList(u8).init(builder.allocator),
|
||||
.build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
|
||||
.build_options_file_source_args = std.ArrayList(BuildOptionFileSourceArg).init(builder.allocator),
|
||||
.c_std = Builder.CStd.C99,
|
||||
.override_lib_dir = null,
|
||||
.main_pkg_path = null,
|
||||
|
|
@ -1735,7 +1720,6 @@ pub const LibExeObjStep = struct {
|
|||
}
|
||||
|
||||
pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
|
||||
assert(self.target.isDarwin());
|
||||
// Note: No need to dupe because frameworks dupes internally.
|
||||
self.frameworks.insert(framework_name) catch unreachable;
|
||||
}
|
||||
|
|
@ -2043,119 +2027,6 @@ pub const LibExeObjStep = struct {
|
|||
self.linkLibraryOrObject(obj);
|
||||
}
|
||||
|
||||
pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
|
||||
const out = self.build_options_contents.writer();
|
||||
switch (T) {
|
||||
[]const []const u8 => {
|
||||
out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
|
||||
for (value) |slice| {
|
||||
out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
return;
|
||||
},
|
||||
[:0]const u8 => {
|
||||
out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
||||
return;
|
||||
},
|
||||
[]const u8 => {
|
||||
out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
||||
return;
|
||||
},
|
||||
?[:0]const u8 => {
|
||||
out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
||||
if (value) |payload| {
|
||||
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
||||
} else {
|
||||
out.writeAll("null;\n") catch unreachable;
|
||||
}
|
||||
return;
|
||||
},
|
||||
?[]const u8 => {
|
||||
out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
||||
if (value) |payload| {
|
||||
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
||||
} else {
|
||||
out.writeAll("null;\n") catch unreachable;
|
||||
}
|
||||
return;
|
||||
},
|
||||
std.builtin.Version => {
|
||||
out.print(
|
||||
\\pub const {}: @import("std").builtin.Version = .{{
|
||||
\\ .major = {d},
|
||||
\\ .minor = {d},
|
||||
\\ .patch = {d},
|
||||
\\}};
|
||||
\\
|
||||
, .{
|
||||
std.zig.fmtId(name),
|
||||
|
||||
value.major,
|
||||
value.minor,
|
||||
value.patch,
|
||||
}) catch unreachable;
|
||||
},
|
||||
std.SemanticVersion => {
|
||||
out.print(
|
||||
\\pub const {}: @import("std").SemanticVersion = .{{
|
||||
\\ .major = {d},
|
||||
\\ .minor = {d},
|
||||
\\ .patch = {d},
|
||||
\\
|
||||
, .{
|
||||
std.zig.fmtId(name),
|
||||
|
||||
value.major,
|
||||
value.minor,
|
||||
value.patch,
|
||||
}) catch unreachable;
|
||||
if (value.pre) |some| {
|
||||
out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
||||
}
|
||||
if (value.build) |some| {
|
||||
out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (@typeInfo(T)) {
|
||||
.Enum => |enum_info| {
|
||||
out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
|
||||
inline for (enum_info.fields) |field| {
|
||||
out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
|
||||
}
|
||||
|
||||
/// The value is the path in the cache dir.
|
||||
/// Adds a dependency automatically.
|
||||
pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void {
|
||||
self.build_options_artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
|
||||
self.step.dependOn(&artifact.step);
|
||||
}
|
||||
|
||||
/// The value is the path in the cache dir.
|
||||
/// Adds a dependency automatically.
|
||||
/// basename refers to the basename of the WriteFileStep
|
||||
pub fn addBuildOptionFileSource(
|
||||
self: *LibExeObjStep,
|
||||
name: []const u8,
|
||||
source: FileSource,
|
||||
) void {
|
||||
self.build_options_file_source_args.append(.{
|
||||
.name = name,
|
||||
.source = source.dupe(self.builder),
|
||||
}) catch unreachable;
|
||||
source.addStepDependencies(&self.step);
|
||||
}
|
||||
|
||||
pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
|
||||
self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
|
||||
}
|
||||
|
|
@ -2181,6 +2052,10 @@ pub const LibExeObjStep = struct {
|
|||
self.addRecursiveBuildDeps(package);
|
||||
}
|
||||
|
||||
pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
|
||||
self.addPackage(options.getPackage(package_name));
|
||||
}
|
||||
|
||||
fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
|
||||
package.path.addStepDependencies(&self.step);
|
||||
if (package.dependencies) |deps| {
|
||||
|
|
@ -2247,28 +2122,6 @@ pub const LibExeObjStep = struct {
|
|||
self.step.dependOn(&other.step);
|
||||
self.link_objects.append(.{ .other_step = other }) catch unreachable;
|
||||
self.include_dirs.append(.{ .other_step = other }) catch unreachable;
|
||||
|
||||
// BUG: The following code introduces a order-of-call dependency:
|
||||
// var lib = addSharedLibrary(...);
|
||||
// var exe = addExecutable(...);
|
||||
// exe.linkLibrary(lib);
|
||||
// lib.linkSystemLibrary("foobar"); // this will be ignored for exe!
|
||||
|
||||
// Inherit dependency on system libraries
|
||||
for (other.link_objects.items) |link_object| {
|
||||
switch (link_object) {
|
||||
.system_lib => |name| self.linkSystemLibrary(name),
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
|
||||
// Inherit dependencies on darwin frameworks
|
||||
if (self.target.isDarwin() and !other.isDynamicLibrary()) {
|
||||
var it = other.frameworks.iterator();
|
||||
while (it.next()) |framework| {
|
||||
self.frameworks.insert(framework.*) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
|
||||
|
|
@ -2322,6 +2175,31 @@ pub const LibExeObjStep = struct {
|
|||
if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
|
||||
|
||||
var prev_has_extra_flags = false;
|
||||
|
||||
// Resolve transitive dependencies
|
||||
for (self.link_objects.items) |link_object| {
|
||||
switch (link_object) {
|
||||
.other_step => |other| {
|
||||
// Inherit dependency on system libraries
|
||||
for (other.link_objects.items) |other_link_object| {
|
||||
switch (other_link_object) {
|
||||
.system_lib => |name| self.linkSystemLibrary(name),
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
|
||||
// Inherit dependencies on darwin frameworks
|
||||
if (!other.isDynamicLibrary()) {
|
||||
var it = other.frameworks.iterator();
|
||||
while (it.next()) |framework| {
|
||||
self.frameworks.insert(framework.*) catch unreachable;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
|
||||
for (self.link_objects.items) |link_object| {
|
||||
switch (link_object) {
|
||||
.static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
|
||||
|
|
@ -2395,41 +2273,6 @@ pub const LibExeObjStep = struct {
|
|||
}
|
||||
}
|
||||
|
||||
if (self.build_options_contents.items.len > 0 or
|
||||
self.build_options_artifact_args.items.len > 0 or
|
||||
self.build_options_file_source_args.items.len > 0)
|
||||
{
|
||||
// Render build artifact and write file options at the last minute, now that the path is known.
|
||||
//
|
||||
// Note that pathFromRoot uses resolve path, so this will have
|
||||
// correct behavior even if getOutputPath is already absolute.
|
||||
for (self.build_options_artifact_args.items) |item| {
|
||||
self.addBuildOption(
|
||||
[]const u8,
|
||||
item.name,
|
||||
self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
|
||||
);
|
||||
}
|
||||
for (self.build_options_file_source_args.items) |item| {
|
||||
self.addBuildOption(
|
||||
[]const u8,
|
||||
item.name,
|
||||
item.source.getPath(self.builder),
|
||||
);
|
||||
}
|
||||
|
||||
const build_options_file = try fs.path.join(
|
||||
builder.allocator,
|
||||
&[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) },
|
||||
);
|
||||
const path_from_root = builder.pathFromRoot(build_options_file);
|
||||
try fs.cwd().writeFile(path_from_root, self.build_options_contents.items);
|
||||
try zig_args.append("--pkg-begin");
|
||||
try zig_args.append("build_options");
|
||||
try zig_args.append(path_from_root);
|
||||
try zig_args.append("--pkg-end");
|
||||
}
|
||||
|
||||
if (self.image_base) |image_base| {
|
||||
try zig_args.append("--image-base");
|
||||
try zig_args.append(builder.fmt("0x{x}", .{image_base}));
|
||||
|
|
@ -2547,6 +2390,9 @@ pub const LibExeObjStep = struct {
|
|||
try zig_args.append("-mcmodel");
|
||||
try zig_args.append(@tagName(self.code_model));
|
||||
}
|
||||
if (self.wasi_exec_model) |model| {
|
||||
try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
|
||||
}
|
||||
|
||||
if (!self.target.isNative()) {
|
||||
try zig_args.append("-target");
|
||||
|
|
@ -2719,6 +2565,14 @@ pub const LibExeObjStep = struct {
|
|||
zig_args.append("-framework") catch unreachable;
|
||||
zig_args.append(framework.*) catch unreachable;
|
||||
}
|
||||
} else {
|
||||
if (self.framework_dirs.items.len > 0) {
|
||||
warn("Framework directories have been added for a non-darwin target, this will have no affect on the build\n", .{});
|
||||
}
|
||||
|
||||
if (self.frameworks.count() > 0) {
|
||||
warn("Frameworks have been added for a non-darwin target, this will have no affect on the build\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.sysroot) |sysroot| {
|
||||
|
|
@ -3026,7 +2880,8 @@ pub const InstallDirStep = struct {
|
|||
const self = @fieldParentPtr(InstallDirStep, "step", step);
|
||||
const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
|
||||
const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);
|
||||
const src_dir = try std.fs.cwd().openDir(full_src_dir, .{ .iterate = true });
|
||||
var src_dir = try std.fs.cwd().openDir(full_src_dir, .{ .iterate = true });
|
||||
defer src_dir.close();
|
||||
var it = try src_dir.walk(self.builder.allocator);
|
||||
next_entry: while (try it.next()) |entry| {
|
||||
for (self.options.exclude_extensions) |ext| {
|
||||
|
|
@ -3131,6 +2986,7 @@ pub const Step = struct {
|
|||
run,
|
||||
check_file,
|
||||
install_raw,
|
||||
options,
|
||||
custom,
|
||||
};
|
||||
|
||||
|
|
@ -3302,43 +3158,6 @@ test "Builder.dupePkg()" {
|
|||
try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr);
|
||||
}
|
||||
|
||||
test "LibExeObjStep.addBuildOption" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
var builder = try Builder.create(
|
||||
&arena.allocator,
|
||||
"test",
|
||||
"test",
|
||||
"test",
|
||||
"test",
|
||||
);
|
||||
defer builder.destroy();
|
||||
|
||||
var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
|
||||
exe.addBuildOption(usize, "option1", 1);
|
||||
exe.addBuildOption(?usize, "option2", null);
|
||||
exe.addBuildOption([]const u8, "string", "zigisthebest");
|
||||
exe.addBuildOption(?[]const u8, "optional_string", null);
|
||||
exe.addBuildOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\pub const option1: usize = 1;
|
||||
\\pub const option2: ?usize = null;
|
||||
\\pub const string: []const u8 = "zigisthebest";
|
||||
\\pub const optional_string: ?[]const u8 = null;
|
||||
\\pub const semantic_version: @import("std").SemanticVersion = .{
|
||||
\\ .major = 0,
|
||||
\\ .minor = 1,
|
||||
\\ .patch = 2,
|
||||
\\ .pre = "foo",
|
||||
\\ .build = "bar",
|
||||
\\};
|
||||
\\
|
||||
, exe.build_options_contents.items);
|
||||
}
|
||||
|
||||
test "LibExeObjStep.addPackage" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const build = std.build;
|
||||
const Step = build.Step;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const build = @import("../build.zig");
|
||||
const Step = build.Step;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
|
|
|||
257
lib/std/build/OptionsStep.zig
Normal file
257
lib/std/build/OptionsStep.zig
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
const std = @import("../std.zig");
|
||||
const build = std.build;
|
||||
const fs = std.fs;
|
||||
const Step = build.Step;
|
||||
const Builder = build.Builder;
|
||||
const GeneratedFile = build.GeneratedFile;
|
||||
const LibExeObjStep = build.LibExeObjStep;
|
||||
const FileSource = build.FileSource;
|
||||
|
||||
const OptionsStep = @This();
|
||||
|
||||
step: Step,
|
||||
generated_file: GeneratedFile,
|
||||
builder: *Builder,
|
||||
|
||||
contents: std.ArrayList(u8),
|
||||
artifact_args: std.ArrayList(OptionArtifactArg),
|
||||
file_source_args: std.ArrayList(OptionFileSourceArg),
|
||||
|
||||
pub fn create(builder: *Builder) *OptionsStep {
|
||||
const self = builder.allocator.create(OptionsStep) catch unreachable;
|
||||
self.* = .{
|
||||
.builder = builder,
|
||||
.step = Step.init(.options, "options", builder.allocator, make),
|
||||
.generated_file = undefined,
|
||||
.contents = std.ArrayList(u8).init(builder.allocator),
|
||||
.artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
|
||||
.file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
|
||||
};
|
||||
self.generated_file = .{ .step = &self.step };
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
|
||||
const out = self.contents.writer();
|
||||
switch (T) {
|
||||
[]const []const u8 => {
|
||||
out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
|
||||
for (value) |slice| {
|
||||
out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
return;
|
||||
},
|
||||
[:0]const u8 => {
|
||||
out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
||||
return;
|
||||
},
|
||||
[]const u8 => {
|
||||
out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
|
||||
return;
|
||||
},
|
||||
?[:0]const u8 => {
|
||||
out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
||||
if (value) |payload| {
|
||||
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
||||
} else {
|
||||
out.writeAll("null;\n") catch unreachable;
|
||||
}
|
||||
return;
|
||||
},
|
||||
?[]const u8 => {
|
||||
out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
|
||||
if (value) |payload| {
|
||||
out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
|
||||
} else {
|
||||
out.writeAll("null;\n") catch unreachable;
|
||||
}
|
||||
return;
|
||||
},
|
||||
std.builtin.Version => {
|
||||
out.print(
|
||||
\\pub const {}: @import("std").builtin.Version = .{{
|
||||
\\ .major = {d},
|
||||
\\ .minor = {d},
|
||||
\\ .patch = {d},
|
||||
\\}};
|
||||
\\
|
||||
, .{
|
||||
std.zig.fmtId(name),
|
||||
|
||||
value.major,
|
||||
value.minor,
|
||||
value.patch,
|
||||
}) catch unreachable;
|
||||
},
|
||||
std.SemanticVersion => {
|
||||
out.print(
|
||||
\\pub const {}: @import("std").SemanticVersion = .{{
|
||||
\\ .major = {d},
|
||||
\\ .minor = {d},
|
||||
\\ .patch = {d},
|
||||
\\
|
||||
, .{
|
||||
std.zig.fmtId(name),
|
||||
|
||||
value.major,
|
||||
value.minor,
|
||||
value.patch,
|
||||
}) catch unreachable;
|
||||
if (value.pre) |some| {
|
||||
out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
||||
}
|
||||
if (value.build) |some| {
|
||||
out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (@typeInfo(T)) {
|
||||
.Enum => |enum_info| {
|
||||
out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
|
||||
inline for (enum_info.fields) |field| {
|
||||
out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
|
||||
}
|
||||
|
||||
/// The value is the path in the cache dir.
|
||||
/// Adds a dependency automatically.
|
||||
pub fn addOptionFileSource(
|
||||
self: *OptionsStep,
|
||||
name: []const u8,
|
||||
source: FileSource,
|
||||
) void {
|
||||
self.file_source_args.append(.{
|
||||
.name = name,
|
||||
.source = source.dupe(self.builder),
|
||||
}) catch unreachable;
|
||||
source.addStepDependencies(&self.step);
|
||||
}
|
||||
|
||||
/// The value is the path in the cache dir.
|
||||
/// Adds a dependency automatically.
|
||||
pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
|
||||
self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
|
||||
self.step.dependOn(&artifact.step);
|
||||
}
|
||||
|
||||
pub fn getPackage(self: OptionsStep, package_name: []const u8) build.Pkg {
|
||||
return .{ .name = package_name, .path = self.getSource() };
|
||||
}
|
||||
|
||||
pub fn getSource(self: OptionsStep) FileSource {
|
||||
return .{ .generated = &self.generated_file };
|
||||
}
|
||||
|
||||
fn make(step: *Step) !void {
|
||||
const self = @fieldParentPtr(OptionsStep, "step", step);
|
||||
|
||||
for (self.artifact_args.items) |item| {
|
||||
self.addOption(
|
||||
[]const u8,
|
||||
item.name,
|
||||
self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
|
||||
);
|
||||
}
|
||||
|
||||
for (self.file_source_args.items) |item| {
|
||||
self.addOption(
|
||||
[]const u8,
|
||||
item.name,
|
||||
item.source.getPath(self.builder),
|
||||
);
|
||||
}
|
||||
|
||||
const options_directory = self.builder.pathFromRoot(
|
||||
try fs.path.join(
|
||||
self.builder.allocator,
|
||||
&[_][]const u8{ self.builder.cache_root, "options" },
|
||||
),
|
||||
);
|
||||
|
||||
try fs.cwd().makePath(options_directory);
|
||||
|
||||
const options_file = try fs.path.join(
|
||||
self.builder.allocator,
|
||||
&[_][]const u8{ options_directory, &self.hashContentsToFileName() },
|
||||
);
|
||||
|
||||
try fs.cwd().writeFile(options_file, self.contents.items);
|
||||
|
||||
self.generated_file.path = options_file;
|
||||
}
|
||||
|
||||
fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
|
||||
// This implementation is copied from `WriteFileStep.make`
|
||||
|
||||
var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
|
||||
|
||||
// Random bytes to make OptionsStep unique. Refresh this with
|
||||
// new random bytes when OptionsStep implementation is modified
|
||||
// in a non-backwards-compatible way.
|
||||
hash.update("yL0Ya4KkmcCjBlP8");
|
||||
hash.update(self.contents.items);
|
||||
|
||||
var digest: [48]u8 = undefined;
|
||||
hash.final(&digest);
|
||||
var hash_basename: [64]u8 = undefined;
|
||||
_ = fs.base64_encoder.encode(&hash_basename, &digest);
|
||||
return hash_basename;
|
||||
}
|
||||
|
||||
const OptionArtifactArg = struct {
|
||||
name: []const u8,
|
||||
artifact: *LibExeObjStep,
|
||||
};
|
||||
|
||||
const OptionFileSourceArg = struct {
|
||||
name: []const u8,
|
||||
source: FileSource,
|
||||
};
|
||||
|
||||
test "OptionsStep" {
|
||||
if (std.builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
var builder = try Builder.create(
|
||||
&arena.allocator,
|
||||
"test",
|
||||
"test",
|
||||
"test",
|
||||
"test",
|
||||
);
|
||||
defer builder.destroy();
|
||||
|
||||
const options = builder.addOptions();
|
||||
|
||||
options.addOption(usize, "option1", 1);
|
||||
options.addOption(?usize, "option2", null);
|
||||
options.addOption([]const u8, "string", "zigisthebest");
|
||||
options.addOption(?[]const u8, "optional_string", null);
|
||||
options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\pub const option1: usize = 1;
|
||||
\\pub const option2: ?usize = null;
|
||||
\\pub const string: []const u8 = "zigisthebest";
|
||||
\\pub const optional_string: ?[]const u8 = null;
|
||||
\\pub const semantic_version: @import("std").SemanticVersion = .{
|
||||
\\ .major = 0,
|
||||
\\ .minor = 1,
|
||||
\\ .patch = 2,
|
||||
\\ .pre = "foo",
|
||||
\\ .build = "bar",
|
||||
\\};
|
||||
\\
|
||||
, options.contents.items);
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const build = std.build;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const build = std.build;
|
||||
const Step = build.Step;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const build = @import("../build.zig");
|
||||
const Step = build.Step;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// These are all deprecated.
|
||||
|
|
@ -237,7 +232,7 @@ pub const TypeInfo = union(enum) {
|
|||
/// This field is an optional type.
|
||||
/// The type of the sentinel is the element type of the pointer, which is
|
||||
/// the value of the `child` field in this struct. However there is no way
|
||||
/// to refer to that type here, so we use `var`.
|
||||
/// to refer to that type here, so we use `anytype`.
|
||||
sentinel: anytype,
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
|
|
@ -259,7 +254,7 @@ pub const TypeInfo = union(enum) {
|
|||
/// This field is an optional type.
|
||||
/// The type of the sentinel is the element type of the array, which is
|
||||
/// the value of the `child` field in this struct. However there is no way
|
||||
/// to refer to that type here, so we use `var`.
|
||||
/// to refer to that type here, so we use `anytype`.
|
||||
sentinel: anytype,
|
||||
};
|
||||
|
||||
|
|
@ -671,7 +666,12 @@ pub const PanicFn = fn ([]const u8, ?*StackTrace) noreturn;
|
|||
|
||||
/// This function is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_panic;
|
||||
pub const panic: PanicFn = if (@hasDecl(root, "panic"))
|
||||
root.panic
|
||||
else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))
|
||||
root.os.panic
|
||||
else
|
||||
default_panic;
|
||||
|
||||
/// This function is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
|
|
@ -684,10 +684,6 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
|
|||
@breakpoint();
|
||||
}
|
||||
}
|
||||
if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) {
|
||||
root.os.panic(msg, error_return_trace);
|
||||
unreachable;
|
||||
}
|
||||
switch (os.tag) {
|
||||
.freestanding => {
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const page_size = std.mem.page_size;
|
||||
|
|
@ -35,11 +30,11 @@ pub usingnamespace switch (std.Target.current.os.tag) {
|
|||
else => struct {},
|
||||
};
|
||||
|
||||
pub fn getErrno(rc: anytype) c_int {
|
||||
pub fn getErrno(rc: anytype) E {
|
||||
if (rc == -1) {
|
||||
return _errno().*;
|
||||
return @intToEnum(E, _errno().*);
|
||||
} else {
|
||||
return 0;
|
||||
return .SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,22 +265,22 @@ pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]timeval) c_int;
|
|||
pub extern "c" fn utimensat(dirfd: fd_t, pathname: [*:0]const u8, times: *[2]timespec, flags: u32) c_int;
|
||||
pub extern "c" fn futimens(fd: fd_t, times: *const [2]timespec) c_int;
|
||||
|
||||
pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: fn (?*c_void) callconv(.C) ?*c_void, noalias arg: ?*c_void) c_int;
|
||||
pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
|
||||
pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;
|
||||
pub extern "c" fn pthread_attr_setstacksize(attr: *pthread_attr_t, stacksize: usize) c_int;
|
||||
pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: usize) c_int;
|
||||
pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
|
||||
pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: fn (?*c_void) callconv(.C) ?*c_void, noalias arg: ?*c_void) E;
|
||||
pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) E;
|
||||
pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) E;
|
||||
pub extern "c" fn pthread_attr_setstacksize(attr: *pthread_attr_t, stacksize: usize) E;
|
||||
pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: usize) E;
|
||||
pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) E;
|
||||
pub extern "c" fn pthread_self() pthread_t;
|
||||
pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
|
||||
pub extern "c" fn pthread_detach(thread: pthread_t) c_int;
|
||||
pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) E;
|
||||
pub extern "c" fn pthread_detach(thread: pthread_t) E;
|
||||
pub extern "c" fn pthread_atfork(
|
||||
prepare: ?fn () callconv(.C) void,
|
||||
parent: ?fn () callconv(.C) void,
|
||||
child: ?fn () callconv(.C) void,
|
||||
) c_int;
|
||||
pub extern "c" fn pthread_key_create(key: *pthread_key_t, destructor: ?fn (value: *c_void) callconv(.C) void) c_int;
|
||||
pub extern "c" fn pthread_key_delete(key: pthread_key_t) c_int;
|
||||
pub extern "c" fn pthread_key_create(key: *pthread_key_t, destructor: ?fn (value: *c_void) callconv(.C) void) E;
|
||||
pub extern "c" fn pthread_key_delete(key: pthread_key_t) E;
|
||||
pub extern "c" fn pthread_getspecific(key: pthread_key_t) ?*c_void;
|
||||
pub extern "c" fn pthread_setspecific(key: pthread_key_t, value: ?*c_void) c_int;
|
||||
pub extern "c" fn sem_init(sem: *sem_t, pshared: c_int, value: c_uint) c_int;
|
||||
|
|
@ -339,24 +334,24 @@ pub extern "c" fn dn_expand(
|
|||
) c_int;
|
||||
|
||||
pub const PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{};
|
||||
pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) c_int;
|
||||
pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) c_int;
|
||||
pub extern "c" fn pthread_mutex_trylock(mutex: *pthread_mutex_t) c_int;
|
||||
pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int;
|
||||
pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) E;
|
||||
pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) E;
|
||||
pub extern "c" fn pthread_mutex_trylock(mutex: *pthread_mutex_t) E;
|
||||
pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) E;
|
||||
|
||||
pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{};
|
||||
pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int;
|
||||
pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int;
|
||||
pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int;
|
||||
pub extern "c" fn pthread_cond_broadcast(cond: *pthread_cond_t) c_int;
|
||||
pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
|
||||
pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) E;
|
||||
pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const timespec) E;
|
||||
pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) E;
|
||||
pub extern "c" fn pthread_cond_broadcast(cond: *pthread_cond_t) E;
|
||||
pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) E;
|
||||
|
||||
pub extern "c" fn pthread_rwlock_destroy(rwl: *pthread_rwlock_t) callconv(.C) c_int;
|
||||
pub extern "c" fn pthread_rwlock_rdlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
|
||||
pub extern "c" fn pthread_rwlock_wrlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
|
||||
pub extern "c" fn pthread_rwlock_tryrdlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
|
||||
pub extern "c" fn pthread_rwlock_trywrlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
|
||||
pub extern "c" fn pthread_rwlock_unlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
|
||||
pub extern "c" fn pthread_rwlock_destroy(rwl: *pthread_rwlock_t) callconv(.C) E;
|
||||
pub extern "c" fn pthread_rwlock_rdlock(rwl: *pthread_rwlock_t) callconv(.C) E;
|
||||
pub extern "c" fn pthread_rwlock_wrlock(rwl: *pthread_rwlock_t) callconv(.C) E;
|
||||
pub extern "c" fn pthread_rwlock_tryrdlock(rwl: *pthread_rwlock_t) callconv(.C) E;
|
||||
pub extern "c" fn pthread_rwlock_trywrlock(rwl: *pthread_rwlock_t) callconv(.C) E;
|
||||
pub extern "c" fn pthread_rwlock_unlock(rwl: *pthread_rwlock_t) callconv(.C) E;
|
||||
|
||||
pub const pthread_t = *opaque {};
|
||||
pub const FILE = opaque {};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
|
|
@ -193,8 +188,8 @@ pub const pthread_attr_t = extern struct {
|
|||
|
||||
const pthread_t = std.c.pthread_t;
|
||||
pub extern "c" fn pthread_threadid_np(thread: ?pthread_t, thread_id: *u64) c_int;
|
||||
pub extern "c" fn pthread_setname_np(name: [*:0]const u8) c_int;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) c_int;
|
||||
pub extern "c" fn pthread_setname_np(name: [*:0]const u8) E;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
|
||||
|
||||
pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
usingnamespace std.c;
|
||||
extern "c" threadlocal var errno: c_int;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
pub const pthread_mutex_t = extern struct {
|
||||
size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(4) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
usingnamespace std.c;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
pub const pthread_mutex_t = extern struct {
|
||||
size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
//
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
pub const pthread_mutex_t = extern struct {
|
||||
inner: usize = ~@as(usize, 0),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const maxInt = std.math.maxInt;
|
||||
const abi = std.Target.current.abi;
|
||||
|
|
@ -186,8 +181,8 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (os_tag == .fuchsia) 40 else switch (abi) {
|
|||
};
|
||||
const __SIZEOF_SEM_T = 4 * @sizeOf(usize);
|
||||
|
||||
pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8) c_int;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) c_int;
|
||||
pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8) E;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
|
||||
|
||||
pub const RTLD_LAZY = 1;
|
||||
pub const RTLD_NOW = 2;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const builtin = @import("builtin");
|
||||
pub const pthread_mutex_t = extern struct {
|
||||
size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
|
||||
|
|
@ -95,5 +90,5 @@ pub const pthread_attr_t = extern struct {
|
|||
|
||||
pub const sem_t = ?*opaque {};
|
||||
|
||||
pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*c_void) c_int;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) c_int;
|
||||
pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*c_void) E;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
pub const pthread_mutex_t = extern struct {
|
||||
__pthread_mutex_flag1: u16 = 0,
|
||||
__pthread_mutex_flag2: u8 = 0,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
usingnamespace @import("../os/bits.zig");
|
||||
|
||||
extern threadlocal var errno: c_int;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
pub extern "c" fn _errno() *c_int;
|
||||
|
||||
pub extern "c" fn _msize(memblock: ?*c_void) usize;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const cstr = std.cstr;
|
||||
const unicode = std.unicode;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const builtin = std.builtin;
|
||||
const std = @import("std.zig");
|
||||
const io = std.io;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
|
||||
pub const deflate = @import("compress/deflate.zig");
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
//
|
||||
// Decompressor for DEFLATE data streams (RFC1951)
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
//
|
||||
// Decompressor for GZIP data streams (RFC1952)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
//
|
||||
// Decompressor for ZLIB data streams (RFC1950)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std.zig");
|
||||
const mem = std.mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
/// Authenticated Encryption with Associated Data
|
||||
pub const aead = struct {
|
||||
pub const aegis = struct {
|
||||
|
|
@ -110,7 +104,16 @@ pub const onetimeauth = struct {
|
|||
///
|
||||
/// Password hashing functions must be used whenever sensitive data has to be directly derived from a password.
|
||||
pub const pwhash = struct {
|
||||
pub const Encoding = enum {
|
||||
phc,
|
||||
crypt,
|
||||
};
|
||||
pub const KdfError = errors.Error || std.mem.Allocator.Error;
|
||||
pub const HasherError = KdfError || @import("crypto/phc_encoding.zig").Error;
|
||||
pub const Error = HasherError || error{AllocatorRequired};
|
||||
|
||||
pub const bcrypt = @import("crypto/bcrypt.zig");
|
||||
pub const scrypt = @import("crypto/scrypt.zig");
|
||||
pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const readIntLittle = std.mem.readIntLittle;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const fmt = std.fmt;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const mem = std.mem;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("../std.zig");
|
||||
const testing = std.testing;
|
||||
const builtin = std.builtin;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("../../std.zig");
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("../../std.zig");
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// Based on Go stdlib implementation
|
||||
|
||||
const std = @import("../../std.zig");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = std.builtin;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const aes = crypto.core.aes;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
const fmt = std.fmt;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
const pwhash = crypto.pwhash;
|
||||
const testing = std.testing;
|
||||
const utils = crypto.utils;
|
||||
const EncodingError = crypto.errors.EncodingError;
|
||||
const PasswordVerificationError = crypto.errors.PasswordVerificationError;
|
||||
|
||||
const phc_format = @import("phc_encoding.zig");
|
||||
|
||||
const KdfError = pwhash.KdfError;
|
||||
const HasherError = pwhash.HasherError;
|
||||
const EncodingError = phc_format.Error;
|
||||
const Error = pwhash.Error;
|
||||
|
||||
const salt_length: usize = 16;
|
||||
const salt_str_length: usize = 22;
|
||||
const ct_str_length: usize = 31;
|
||||
const ct_length: usize = 24;
|
||||
const dk_length: usize = ct_length - 1;
|
||||
|
||||
/// Length (in bytes) of a password hash
|
||||
/// Length (in bytes) of a password hash in crypt encoding
|
||||
pub const hash_length: usize = 60;
|
||||
|
||||
const State = struct {
|
||||
|
|
@ -139,8 +140,50 @@ const State = struct {
|
|||
}
|
||||
};
|
||||
|
||||
// bcrypt has its own variant of base64, with its own alphabet and no padding
|
||||
const Codec = struct {
|
||||
pub const Params = struct {
|
||||
rounds_log: u6,
|
||||
};
|
||||
|
||||
pub fn bcrypt(
|
||||
password: []const u8,
|
||||
salt: [salt_length]u8,
|
||||
params: Params,
|
||||
) [dk_length]u8 {
|
||||
var state = State{};
|
||||
var password_buf: [73]u8 = undefined;
|
||||
const trimmed_len = math.min(password.len, password_buf.len - 1);
|
||||
mem.copy(u8, password_buf[0..], password[0..trimmed_len]);
|
||||
password_buf[trimmed_len] = 0;
|
||||
var passwordZ = password_buf[0 .. trimmed_len + 1];
|
||||
state.expand(salt[0..], passwordZ);
|
||||
|
||||
const rounds: u64 = @as(u64, 1) << params.rounds_log;
|
||||
var k: u64 = 0;
|
||||
while (k < rounds) : (k += 1) {
|
||||
state.expand0(passwordZ);
|
||||
state.expand0(salt[0..]);
|
||||
}
|
||||
utils.secureZero(u8, &password_buf);
|
||||
|
||||
var cdata = [6]u32{ 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; // "OrpheanBeholderScryDoubt"
|
||||
k = 0;
|
||||
while (k < 64) : (k += 1) {
|
||||
state.encrypt(&cdata);
|
||||
}
|
||||
|
||||
var ct: [ct_length]u8 = undefined;
|
||||
for (cdata) |c, i| {
|
||||
mem.writeIntBig(u32, ct[i * 4 ..][0..4], c);
|
||||
}
|
||||
return ct[0..dk_length].*;
|
||||
}
|
||||
|
||||
const crypt_format = struct {
|
||||
/// String prefix for bcrypt
|
||||
pub const prefix = "$2";
|
||||
|
||||
// bcrypt has its own variant of base64, with its own alphabet and no padding
|
||||
const Codec = struct {
|
||||
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
fn encode(b64: []u8, bin: []const u8) void {
|
||||
|
|
@ -182,66 +225,153 @@ const Codec = struct {
|
|||
var i: usize = 0;
|
||||
var j: usize = 0;
|
||||
while (j < bin.len) {
|
||||
const c1 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i]) orelse return error.InvalidEncoding);
|
||||
const c2 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 1]) orelse return error.InvalidEncoding);
|
||||
const c1 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i]) orelse
|
||||
return EncodingError.InvalidEncoding);
|
||||
const c2 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 1]) orelse
|
||||
return EncodingError.InvalidEncoding);
|
||||
bin[j] = (c1 << 2) | ((c2 & 0x30) >> 4);
|
||||
j += 1;
|
||||
if (j >= bin.len) {
|
||||
break;
|
||||
}
|
||||
const c3 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 2]) orelse return error.InvalidEncoding);
|
||||
const c3 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 2]) orelse
|
||||
return EncodingError.InvalidEncoding);
|
||||
bin[j] = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
|
||||
j += 1;
|
||||
if (j >= bin.len) {
|
||||
break;
|
||||
}
|
||||
const c4 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 3]) orelse return error.InvalidEncoding);
|
||||
const c4 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 3]) orelse
|
||||
return EncodingError.InvalidEncoding);
|
||||
bin[j] = ((c3 & 0x03) << 6) | c4;
|
||||
j += 1;
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) ![hash_length]u8 {
|
||||
var state = State{};
|
||||
var password_buf: [73]u8 = undefined;
|
||||
const trimmed_len = math.min(password.len, password_buf.len - 1);
|
||||
mem.copy(u8, password_buf[0..], password[0..trimmed_len]);
|
||||
password_buf[trimmed_len] = 0;
|
||||
var passwordZ = password_buf[0 .. trimmed_len + 1];
|
||||
state.expand(salt[0..], passwordZ);
|
||||
|
||||
const rounds: u64 = @as(u64, 1) << rounds_log;
|
||||
var k: u64 = 0;
|
||||
while (k < rounds) : (k += 1) {
|
||||
state.expand0(passwordZ);
|
||||
state.expand0(salt[0..]);
|
||||
}
|
||||
utils.secureZero(u8, &password_buf);
|
||||
|
||||
var cdata = [6]u32{ 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; // "OrpheanBeholderScryDoubt"
|
||||
k = 0;
|
||||
while (k < 64) : (k += 1) {
|
||||
state.encrypt(&cdata);
|
||||
}
|
||||
|
||||
var ct: [ct_length]u8 = undefined;
|
||||
for (cdata) |c, i| {
|
||||
mem.writeIntBig(u32, ct[i * 4 ..][0..4], c);
|
||||
}
|
||||
fn strHashInternal(
|
||||
password: []const u8,
|
||||
salt: [salt_length]u8,
|
||||
params: Params,
|
||||
) [hash_length]u8 {
|
||||
var dk = bcrypt(password, salt, params);
|
||||
|
||||
var salt_str: [salt_str_length]u8 = undefined;
|
||||
Codec.encode(salt_str[0..], salt[0..]);
|
||||
|
||||
var ct_str: [ct_str_length]u8 = undefined;
|
||||
Codec.encode(ct_str[0..], ct[0 .. ct.len - 1]);
|
||||
Codec.encode(ct_str[0..], dk[0..]);
|
||||
|
||||
var s_buf: [hash_length]u8 = undefined;
|
||||
const s = fmt.bufPrint(s_buf[0..], "$2b${d}{d}${s}{s}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable;
|
||||
const s = fmt.bufPrint(
|
||||
s_buf[0..],
|
||||
"{s}b${d}{d}${s}{s}",
|
||||
.{ prefix, params.rounds_log / 10, params.rounds_log % 10, salt_str, ct_str },
|
||||
) catch unreachable;
|
||||
debug.assert(s.len == s_buf.len);
|
||||
return s_buf;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash and verify passwords using the PHC format.
|
||||
const PhcFormatHasher = struct {
|
||||
const alg_id = "bcrypt";
|
||||
const BinValue = phc_format.BinValue;
|
||||
|
||||
const HashResult = struct {
|
||||
alg_id: []const u8,
|
||||
r: u6,
|
||||
salt: BinValue(salt_length),
|
||||
hash: BinValue(dk_length),
|
||||
};
|
||||
|
||||
/// Return a non-deterministic hash of the password encoded as a PHC-format string
|
||||
pub fn create(
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
) HasherError![]const u8 {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
crypto.random.bytes(&salt);
|
||||
|
||||
const hash = bcrypt(password, salt, params);
|
||||
|
||||
return phc_format.serialize(HashResult{
|
||||
.alg_id = alg_id,
|
||||
.r = params.rounds_log,
|
||||
.salt = try BinValue(salt_length).fromSlice(&salt),
|
||||
.hash = try BinValue(dk_length).fromSlice(&hash),
|
||||
}, buf);
|
||||
}
|
||||
|
||||
/// Verify a password against a PHC-format encoded string
|
||||
pub fn verify(
|
||||
str: []const u8,
|
||||
password: []const u8,
|
||||
) HasherError!void {
|
||||
const hash_result = try phc_format.deserialize(HashResult, str);
|
||||
|
||||
if (!mem.eql(u8, hash_result.alg_id, alg_id)) return HasherError.PasswordVerificationFailed;
|
||||
if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)
|
||||
return HasherError.InvalidEncoding;
|
||||
|
||||
const hash = bcrypt(password, hash_result.salt.buf, .{ .rounds_log = hash_result.r });
|
||||
const expected_hash = hash_result.hash.constSlice();
|
||||
|
||||
if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash and verify passwords using the modular crypt format.
|
||||
const CryptFormatHasher = struct {
|
||||
/// Length of a string returned by the create() function
|
||||
pub const pwhash_str_length: usize = hash_length;
|
||||
|
||||
/// Return a non-deterministic hash of the password encoded into the modular crypt format
|
||||
pub fn create(
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
) HasherError![]const u8 {
|
||||
if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
|
||||
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
crypto.random.bytes(&salt);
|
||||
|
||||
const hash = crypt_format.strHashInternal(password, salt, params);
|
||||
mem.copy(u8, buf, &hash);
|
||||
|
||||
return buf[0..pwhash_str_length];
|
||||
}
|
||||
|
||||
/// Verify a password against a string in modular crypt format
|
||||
pub fn verify(
|
||||
str: []const u8,
|
||||
password: []const u8,
|
||||
) HasherError!void {
|
||||
if (str.len != pwhash_str_length or str[3] != '$' or str[6] != '$')
|
||||
return HasherError.InvalidEncoding;
|
||||
|
||||
const rounds_log_str = str[4..][0..2];
|
||||
const rounds_log = fmt.parseInt(u6, rounds_log_str[0..], 10) catch
|
||||
return HasherError.InvalidEncoding;
|
||||
|
||||
const salt_str = str[7..][0..salt_str_length];
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
try crypt_format.Codec.decode(salt[0..], salt_str[0..]);
|
||||
|
||||
const wanted_s = crypt_format.strHashInternal(password, salt, .{ .rounds_log = rounds_log });
|
||||
if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed;
|
||||
}
|
||||
};
|
||||
|
||||
/// Options for hashing a password.
|
||||
pub const HashOptions = struct {
|
||||
allocator: ?*mem.Allocator = null,
|
||||
params: Params,
|
||||
encoding: pwhash.Encoding,
|
||||
};
|
||||
|
||||
/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
|
||||
/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.
|
||||
|
|
@ -251,24 +381,32 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
|
|||
/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
|
||||
/// If this is an issue for your application, hash the password first using a function such as SHA-512,
|
||||
/// and then use the resulting hash as the password parameter for bcrypt.
|
||||
pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
crypto.random.bytes(&salt);
|
||||
return strHashInternal(password, rounds_log, salt);
|
||||
pub fn strHash(
|
||||
password: []const u8,
|
||||
options: HashOptions,
|
||||
out: []u8,
|
||||
) Error![]const u8 {
|
||||
switch (options.encoding) {
|
||||
.phc => return PhcFormatHasher.create(password, options.params, out),
|
||||
.crypt => return CryptFormatHasher.create(password, options.params, out),
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for hash verification.
|
||||
pub const VerifyOptions = struct {
|
||||
allocator: ?*mem.Allocator = null,
|
||||
};
|
||||
|
||||
/// Verify that a previously computed hash is valid for a given password.
|
||||
pub fn strVerify(h: [hash_length]u8, password: []const u8) (EncodingError || PasswordVerificationError)!void {
|
||||
if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding;
|
||||
if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;
|
||||
const rounds_log_str = h[4..][0..2];
|
||||
const salt_str = h[7..][0..salt_str_length];
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
try Codec.decode(salt[0..], salt_str[0..]);
|
||||
const rounds_log = fmt.parseInt(u6, rounds_log_str[0..], 10) catch return error.InvalidEncoding;
|
||||
const wanted_s = try strHashInternal(password, rounds_log, salt);
|
||||
if (!mem.eql(u8, wanted_s[0..], h[0..])) {
|
||||
return error.PasswordVerificationFailed;
|
||||
pub fn strVerify(
|
||||
str: []const u8,
|
||||
password: []const u8,
|
||||
_: VerifyOptions,
|
||||
) Error!void {
|
||||
if (mem.startsWith(u8, str, crypt_format.prefix)) {
|
||||
return CryptFormatHasher.verify(str, password);
|
||||
} else {
|
||||
return PhcFormatHasher.verify(str, password);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -276,20 +414,71 @@ test "bcrypt codec" {
|
|||
var salt: [salt_length]u8 = undefined;
|
||||
crypto.random.bytes(&salt);
|
||||
var salt_str: [salt_str_length]u8 = undefined;
|
||||
Codec.encode(salt_str[0..], salt[0..]);
|
||||
crypt_format.Codec.encode(salt_str[0..], salt[0..]);
|
||||
var salt2: [salt_length]u8 = undefined;
|
||||
try Codec.decode(salt2[0..], salt_str[0..]);
|
||||
try crypt_format.Codec.decode(salt2[0..], salt_str[0..]);
|
||||
try testing.expectEqualSlices(u8, salt[0..], salt2[0..]);
|
||||
}
|
||||
|
||||
test "bcrypt" {
|
||||
const s = try strHash("password", 5);
|
||||
try strVerify(s, "password");
|
||||
try testing.expectError(error.PasswordVerificationFailed, strVerify(s, "invalid password"));
|
||||
test "bcrypt crypt format" {
|
||||
const hash_options = HashOptions{
|
||||
.params = .{ .rounds_log = 5 },
|
||||
.encoding = .crypt,
|
||||
};
|
||||
const verify_options = VerifyOptions{};
|
||||
|
||||
const long_s = try strHash("password" ** 100, 5);
|
||||
try strVerify(long_s, "password" ** 100);
|
||||
try strVerify(long_s, "password" ** 101);
|
||||
var buf: [hash_length]u8 = undefined;
|
||||
const s = try strHash("password", hash_options, &buf);
|
||||
|
||||
try strVerify("$2b$08$WUQKyBCaKpziCwUXHiMVvu40dYVjkTxtWJlftl0PpjY2BxWSvFIEe".*, "The devil himself");
|
||||
try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));
|
||||
try strVerify(s, "password", verify_options);
|
||||
try testing.expectError(
|
||||
error.PasswordVerificationFailed,
|
||||
strVerify(s, "invalid password", verify_options),
|
||||
);
|
||||
|
||||
var long_buf: [hash_length]u8 = undefined;
|
||||
const long_s = try strHash("password" ** 100, hash_options, &long_buf);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));
|
||||
try strVerify(long_s, "password" ** 100, verify_options);
|
||||
try strVerify(long_s, "password" ** 101, verify_options);
|
||||
|
||||
try strVerify(
|
||||
"$2b$08$WUQKyBCaKpziCwUXHiMVvu40dYVjkTxtWJlftl0PpjY2BxWSvFIEe",
|
||||
"The devil himself",
|
||||
verify_options,
|
||||
);
|
||||
}
|
||||
|
||||
test "bcrypt phc format" {
|
||||
const hash_options = HashOptions{
|
||||
.params = .{ .rounds_log = 5 },
|
||||
.encoding = .phc,
|
||||
};
|
||||
const verify_options = VerifyOptions{};
|
||||
const prefix = "$bcrypt$";
|
||||
|
||||
var buf: [hash_length * 2]u8 = undefined;
|
||||
const s = try strHash("password", hash_options, &buf);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, s, prefix));
|
||||
try strVerify(s, "password", verify_options);
|
||||
try testing.expectError(
|
||||
error.PasswordVerificationFailed,
|
||||
strVerify(s, "invalid password", verify_options),
|
||||
);
|
||||
|
||||
var long_buf: [hash_length * 2]u8 = undefined;
|
||||
const long_s = try strHash("password" ** 100, hash_options, &long_buf);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, long_s, prefix));
|
||||
try strVerify(long_s, "password" ** 100, verify_options);
|
||||
try strVerify(long_s, "password" ** 101, verify_options);
|
||||
|
||||
try strVerify(
|
||||
"$bcrypt$r=5$2NopntlgE2lX3cTwr4qz8A$r3T7iKYQNnY4hAhGjk9RmuyvgrYJZwc",
|
||||
"The devil himself",
|
||||
verify_options,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// zig run benchmark.zig --release-fast --zig-lib-dir ..
|
||||
|
||||
const std = @import("../std.zig");
|
||||
|
|
@ -300,6 +295,43 @@ pub fn benchmarkAes8(comptime Aes: anytype, comptime count: comptime_int) !u64 {
|
|||
return throughput;
|
||||
}
|
||||
|
||||
const CryptoPwhash = struct {
|
||||
hashFn: anytype,
|
||||
params: anytype,
|
||||
name: []const u8,
|
||||
};
|
||||
const bcrypt_params = bcrypt.Params{ .rounds_log = 5 };
|
||||
const pwhashes = [_]CryptoPwhash{
|
||||
CryptoPwhash{ .hashFn = bcrypt.strHash, .params = bcrypt_params, .name = "bcrypt" },
|
||||
CryptoPwhash{ .hashFn = scrypt.strHash, .params = scrypt.Params.interactive, .name = "scrypt" },
|
||||
};
|
||||
|
||||
fn benchmarkPwhash(
|
||||
comptime hashFn: anytype,
|
||||
comptime params: anytype,
|
||||
comptime count: comptime_int,
|
||||
) !u64 {
|
||||
const password = "testpass" ** 2;
|
||||
const opts = .{ .allocator = std.testing.allocator, .params = params, .encoding = .phc };
|
||||
var buf: [256]u8 = undefined;
|
||||
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
_ = try hashFn(password, opts, &buf);
|
||||
mem.doNotOptimizeAway(&buf);
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
const throughput = @floatToInt(u64, count / elapsed_s);
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
fn usage() void {
|
||||
std.debug.warn(
|
||||
\\throughput_test [options]
|
||||
|
|
@ -418,4 +450,11 @@ pub fn main() !void {
|
|||
try stdout.print("{s:>17}: {:10} ops/s\n", .{ E.name, throughput });
|
||||
}
|
||||
}
|
||||
|
||||
inline for (pwhashes) |H| {
|
||||
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
|
||||
const throughput = try benchmarkPwhash(H.hashFn, H.params, mode(64));
|
||||
try stdout.print("{s:>17}: {:10} ops/s\n", .{ H.name, throughput });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// Translated from BLAKE3 reference implementation.
|
||||
// Source: https://github.com/BLAKE3-team/BLAKE3
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// Based on public domain Supercop by Daniel J. Bernstein
|
||||
|
||||
const std = @import("../std.zig");
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
//
|
||||
// Adapted from BearSSL's ctmul64 implementation originally written by Thomas Pornin <pornin@bolet.org>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const hmac = std.crypto.auth.hmac;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
// Based on Go stdlib implementation
|
||||
|
||||
const std = @import("../std.zig");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const crypto = std.crypto;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const common = @import("../common.zig");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const common = @import("../common.zig");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const fmt = std.fmt;
|
||||
const testing = std.testing;
|
||||
|
|
|
|||
371
lib/std/crypto/phc_encoding.zig
Normal file
371
lib/std/crypto/phc_encoding.zig
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
// https://github.com/P-H-C/phc-string-format
|
||||
|
||||
const std = @import("std");
|
||||
const fmt = std.fmt;
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
|
||||
const fields_delimiter = "$";
|
||||
const version_param_name = "v";
|
||||
const params_delimiter = ",";
|
||||
const kv_delimiter = "=";
|
||||
|
||||
pub const Error = std.crypto.errors.EncodingError || error{NoSpaceLeft};
|
||||
|
||||
const B64Decoder = std.base64.standard_no_pad.Decoder;
|
||||
const B64Encoder = std.base64.standard_no_pad.Encoder;
|
||||
|
||||
/// A wrapped binary value whose maximum size is `max_len`.
|
||||
///
|
||||
/// This type must be used whenever a binary value is encoded in a PHC-formatted string.
|
||||
/// This includes `salt`, `hash`, and any other binary parameters such as keys.
|
||||
///
|
||||
/// Once initialized, the actual value can be read with the `constSlice()` function.
|
||||
pub fn BinValue(comptime max_len: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
const capacity = max_len;
|
||||
const max_encoded_length = B64Encoder.calcSize(max_len);
|
||||
|
||||
buf: [max_len]u8 = undefined,
|
||||
len: usize = 0,
|
||||
|
||||
/// Wrap an existing byte slice
|
||||
pub fn fromSlice(slice: []const u8) Error!Self {
|
||||
if (slice.len > capacity) return Error.NoSpaceLeft;
|
||||
var bin_value: Self = undefined;
|
||||
mem.copy(u8, &bin_value.buf, slice);
|
||||
bin_value.len = slice.len;
|
||||
return bin_value;
|
||||
}
|
||||
|
||||
/// Return the slice containing the actual value.
|
||||
pub fn constSlice(self: Self) []const u8 {
|
||||
return self.buf[0..self.len];
|
||||
}
|
||||
|
||||
fn fromB64(self: *Self, str: []const u8) !void {
|
||||
const len = B64Decoder.calcSizeForSlice(str) catch return Error.InvalidEncoding;
|
||||
if (len > self.buf.len) return Error.NoSpaceLeft;
|
||||
B64Decoder.decode(&self.buf, str) catch return Error.InvalidEncoding;
|
||||
self.len = len;
|
||||
}
|
||||
|
||||
fn toB64(self: Self, buf: []u8) ![]const u8 {
|
||||
const value = self.constSlice();
|
||||
const len = B64Encoder.calcSize(value.len);
|
||||
if (len > buf.len) return Error.NoSpaceLeft;
|
||||
return B64Encoder.encode(buf, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Deserialize a PHC-formatted string into a structure `HashResult`.
|
||||
///
|
||||
/// Required field in the `HashResult` structure:
|
||||
/// - `alg_id`: algorithm identifier
|
||||
/// Optional, special fields:
|
||||
/// - `alg_version`: algorithm version (unsigned integer)
|
||||
/// - `salt`: salt
|
||||
/// - `hash`: output of the hash function
|
||||
///
|
||||
/// Other fields will also be deserialized from the function parameters section.
|
||||
pub fn deserialize(comptime HashResult: type, str: []const u8) Error!HashResult {
|
||||
var out = mem.zeroes(HashResult);
|
||||
var it = mem.split(u8, str, fields_delimiter);
|
||||
var set_fields: usize = 0;
|
||||
|
||||
while (true) {
|
||||
// Read the algorithm identifier
|
||||
if ((it.next() orelse return Error.InvalidEncoding).len != 0) return Error.InvalidEncoding;
|
||||
out.alg_id = it.next() orelse return Error.InvalidEncoding;
|
||||
set_fields += 1;
|
||||
|
||||
// Read the optional version number
|
||||
var field = it.next() orelse break;
|
||||
if (kvSplit(field)) |opt_version| {
|
||||
if (mem.eql(u8, opt_version.key, version_param_name)) {
|
||||
if (@hasField(HashResult, "alg_version")) {
|
||||
const value_type_info = switch (@typeInfo(@TypeOf(out.alg_version))) {
|
||||
.Optional => |opt| comptime @typeInfo(opt.child),
|
||||
else => |t| t,
|
||||
};
|
||||
out.alg_version = fmt.parseUnsigned(
|
||||
@Type(value_type_info),
|
||||
opt_version.value,
|
||||
10,
|
||||
) catch return Error.InvalidEncoding;
|
||||
set_fields += 1;
|
||||
}
|
||||
field = it.next() orelse break;
|
||||
}
|
||||
} else |_| {}
|
||||
|
||||
// Read optional parameters
|
||||
var has_params = false;
|
||||
var it_params = mem.split(u8, field, params_delimiter);
|
||||
while (it_params.next()) |params| {
|
||||
const param = kvSplit(params) catch break;
|
||||
var found = false;
|
||||
inline for (comptime meta.fields(HashResult)) |p| {
|
||||
if (mem.eql(u8, p.name, param.key)) {
|
||||
switch (@typeInfo(p.field_type)) {
|
||||
.Int => @field(out, p.name) = fmt.parseUnsigned(
|
||||
p.field_type,
|
||||
param.value,
|
||||
10,
|
||||
) catch return Error.InvalidEncoding,
|
||||
.Pointer => |ptr| {
|
||||
if (!ptr.is_const) @compileError("Value slice must be constant");
|
||||
@field(out, p.name) = param.value;
|
||||
},
|
||||
.Struct => try @field(out, p.name).fromB64(param.value),
|
||||
else => std.debug.panic(
|
||||
"Value for [{s}] must be an integer, a constant slice or a BinValue",
|
||||
.{p.name},
|
||||
),
|
||||
}
|
||||
set_fields += 1;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return Error.InvalidEncoding; // An unexpected parameter was found in the string
|
||||
has_params = true;
|
||||
}
|
||||
|
||||
// No separator between an empty parameters set and the salt
|
||||
if (has_params) field = it.next() orelse break;
|
||||
|
||||
// Read an optional salt
|
||||
if (@hasField(HashResult, "salt")) {
|
||||
try out.salt.fromB64(field);
|
||||
set_fields += 1;
|
||||
} else {
|
||||
return Error.InvalidEncoding;
|
||||
}
|
||||
|
||||
// Read an optional hash
|
||||
field = it.next() orelse break;
|
||||
if (@hasField(HashResult, "hash")) {
|
||||
try out.hash.fromB64(field);
|
||||
set_fields += 1;
|
||||
} else {
|
||||
return Error.InvalidEncoding;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Check that all the required fields have been set, excluding optional values and parameters
|
||||
// with default values
|
||||
var expected_fields: usize = 0;
|
||||
inline for (comptime meta.fields(HashResult)) |p| {
|
||||
if (@typeInfo(p.field_type) != .Optional and p.default_value == null) {
|
||||
expected_fields += 1;
|
||||
}
|
||||
}
|
||||
if (set_fields < expected_fields) return Error.InvalidEncoding;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Serialize parameters into a PHC string.
|
||||
///
|
||||
/// Required field for `params`:
|
||||
/// - `alg_id`: algorithm identifier
|
||||
/// Optional, special fields:
|
||||
/// - `alg_version`: algorithm version (unsigned integer)
|
||||
/// - `salt`: salt
|
||||
/// - `hash`: output of the hash function
|
||||
///
|
||||
/// `params` can also include any additional parameters.
|
||||
pub fn serialize(params: anytype, str: []u8) Error![]const u8 {
|
||||
var buf = io.fixedBufferStream(str);
|
||||
try serializeTo(params, buf.writer());
|
||||
return buf.getWritten();
|
||||
}
|
||||
|
||||
/// Compute the number of bytes required to serialize `params`
|
||||
pub fn calcSize(params: anytype) usize {
|
||||
var buf = io.countingWriter(io.null_writer);
|
||||
serializeTo(params, buf.writer()) catch unreachable;
|
||||
return @intCast(usize, buf.bytes_written);
|
||||
}
|
||||
|
||||
fn serializeTo(params: anytype, out: anytype) !void {
|
||||
const HashResult = @TypeOf(params);
|
||||
try out.writeAll(fields_delimiter);
|
||||
try out.writeAll(params.alg_id);
|
||||
|
||||
if (@hasField(HashResult, "alg_version")) {
|
||||
if (@typeInfo(@TypeOf(params.alg_version)) == .Optional) {
|
||||
if (params.alg_version) |alg_version| {
|
||||
try out.print(
|
||||
"{s}{s}{s}{}",
|
||||
.{ fields_delimiter, version_param_name, kv_delimiter, alg_version },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
try out.print(
|
||||
"{s}{s}{s}{}",
|
||||
.{ fields_delimiter, version_param_name, kv_delimiter, params.alg_version },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var has_params = false;
|
||||
inline for (comptime meta.fields(HashResult)) |p| {
|
||||
if (!(mem.eql(u8, p.name, "alg_id") or
|
||||
mem.eql(u8, p.name, "alg_version") or
|
||||
mem.eql(u8, p.name, "hash") or
|
||||
mem.eql(u8, p.name, "salt")))
|
||||
{
|
||||
const value = @field(params, p.name);
|
||||
try out.writeAll(if (has_params) params_delimiter else fields_delimiter);
|
||||
if (@typeInfo(p.field_type) == .Struct) {
|
||||
var buf: [@TypeOf(value).max_encoded_length]u8 = undefined;
|
||||
try out.print("{s}{s}{s}", .{ p.name, kv_delimiter, try value.toB64(&buf) });
|
||||
} else {
|
||||
try out.print(
|
||||
if (@typeInfo(@TypeOf(value)) == .Pointer) "{s}{s}{s}" else "{s}{s}{}",
|
||||
.{ p.name, kv_delimiter, value },
|
||||
);
|
||||
}
|
||||
has_params = true;
|
||||
}
|
||||
}
|
||||
|
||||
var has_salt = false;
|
||||
if (@hasField(HashResult, "salt")) {
|
||||
var buf: [@TypeOf(params.salt).max_encoded_length]u8 = undefined;
|
||||
try out.print("{s}{s}", .{ fields_delimiter, try params.salt.toB64(&buf) });
|
||||
has_salt = true;
|
||||
}
|
||||
|
||||
if (@hasField(HashResult, "hash")) {
|
||||
var buf: [@TypeOf(params.hash).max_encoded_length]u8 = undefined;
|
||||
if (!has_salt) try out.writeAll(fields_delimiter);
|
||||
try out.print("{s}{s}", .{ fields_delimiter, try params.hash.toB64(&buf) });
|
||||
}
|
||||
}
|
||||
|
||||
// Split a `key=value` string into `key` and `value`
|
||||
fn kvSplit(str: []const u8) !struct { key: []const u8, value: []const u8 } {
|
||||
var it = mem.split(u8, str, kv_delimiter);
|
||||
const key = it.next() orelse return Error.InvalidEncoding;
|
||||
const value = it.next() orelse return Error.InvalidEncoding;
|
||||
const ret = .{ .key = key, .value = value };
|
||||
return ret;
|
||||
}
|
||||
|
||||
test "phc format - encoding/decoding" {
|
||||
const Input = struct {
|
||||
str: []const u8,
|
||||
HashResult: type,
|
||||
};
|
||||
const inputs = [_]Input{
|
||||
.{
|
||||
.str = "$argon2id$v=19$key=a2V5,m=4096,t=0,p=1$X1NhbHQAAAAAAAAAAAAAAA$bWh++MKN1OiFHKgIWTLvIi1iHicmHH7+Fv3K88ifFfI",
|
||||
.HashResult = struct {
|
||||
alg_id: []const u8,
|
||||
alg_version: u16,
|
||||
key: BinValue(16),
|
||||
m: usize,
|
||||
t: u64,
|
||||
p: u32,
|
||||
salt: BinValue(16),
|
||||
hash: BinValue(32),
|
||||
},
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$v=1$ln=15,r=8,p=1$c2FsdHNhbHQ$dGVzdHBhc3M",
|
||||
.HashResult = struct {
|
||||
alg_id: []const u8,
|
||||
alg_version: ?u30,
|
||||
ln: u6,
|
||||
r: u30,
|
||||
p: u30,
|
||||
salt: BinValue(16),
|
||||
hash: BinValue(16),
|
||||
},
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt",
|
||||
.HashResult = struct { alg_id: []const u8 },
|
||||
},
|
||||
.{ .str = "$scrypt$v=1", .HashResult = struct { alg_id: []const u8, alg_version: u16 } },
|
||||
.{
|
||||
.str = "$scrypt$ln=15,r=8,p=1",
|
||||
.HashResult = struct { alg_id: []const u8, alg_version: ?u30, ln: u6, r: u30, p: u30 },
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$c2FsdHNhbHQ",
|
||||
.HashResult = struct { alg_id: []const u8, salt: BinValue(16) },
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$v=1$ln=15,r=8,p=1$c2FsdHNhbHQ",
|
||||
.HashResult = struct {
|
||||
alg_id: []const u8,
|
||||
alg_version: u16,
|
||||
ln: u6,
|
||||
r: u30,
|
||||
p: u30,
|
||||
salt: BinValue(16),
|
||||
},
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$v=1$ln=15,r=8,p=1",
|
||||
.HashResult = struct { alg_id: []const u8, alg_version: ?u30, ln: u6, r: u30, p: u30 },
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$v=1$c2FsdHNhbHQ$dGVzdHBhc3M",
|
||||
.HashResult = struct {
|
||||
alg_id: []const u8,
|
||||
alg_version: u16,
|
||||
salt: BinValue(16),
|
||||
hash: BinValue(16),
|
||||
},
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$v=1$c2FsdHNhbHQ",
|
||||
.HashResult = struct { alg_id: []const u8, alg_version: u16, salt: BinValue(16) },
|
||||
},
|
||||
.{
|
||||
.str = "$scrypt$c2FsdHNhbHQ$dGVzdHBhc3M",
|
||||
.HashResult = struct { alg_id: []const u8, salt: BinValue(16), hash: BinValue(16) },
|
||||
},
|
||||
};
|
||||
inline for (inputs) |input| {
|
||||
const v = try deserialize(input.HashResult, input.str);
|
||||
var buf: [input.str.len]u8 = undefined;
|
||||
const s1 = try serialize(v, &buf);
|
||||
try std.testing.expectEqualSlices(u8, input.str, s1);
|
||||
}
|
||||
}
|
||||
|
||||
test "phc format - empty input string" {
|
||||
const s = "";
|
||||
const v = deserialize(struct { alg_id: []const u8 }, s);
|
||||
try std.testing.expectError(Error.InvalidEncoding, v);
|
||||
}
|
||||
|
||||
test "phc format - hash without salt" {
|
||||
const s = "$scrypt";
|
||||
const v = deserialize(struct { alg_id: []const u8, hash: BinValue(16) }, s);
|
||||
try std.testing.expectError(Error.InvalidEncoding, v);
|
||||
}
|
||||
|
||||
test "phc format - calcSize" {
|
||||
const s = "$scrypt$v=1$ln=15,r=8,p=1$c2FsdHNhbHQ$dGVzdHBhc3M";
|
||||
const v = try deserialize(struct {
|
||||
alg_id: []const u8,
|
||||
alg_version: u16,
|
||||
ln: u6,
|
||||
r: u30,
|
||||
p: u30,
|
||||
salt: BinValue(8),
|
||||
hash: BinValue(8),
|
||||
}, s);
|
||||
try std.testing.expectEqual(calcSize(v), s.len);
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const utils = std.crypto.utils;
|
||||
const mem = std.mem;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
|
|
|
|||
657
lib/std/crypto/scrypt.zig
Normal file
657
lib/std/crypto/scrypt.zig
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
// https://tools.ietf.org/html/rfc7914
|
||||
// https://github.com/golang/crypto/blob/master/scrypt/scrypt.go
|
||||
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const fmt = std.fmt;
|
||||
const io = std.io;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
const pwhash = crypto.pwhash;
|
||||
|
||||
const phc_format = @import("phc_encoding.zig");
|
||||
|
||||
const HmacSha256 = crypto.auth.hmac.sha2.HmacSha256;
|
||||
const KdfError = pwhash.KdfError;
|
||||
const HasherError = pwhash.HasherError;
|
||||
const EncodingError = phc_format.Error;
|
||||
const Error = pwhash.Error;
|
||||
|
||||
const max_size = math.maxInt(usize);
|
||||
const max_int = max_size >> 1;
|
||||
const default_salt_len = 32;
|
||||
const default_hash_len = 32;
|
||||
const max_salt_len = 64;
|
||||
const max_hash_len = 64;
|
||||
|
||||
fn blockCopy(dst: []align(16) u32, src: []align(16) const u32, n: usize) void {
|
||||
mem.copy(u32, dst, src[0 .. n * 16]);
|
||||
}
|
||||
|
||||
fn blockXor(dst: []align(16) u32, src: []align(16) const u32, n: usize) void {
|
||||
for (src[0 .. n * 16]) |v, i| {
|
||||
dst[i] ^= v;
|
||||
}
|
||||
}
|
||||
|
||||
const QuarterRound = struct { a: usize, b: usize, c: usize, d: u6 };
|
||||
|
||||
fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound {
|
||||
return QuarterRound{ .a = a, .b = b, .c = c, .d = d };
|
||||
}
|
||||
|
||||
fn salsa8core(b: *align(16) [16]u32) void {
|
||||
const arx_steps = comptime [_]QuarterRound{
|
||||
Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18),
|
||||
Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18),
|
||||
Rp(14, 10, 6, 7), Rp(2, 14, 10, 9), Rp(6, 2, 14, 13), Rp(10, 6, 2, 18),
|
||||
Rp(3, 15, 11, 7), Rp(7, 3, 15, 9), Rp(11, 7, 3, 13), Rp(15, 11, 7, 18),
|
||||
Rp(1, 0, 3, 7), Rp(2, 1, 0, 9), Rp(3, 2, 1, 13), Rp(0, 3, 2, 18),
|
||||
Rp(6, 5, 4, 7), Rp(7, 6, 5, 9), Rp(4, 7, 6, 13), Rp(5, 4, 7, 18),
|
||||
Rp(11, 10, 9, 7), Rp(8, 11, 10, 9), Rp(9, 8, 11, 13), Rp(10, 9, 8, 18),
|
||||
Rp(12, 15, 14, 7), Rp(13, 12, 15, 9), Rp(14, 13, 12, 13), Rp(15, 14, 13, 18),
|
||||
};
|
||||
var x = b.*;
|
||||
var j: usize = 0;
|
||||
while (j < 8) : (j += 2) {
|
||||
inline for (arx_steps) |r| {
|
||||
x[r.a] ^= math.rotl(u32, x[r.b] +% x[r.c], r.d);
|
||||
}
|
||||
}
|
||||
j = 0;
|
||||
while (j < 16) : (j += 1) {
|
||||
b[j] +%= x[j];
|
||||
}
|
||||
}
|
||||
|
||||
fn salsaXor(tmp: *align(16) [16]u32, in: []align(16) const u32, out: []align(16) u32) void {
|
||||
blockXor(tmp, in, 1);
|
||||
salsa8core(tmp);
|
||||
blockCopy(out, tmp, 1);
|
||||
}
|
||||
|
||||
fn blockMix(tmp: *align(16) [16]u32, in: []align(16) const u32, out: []align(16) u32, r: u30) void {
|
||||
blockCopy(tmp, in[(2 * r - 1) * 16 ..], 1);
|
||||
var i: usize = 0;
|
||||
while (i < 2 * r) : (i += 2) {
|
||||
salsaXor(tmp, in[i * 16 ..], out[i * 8 ..]);
|
||||
salsaXor(tmp, in[i * 16 + 16 ..], out[i * 8 + r * 16 ..]);
|
||||
}
|
||||
}
|
||||
|
||||
fn integerify(b: []align(16) const u32, r: u30) u64 {
|
||||
const j = (2 * r - 1) * 16;
|
||||
return @as(u64, b[j]) | @as(u64, b[j + 1]) << 32;
|
||||
}
|
||||
|
||||
fn smix(b: []align(16) u8, r: u30, n: usize, v: []align(16) u32, xy: []align(16) u32) void {
|
||||
var x = xy[0 .. 32 * r];
|
||||
var y = xy[32 * r ..];
|
||||
|
||||
for (x) |*v1, j| {
|
||||
v1.* = mem.readIntSliceLittle(u32, b[4 * j ..]);
|
||||
}
|
||||
|
||||
var tmp: [16]u32 align(16) = undefined;
|
||||
var i: usize = 0;
|
||||
while (i < n) : (i += 2) {
|
||||
blockCopy(v[i * (32 * r) ..], x, 2 * r);
|
||||
blockMix(&tmp, x, y, r);
|
||||
|
||||
blockCopy(v[(i + 1) * (32 * r) ..], y, 2 * r);
|
||||
blockMix(&tmp, y, x, r);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (i < n) : (i += 2) {
|
||||
var j = @intCast(usize, integerify(x, r) & (n - 1));
|
||||
blockXor(x, v[j * (32 * r) ..], 2 * r);
|
||||
blockMix(&tmp, x, y, r);
|
||||
|
||||
j = @intCast(usize, integerify(y, r) & (n - 1));
|
||||
blockXor(y, v[j * (32 * r) ..], 2 * r);
|
||||
blockMix(&tmp, y, x, r);
|
||||
}
|
||||
|
||||
for (x) |v1, j| {
|
||||
mem.writeIntLittle(u32, b[4 * j ..][0..4], v1);
|
||||
}
|
||||
}
|
||||
|
||||
pub const Params = struct {
|
||||
const Self = @This();
|
||||
|
||||
ln: u6,
|
||||
r: u30,
|
||||
p: u30,
|
||||
|
||||
/// Baseline parameters for interactive logins
|
||||
pub const interactive = Self.fromLimits(524288, 16777216);
|
||||
|
||||
/// Baseline parameters for offline usage
|
||||
pub const sensitive = Self.fromLimits(33554432, 1073741824);
|
||||
|
||||
/// Create parameters from ops and mem limits
|
||||
pub fn fromLimits(ops_limit: u64, mem_limit: usize) Self {
|
||||
const ops = math.max(32768, ops_limit);
|
||||
const r: u30 = 8;
|
||||
if (ops < mem_limit / 32) {
|
||||
const max_n = ops / (r * 4);
|
||||
return Self{ .r = r, .p = 1, .ln = @intCast(u6, math.log2(max_n)) };
|
||||
} else {
|
||||
const max_n = mem_limit / (@intCast(usize, r) * 128);
|
||||
const ln = @intCast(u6, math.log2(max_n));
|
||||
const max_rp = math.min(0x3fffffff, (ops / 4) / (@as(u64, 1) << ln));
|
||||
return Self{ .r = r, .p = @intCast(u30, max_rp / @as(u64, r)), .ln = ln };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Apply scrypt to generate a key from a password.
|
||||
///
|
||||
/// scrypt is defined in RFC 7914.
|
||||
///
|
||||
/// allocator: *mem.Allocator.
|
||||
///
|
||||
/// derived_key: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length.
|
||||
/// May be uninitialized. All bytes will be overwritten.
|
||||
/// Maximum size is `derived_key.len / 32 == 0xffff_ffff`.
|
||||
///
|
||||
/// password: Arbitrary sequence of bytes of any length.
|
||||
///
|
||||
/// salt: Arbitrary sequence of bytes of any length.
|
||||
///
|
||||
/// params: Params.
|
||||
pub fn kdf(
|
||||
allocator: *mem.Allocator,
|
||||
derived_key: []u8,
|
||||
password: []const u8,
|
||||
salt: []const u8,
|
||||
params: Params,
|
||||
) KdfError!void {
|
||||
if (derived_key.len == 0 or derived_key.len / 32 > 0xffff_ffff) return KdfError.OutputTooLong;
|
||||
if (params.ln == 0 or params.r == 0 or params.p == 0) return KdfError.WeakParameters;
|
||||
|
||||
const n64 = @as(u64, 1) << params.ln;
|
||||
if (n64 > max_size) return KdfError.WeakParameters;
|
||||
const n = @intCast(usize, n64);
|
||||
if (@as(u64, params.r) * @as(u64, params.p) >= 1 << 30 or
|
||||
params.r > max_int / 128 / @as(u64, params.p) or
|
||||
params.r > max_int / 256 or
|
||||
n > max_int / 128 / @as(u64, params.r)) return KdfError.WeakParameters;
|
||||
|
||||
var xy = try allocator.alignedAlloc(u32, 16, 64 * params.r);
|
||||
defer allocator.free(xy);
|
||||
var v = try allocator.alignedAlloc(u32, 16, 32 * n * params.r);
|
||||
defer allocator.free(v);
|
||||
var dk = try allocator.alignedAlloc(u8, 16, params.p * 128 * params.r);
|
||||
defer allocator.free(dk);
|
||||
|
||||
try pwhash.pbkdf2(dk, password, salt, 1, HmacSha256);
|
||||
var i: u32 = 0;
|
||||
while (i < params.p) : (i += 1) {
|
||||
smix(dk[i * 128 * params.r ..], params.r, n, v, xy);
|
||||
}
|
||||
try pwhash.pbkdf2(derived_key, password, dk, 1, HmacSha256);
|
||||
}
|
||||
|
||||
const crypt_format = struct {
|
||||
/// String prefix for scrypt
|
||||
pub const prefix = "$7$";
|
||||
|
||||
/// Standard type for a set of scrypt parameters, with the salt and hash.
|
||||
pub fn HashResult(comptime crypt_max_hash_len: usize) type {
|
||||
return struct {
|
||||
ln: u6,
|
||||
r: u30,
|
||||
p: u30,
|
||||
salt: []const u8,
|
||||
hash: BinValue(crypt_max_hash_len),
|
||||
};
|
||||
}
|
||||
|
||||
const Codec = CustomB64Codec("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".*);
|
||||
|
||||
/// A wrapped binary value whose maximum size is `max_len`.
|
||||
///
|
||||
/// This type must be used whenever a binary value is encoded in a PHC-formatted string.
|
||||
/// This includes `salt`, `hash`, and any other binary parameters such as keys.
|
||||
///
|
||||
/// Once initialized, the actual value can be read with the `constSlice()` function.
|
||||
pub fn BinValue(comptime max_len: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
const capacity = max_len;
|
||||
const max_encoded_length = Codec.encodedLen(max_len);
|
||||
|
||||
buf: [max_len]u8 = undefined,
|
||||
len: usize = 0,
|
||||
|
||||
/// Wrap an existing byte slice
|
||||
pub fn fromSlice(slice: []const u8) EncodingError!Self {
|
||||
if (slice.len > capacity) return EncodingError.NoSpaceLeft;
|
||||
var bin_value: Self = undefined;
|
||||
mem.copy(u8, &bin_value.buf, slice);
|
||||
bin_value.len = slice.len;
|
||||
return bin_value;
|
||||
}
|
||||
|
||||
/// Return the slice containing the actual value.
|
||||
pub fn constSlice(self: Self) []const u8 {
|
||||
return self.buf[0..self.len];
|
||||
}
|
||||
|
||||
fn fromB64(self: *Self, str: []const u8) !void {
|
||||
const len = Codec.decodedLen(str.len);
|
||||
if (len > self.buf.len) return EncodingError.NoSpaceLeft;
|
||||
try Codec.decode(self.buf[0..len], str);
|
||||
self.len = len;
|
||||
}
|
||||
|
||||
fn toB64(self: Self, buf: []u8) ![]const u8 {
|
||||
const value = self.constSlice();
|
||||
const len = Codec.encodedLen(value.len);
|
||||
if (len > buf.len) return EncodingError.NoSpaceLeft;
|
||||
var encoded = buf[0..len];
|
||||
Codec.encode(encoded, value);
|
||||
return encoded;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Expand binary data into a salt for the modular crypt format.
|
||||
pub fn saltFromBin(comptime len: usize, salt: [len]u8) [Codec.encodedLen(len)]u8 {
|
||||
var buf: [Codec.encodedLen(len)]u8 = undefined;
|
||||
Codec.encode(&buf, &salt);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/// Deserialize a string into a structure `T` (matching `HashResult`).
|
||||
pub fn deserialize(comptime T: type, str: []const u8) EncodingError!T {
|
||||
var out: T = undefined;
|
||||
|
||||
if (str.len < 16) return EncodingError.InvalidEncoding;
|
||||
if (!mem.eql(u8, prefix, str[0..3])) return EncodingError.InvalidEncoding;
|
||||
out.ln = try Codec.intDecode(u6, str[3..4]);
|
||||
out.r = try Codec.intDecode(u30, str[4..9]);
|
||||
out.p = try Codec.intDecode(u30, str[9..14]);
|
||||
|
||||
var it = mem.split(u8, str[14..], "$");
|
||||
|
||||
const salt = it.next() orelse return EncodingError.InvalidEncoding;
|
||||
if (@hasField(T, "salt")) out.salt = salt;
|
||||
|
||||
const hash_str = it.next() orelse return EncodingError.InvalidEncoding;
|
||||
if (@hasField(T, "hash")) try out.hash.fromB64(hash_str);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Serialize parameters into a string in modular crypt format.
|
||||
pub fn serialize(params: anytype, str: []u8) EncodingError![]const u8 {
|
||||
var buf = io.fixedBufferStream(str);
|
||||
try serializeTo(params, buf.writer());
|
||||
return buf.getWritten();
|
||||
}
|
||||
|
||||
/// Compute the number of bytes required to serialize `params`
|
||||
pub fn calcSize(params: anytype) usize {
|
||||
var buf = io.countingWriter(io.null_writer);
|
||||
serializeTo(params, buf.writer()) catch unreachable;
|
||||
return @intCast(usize, buf.bytes_written);
|
||||
}
|
||||
|
||||
fn serializeTo(params: anytype, out: anytype) !void {
|
||||
var header: [14]u8 = undefined;
|
||||
mem.copy(u8, header[0..3], prefix);
|
||||
Codec.intEncode(header[3..4], params.ln);
|
||||
Codec.intEncode(header[4..9], params.r);
|
||||
Codec.intEncode(header[9..14], params.p);
|
||||
try out.writeAll(&header);
|
||||
try out.writeAll(params.salt);
|
||||
try out.writeAll("$");
|
||||
var buf: [@TypeOf(params.hash).max_encoded_length]u8 = undefined;
|
||||
const hash_str = try params.hash.toB64(&buf);
|
||||
try out.writeAll(hash_str);
|
||||
}
|
||||
|
||||
/// Custom codec that maps 6 bits into 8 like regular Base64, but uses its own alphabet,
|
||||
/// encodes bits in little-endian, and can also encode integers.
|
||||
fn CustomB64Codec(comptime map: [64]u8) type {
|
||||
return struct {
|
||||
const map64 = map;
|
||||
|
||||
fn encodedLen(len: usize) usize {
|
||||
return (len * 4 + 2) / 3;
|
||||
}
|
||||
|
||||
fn decodedLen(len: usize) usize {
|
||||
return len / 4 * 3 + (len % 4) * 3 / 4;
|
||||
}
|
||||
|
||||
fn intEncode(dst: []u8, src: anytype) void {
|
||||
var n = src;
|
||||
for (dst) |*x| {
|
||||
x.* = map64[@truncate(u6, n)];
|
||||
n = math.shr(@TypeOf(src), n, 6);
|
||||
}
|
||||
}
|
||||
|
||||
fn intDecode(comptime T: type, src: *const [(meta.bitCount(T) + 5) / 6]u8) !T {
|
||||
var v: T = 0;
|
||||
for (src) |x, i| {
|
||||
const vi = mem.indexOfScalar(u8, &map64, x) orelse return EncodingError.InvalidEncoding;
|
||||
v |= @intCast(T, vi) << @intCast(math.Log2Int(T), i * 6);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
fn decode(dst: []u8, src: []const u8) !void {
|
||||
std.debug.assert(dst.len == decodedLen(src.len));
|
||||
var i: usize = 0;
|
||||
while (i < src.len / 4) : (i += 1) {
|
||||
mem.writeIntSliceLittle(u24, dst[i * 3 ..], try intDecode(u24, src[i * 4 ..][0..4]));
|
||||
}
|
||||
const leftover = src[i * 4 ..];
|
||||
var v: u24 = 0;
|
||||
for (leftover) |_, j| {
|
||||
v |= @as(u24, try intDecode(u6, leftover[j..][0..1])) << @intCast(u5, j * 6);
|
||||
}
|
||||
for (dst[i * 3 ..]) |*x, j| {
|
||||
x.* = @truncate(u8, v >> @intCast(u5, j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(dst: []u8, src: []const u8) void {
|
||||
std.debug.assert(dst.len == encodedLen(src.len));
|
||||
var i: usize = 0;
|
||||
while (i < src.len / 3) : (i += 1) {
|
||||
intEncode(dst[i * 4 ..][0..4], mem.readIntSliceLittle(u24, src[i * 3 ..]));
|
||||
}
|
||||
const leftover = src[i * 3 ..];
|
||||
var v: u24 = 0;
|
||||
for (leftover) |x, j| {
|
||||
v |= @as(u24, x) << @intCast(u5, j * 8);
|
||||
}
|
||||
intEncode(dst[i * 4 ..], v);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash and verify passwords using the PHC format.
|
||||
const PhcFormatHasher = struct {
|
||||
const alg_id = "scrypt";
|
||||
const BinValue = phc_format.BinValue;
|
||||
|
||||
const HashResult = struct {
|
||||
alg_id: []const u8,
|
||||
ln: u6,
|
||||
r: u30,
|
||||
p: u30,
|
||||
salt: BinValue(max_salt_len),
|
||||
hash: BinValue(max_hash_len),
|
||||
};
|
||||
|
||||
/// Return a non-deterministic hash of the password encoded as a PHC-format string
|
||||
pub fn create(
|
||||
allocator: *mem.Allocator,
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
) HasherError![]const u8 {
|
||||
var salt: [default_salt_len]u8 = undefined;
|
||||
crypto.random.bytes(&salt);
|
||||
|
||||
var hash: [default_hash_len]u8 = undefined;
|
||||
try kdf(allocator, &hash, password, &salt, params);
|
||||
|
||||
return phc_format.serialize(HashResult{
|
||||
.alg_id = alg_id,
|
||||
.ln = params.ln,
|
||||
.r = params.r,
|
||||
.p = params.p,
|
||||
.salt = try BinValue(max_salt_len).fromSlice(&salt),
|
||||
.hash = try BinValue(max_hash_len).fromSlice(&hash),
|
||||
}, buf);
|
||||
}
|
||||
|
||||
/// Verify a password against a PHC-format encoded string
|
||||
pub fn verify(
|
||||
allocator: *mem.Allocator,
|
||||
str: []const u8,
|
||||
password: []const u8,
|
||||
) HasherError!void {
|
||||
const hash_result = try phc_format.deserialize(HashResult, str);
|
||||
if (!mem.eql(u8, hash_result.alg_id, alg_id)) return HasherError.PasswordVerificationFailed;
|
||||
const params = Params{ .ln = hash_result.ln, .r = hash_result.r, .p = hash_result.p };
|
||||
const expected_hash = hash_result.hash.constSlice();
|
||||
var hash_buf: [max_hash_len]u8 = undefined;
|
||||
if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding;
|
||||
var hash = hash_buf[0..expected_hash.len];
|
||||
try kdf(allocator, hash, password, hash_result.salt.constSlice(), params);
|
||||
if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed;
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash and verify passwords using the modular crypt format.
|
||||
const CryptFormatHasher = struct {
|
||||
const BinValue = crypt_format.BinValue;
|
||||
const HashResult = crypt_format.HashResult(max_hash_len);
|
||||
|
||||
/// Length of a string returned by the create() function
|
||||
pub const pwhash_str_length: usize = 101;
|
||||
|
||||
/// Return a non-deterministic hash of the password encoded into the modular crypt format
|
||||
pub fn create(
|
||||
allocator: *mem.Allocator,
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
) HasherError![]const u8 {
|
||||
var salt_bin: [default_salt_len]u8 = undefined;
|
||||
crypto.random.bytes(&salt_bin);
|
||||
const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);
|
||||
|
||||
var hash: [default_hash_len]u8 = undefined;
|
||||
try kdf(allocator, &hash, password, &salt, params);
|
||||
|
||||
return crypt_format.serialize(HashResult{
|
||||
.ln = params.ln,
|
||||
.r = params.r,
|
||||
.p = params.p,
|
||||
.salt = &salt,
|
||||
.hash = try BinValue(max_hash_len).fromSlice(&hash),
|
||||
}, buf);
|
||||
}
|
||||
|
||||
/// Verify a password against a string in modular crypt format
|
||||
pub fn verify(
|
||||
allocator: *mem.Allocator,
|
||||
str: []const u8,
|
||||
password: []const u8,
|
||||
) HasherError!void {
|
||||
const hash_result = try crypt_format.deserialize(HashResult, str);
|
||||
const params = Params{ .ln = hash_result.ln, .r = hash_result.r, .p = hash_result.p };
|
||||
const expected_hash = hash_result.hash.constSlice();
|
||||
var hash_buf: [max_hash_len]u8 = undefined;
|
||||
if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding;
|
||||
var hash = hash_buf[0..expected_hash.len];
|
||||
try kdf(allocator, hash, password, hash_result.salt, params);
|
||||
if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed;
|
||||
}
|
||||
};
|
||||
|
||||
/// Options for hashing a password.
|
||||
pub const HashOptions = struct {
|
||||
allocator: ?*mem.Allocator,
|
||||
params: Params,
|
||||
encoding: pwhash.Encoding,
|
||||
};
|
||||
|
||||
/// Compute a hash of a password using the scrypt key derivation function.
|
||||
/// The function returns a string that includes all the parameters required for verification.
|
||||
pub fn strHash(
|
||||
password: []const u8,
|
||||
options: HashOptions,
|
||||
out: []u8,
|
||||
) Error![]const u8 {
|
||||
const allocator = options.allocator orelse return Error.AllocatorRequired;
|
||||
switch (options.encoding) {
|
||||
.phc => return PhcFormatHasher.create(allocator, password, options.params, out),
|
||||
.crypt => return CryptFormatHasher.create(allocator, password, options.params, out),
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for hash verification.
|
||||
pub const VerifyOptions = struct {
|
||||
allocator: ?*mem.Allocator,
|
||||
};
|
||||
|
||||
/// Verify that a previously computed hash is valid for a given password.
|
||||
pub fn strVerify(
|
||||
str: []const u8,
|
||||
password: []const u8,
|
||||
options: VerifyOptions,
|
||||
) Error!void {
|
||||
const allocator = options.allocator orelse return Error.AllocatorRequired;
|
||||
if (mem.startsWith(u8, str, crypt_format.prefix)) {
|
||||
return CryptFormatHasher.verify(allocator, str, password);
|
||||
} else {
|
||||
return PhcFormatHasher.verify(allocator, str, password);
|
||||
}
|
||||
}
|
||||
|
||||
test "scrypt kdf" {
|
||||
const password = "testpass";
|
||||
const salt = "saltsalt";
|
||||
|
||||
var dk: [32]u8 = undefined;
|
||||
try kdf(std.testing.allocator, &dk, password, salt, .{ .ln = 15, .r = 8, .p = 1 });
|
||||
|
||||
const hex = "1e0f97c3f6609024022fbe698da29c2fe53ef1087a8e396dc6d5d2a041e886de";
|
||||
var bytes: [hex.len / 2]u8 = undefined;
|
||||
_ = try fmt.hexToBytes(&bytes, hex);
|
||||
|
||||
try std.testing.expectEqualSlices(u8, &bytes, &dk);
|
||||
}
|
||||
|
||||
test "scrypt kdf rfc 1" {
|
||||
const password = "";
|
||||
const salt = "";
|
||||
|
||||
var dk: [64]u8 = undefined;
|
||||
try kdf(std.testing.allocator, &dk, password, salt, .{ .ln = 4, .r = 1, .p = 1 });
|
||||
|
||||
const hex = "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906";
|
||||
var bytes: [hex.len / 2]u8 = undefined;
|
||||
_ = try fmt.hexToBytes(&bytes, hex);
|
||||
|
||||
try std.testing.expectEqualSlices(u8, &bytes, &dk);
|
||||
}
|
||||
|
||||
test "scrypt kdf rfc 2" {
|
||||
const password = "password";
|
||||
const salt = "NaCl";
|
||||
|
||||
var dk: [64]u8 = undefined;
|
||||
try kdf(std.testing.allocator, &dk, password, salt, .{ .ln = 10, .r = 8, .p = 16 });
|
||||
|
||||
const hex = "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640";
|
||||
var bytes: [hex.len / 2]u8 = undefined;
|
||||
_ = try fmt.hexToBytes(&bytes, hex);
|
||||
|
||||
try std.testing.expectEqualSlices(u8, &bytes, &dk);
|
||||
}
|
||||
|
||||
test "scrypt kdf rfc 3" {
|
||||
const password = "pleaseletmein";
|
||||
const salt = "SodiumChloride";
|
||||
|
||||
var dk: [64]u8 = undefined;
|
||||
try kdf(std.testing.allocator, &dk, password, salt, .{ .ln = 14, .r = 8, .p = 1 });
|
||||
|
||||
const hex = "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887";
|
||||
var bytes: [hex.len / 2]u8 = undefined;
|
||||
_ = try fmt.hexToBytes(&bytes, hex);
|
||||
|
||||
try std.testing.expectEqualSlices(u8, &bytes, &dk);
|
||||
}
|
||||
|
||||
test "scrypt kdf rfc 4" {
|
||||
// skip slow test
|
||||
if (true) {
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
const password = "pleaseletmein";
|
||||
const salt = "SodiumChloride";
|
||||
|
||||
var dk: [64]u8 = undefined;
|
||||
try kdf(std.testing.allocator, &dk, password, salt, .{ .ln = 20, .r = 8, .p = 1 });
|
||||
|
||||
const hex = "2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4";
|
||||
var bytes: [hex.len / 2]u8 = undefined;
|
||||
_ = try fmt.hexToBytes(&bytes, hex);
|
||||
|
||||
try std.testing.expectEqualSlices(u8, &bytes, &dk);
|
||||
}
|
||||
|
||||
test "scrypt password hashing (crypt format)" {
|
||||
const str = "$7$A6....1....TrXs5Zk6s8sWHpQgWDIXTR8kUU3s6Jc3s.DtdS8M2i4$a4ik5hGDN7foMuHOW.cp.CtX01UyCeO0.JAG.AHPpx5";
|
||||
const password = "Y0!?iQa9M%5ekffW(`";
|
||||
try CryptFormatHasher.verify(std.testing.allocator, str, password);
|
||||
|
||||
const params = Params.interactive;
|
||||
var buf: [CryptFormatHasher.pwhash_str_length]u8 = undefined;
|
||||
const str2 = try CryptFormatHasher.create(std.testing.allocator, password, params, &buf);
|
||||
try CryptFormatHasher.verify(std.testing.allocator, str2, password);
|
||||
}
|
||||
|
||||
test "scrypt strHash and strVerify" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const password = "testpass";
|
||||
const verify_options = VerifyOptions{ .allocator = alloc };
|
||||
var buf: [128]u8 = undefined;
|
||||
|
||||
const s = try strHash(
|
||||
password,
|
||||
HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .crypt },
|
||||
&buf,
|
||||
);
|
||||
try strVerify(s, password, verify_options);
|
||||
|
||||
const s1 = try strHash(
|
||||
password,
|
||||
HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .phc },
|
||||
&buf,
|
||||
);
|
||||
try strVerify(s1, password, verify_options);
|
||||
}
|
||||
|
||||
test "scrypt unix-scrypt" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
// https://gitlab.com/jas/scrypt-unix-crypt/blob/master/unix-scrypt.txt
|
||||
{
|
||||
const str = "$7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D";
|
||||
const password = "pleaseletmein";
|
||||
try strVerify(str, password, .{ .allocator = alloc });
|
||||
}
|
||||
// one of the libsodium test vectors
|
||||
{
|
||||
const str = "$7$B6....1....75gBMAGwfFWZqBdyF3WdTQnWdUsuTiWjG1fF9c1jiSD$tc8RoB3.Em3/zNgMLWo2u00oGIoTyJv4fl3Fl8Tix72";
|
||||
const password = "^T5H$JYt39n%K*j:W]!1s?vg!:jGi]Ax?..l7[p0v:1jHTpla9;]bUN;?bWyCbtqg nrDFal+Jxl3,2`#^tFSu%v_+7iYse8-cCkNf!tD=KrW)";
|
||||
try strVerify(str, password, .{ .allocator = alloc });
|
||||
}
|
||||
}
|
||||
|
||||
test "scrypt crypt format" {
|
||||
const str = "$7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D";
|
||||
const params = try crypt_format.deserialize(crypt_format.HashResult(32), str);
|
||||
var buf: [str.len]u8 = undefined;
|
||||
const s1 = try crypt_format.serialize(params, &buf);
|
||||
try std.testing.expectEqualStrings(s1, str);
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.
|
||||
const std = @import("../std.zig");
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue