Merge remote-tracking branch 'origin/master' into llvm13

Conflicts:
   lib/libcxx/include/__config

d57c0cc3bfeff9af297279759ec2b631e6d95140 added support for DragonFlyBSD
to libc++ by updating some ifdefs. This needed to be synced with llvm13.
This commit is contained in:
Andrew Kelley 2021-08-28 12:41:24 -07:00
commit 6aeab0f323
599 changed files with 8694 additions and 8075 deletions

View File

@ -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"

View File

@ -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,

View File

@ -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);
}
}
},

View File

@ -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 &lt;- 'errdefer' end_of_word
KEYWORD_error &lt;- 'error' end_of_word
KEYWORD_export &lt;- 'export' end_of_word
KEYWORD_extern &lt;- 'extern' end_of_word
KEYWORD_false &lt;- 'false' end_of_word
KEYWORD_fn &lt;- 'fn' end_of_word
KEYWORD_for &lt;- 'for' end_of_word
KEYWORD_if &lt;- 'if' end_of_word
@ -11916,7 +11940,6 @@ KEYWORD_inline &lt;- 'inline' end_of_word
KEYWORD_noalias &lt;- 'noalias' end_of_word
KEYWORD_nosuspend &lt;- 'nosuspend' end_of_word
KEYWORD_noinline &lt;- 'noinline' end_of_word
KEYWORD_null &lt;- 'null' end_of_word
KEYWORD_opaque &lt;- 'opaque' end_of_word
KEYWORD_or &lt;- 'or' end_of_word
KEYWORD_orelse &lt;- 'orelse' end_of_word
@ -11930,9 +11953,7 @@ KEYWORD_suspend &lt;- 'suspend' end_of_word
KEYWORD_switch &lt;- 'switch' end_of_word
KEYWORD_test &lt;- 'test' end_of_word
KEYWORD_threadlocal &lt;- 'threadlocal' end_of_word
KEYWORD_true &lt;- 'true' end_of_word
KEYWORD_try &lt;- 'try' end_of_word
KEYWORD_undefined &lt;- 'undefined' end_of_word
KEYWORD_union &lt;- 'union' end_of_word
KEYWORD_unreachable &lt;- 'unreachable' end_of_word
KEYWORD_usingnamespace &lt;- 'usingnamespace' end_of_word
@ -11945,13 +11966,13 @@ keyword &lt;- 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
View 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

View File

@ -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__) || \

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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`.

View File

@ -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

View File

@ -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;

View File

@ -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).
//!

View File

@ -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,
}
},

View File

@ -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,
}

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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);
}
};

View File

@ -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.

View File

@ -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,
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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);
}

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View 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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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 {};

View File

@ -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;

View File

@ -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;

View File

@ -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,
};

View File

@ -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;

View File

@ -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,
};

View File

@ -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;

View File

@ -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),
};

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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)
//

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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;
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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,6 +140,48 @@ const State = 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";
@ -182,20 +225,24 @@ 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;
@ -203,45 +250,128 @@ const Codec = struct {
}
};
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,
);
}

View File

@ -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 });
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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");

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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;

View 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);
}

View File

@ -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;

View File

@ -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
View 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);
}

View File

@ -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