| author | |
| committer | |
| log | b897e98d30b7e471cdabf6b8f0baab44998265cd |
| tree | 642a2e9b0eb824c9cae69bfd29a72c11eb4b7101 |
| parent | eb3726c502e92ec4a3689732a76479c6d561cff5 |
| parent | ee9ab15679ee04a40bdc582779faf43fb10836ce |
61 files changed, 3197 insertions(+), 344 deletions(-)
CMakeLists.txt+5| ... | ... | @@ -364,6 +364,11 @@ set(ZIG_STD_FILES |
| 364 | 364 | "c/index.zig" |
| 365 | 365 | "c/linux.zig" |
| 366 | 366 | "c/windows.zig" |
| 367 | "crypto/index.zig" | |
| 368 | "crypto/md5.zig" | |
| 369 | "crypto/sha1.zig" | |
| 370 | "crypto/sha2.zig" | |
| 371 | "crypto/blake2.zig" | |
| 367 | 372 | "cstr.zig" |
| 368 | 373 | "debug/failing_allocator.zig" |
| 369 | 374 | "debug/index.zig" |
README.md+5-2| ... | ... | @@ -54,7 +54,7 @@ that counts as "freestanding" for the purposes of this table. |
| 54 | 54 | |
| 55 | 55 | | | freestanding | linux | macosx | windows | other | |
| 56 | 56 | |-------------|--------------|---------|---------|---------|---------| |
| 57 | |i386 | OK | planned | OK | OK | planned | | |
| 57 | |i386 | OK | planned | OK | planned | planned | | |
| 58 | 58 | |x86_64 | OK | OK | OK | OK | planned | |
| 59 | 59 | |arm | OK | planned | planned | N/A | planned | |
| 60 | 60 | |aarch64 | OK | planned | planned | planned | planned | |
| ... | ... | @@ -125,17 +125,20 @@ libc. Create demo games using Zig. |
| 125 | 125 | |
| 126 | 126 | ##### POSIX |
| 127 | 127 | |
| 128 | * gcc >= 5.0.0 or clang >= 3.6.0 | |
| 129 | 128 | * cmake >= 2.8.5 |
| 129 | * gcc >= 5.0.0 or clang >= 3.6.0 | |
| 130 | 130 | * LLVM, Clang, LLD libraries == 6.x, compiled with the same gcc or clang version above |
| 131 | 131 | |
| 132 | 132 | ##### Windows |
| 133 | 133 | |
| 134 | * cmake >= 2.8.5 | |
| 134 | 135 | * Microsoft Visual Studio 2015 |
| 135 | 136 | * LLVM, Clang, LLD libraries == 6.x, compiled with the same MSVC version above |
| 136 | 137 | |
| 137 | 138 | #### Instructions |
| 138 | 139 | |
| 140 | ##### POSIX | |
| 141 | ||
| 139 | 142 | If you have gcc or clang installed, you can find out what `ZIG_LIBC_LIB_DIR`, |
| 140 | 143 | `ZIG_LIBC_STATIC_LIB_DIR`, and `ZIG_LIBC_INCLUDE_DIR` should be set to |
| 141 | 144 | (example below). |
build.zig+6-6| ... | ... | @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; |
| 10 | 10 | const Buffer = std.Buffer; |
| 11 | 11 | const io = std.io; |
| 12 | 12 | |
| 13 | pub fn build(b: &Builder) { | |
| 13 | pub fn build(b: &Builder) -> %void { | |
| 14 | 14 | const mode = b.standardReleaseOptions(); |
| 15 | 15 | |
| 16 | 16 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); |
| ... | ... | @@ -36,7 +36,7 @@ pub fn build(b: &Builder) { |
| 36 | 36 | const test_step = b.step("test", "Run all the tests"); |
| 37 | 37 | |
| 38 | 38 | // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library |
| 39 | const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); | |
| 39 | const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); | |
| 40 | 40 | var index: usize = 0; |
| 41 | 41 | const cmake_binary_dir = nextValue(&index, build_info); |
| 42 | 42 | const cxx_compiler = nextValue(&index, build_info); |
| ... | ... | @@ -68,7 +68,7 @@ pub fn build(b: &Builder) { |
| 68 | 68 | dependOnLib(exe, llvm); |
| 69 | 69 | |
| 70 | 70 | if (exe.target.getOs() == builtin.Os.linux) { |
| 71 | const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); | |
| 71 | const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); | |
| 72 | 72 | const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); |
| 73 | 73 | exe.addObjectFile(libstdcxx_path); |
| 74 | 74 | |
| ... | ... | @@ -155,9 +155,9 @@ const LibraryDep = struct { |
| 155 | 155 | }; |
| 156 | 156 | |
| 157 | 157 | fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep { |
| 158 | const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); | |
| 159 | const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"}); | |
| 160 | const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"}); | |
| 158 | const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); | |
| 159 | const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); | |
| 160 | const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); | |
| 161 | 161 | |
| 162 | 162 | var result = LibraryDep { |
| 163 | 163 | .libs = ArrayList([]const u8).init(b.allocator), |
doc/langref.html.in+8| ... | ... | @@ -142,6 +142,7 @@ |
| 142 | 142 | <li><a href="#builtin-TagType">@TagType</a></li> |
| 143 | 143 | <li><a href="#builtin-EnumTagType">@EnumTagType</a></li> |
| 144 | 144 | <li><a href="#builtin-errorName">@errorName</a></li> |
| 145 | <li><a href="#builtin-errorReturnTrace">@errorReturnTrace</a></li> | |
| 145 | 146 | <li><a href="#builtin-fence">@fence</a></li> |
| 146 | 147 | <li><a href="#builtin-fieldParentPtr">@fieldParentPtr</a></li> |
| 147 | 148 | <li><a href="#builtin-frameAddress">@frameAddress</a></li> |
| ... | ... | @@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement |
| 4412 | 4413 | or all calls have a compile-time known value for <code>err</code>, then no |
| 4413 | 4414 | error name table will be generated. |
| 4414 | 4415 | </p> |
| 4416 | <h3 id="builtin-errorReturnTrace">@errorReturnTrace</h3> | |
| 4417 | <pre><code class="zig">@errorReturnTrace() -&gt; ?&amp;builtin.StackTrace</code></pre> | |
| 4418 | <p> | |
| 4419 | If the binary is built with error return tracing, and this function is invoked in a | |
| 4420 | function that calls a function with an error or error union return type, returns a | |
| 4421 | stack trace object. Otherwise returns `null`. | |
| 4422 | </p> | |
| 4415 | 4423 | <h3 id="builtin-fence">@fence</h3> |
| 4416 | 4424 | <pre><code class="zig">@fence(order: AtomicOrder)</code></pre> |
| 4417 | 4425 | <p> |
example/mix_o_files/build.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | |
| 3 | pub fn build(b: &Builder) { | |
| 3 | pub fn build(b: &Builder) -> %void { | |
| 4 | 4 | const obj = b.addObject("base64", "base64.zig"); |
| 5 | 5 | |
| 6 | 6 | const exe = b.addCExecutable("test"); |
example/shared_library/build.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | |
| 3 | pub fn build(b: &Builder) { | |
| 3 | pub fn build(b: &Builder) -> %void { | |
| 4 | 4 | const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); |
| 5 | 5 | |
| 6 | 6 | const exe = b.addCExecutable("test"); |
src-self-hosted/parser.zig-6| ... | ... | @@ -1146,12 +1146,6 @@ fn testCanonical(source: []const u8) { |
| 1146 | 1146 | } |
| 1147 | 1147 | |
| 1148 | 1148 | test "zig fmt" { |
| 1149 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 1150 | // TODO get this test passing | |
| 1151 | // https://github.com/zig-lang/zig/issues/537 | |
| 1152 | return; | |
| 1153 | } | |
| 1154 | ||
| 1155 | 1149 | testCanonical( |
| 1156 | 1150 | \\extern fn puts(s: &const u8) -> c_int; |
| 1157 | 1151 | \\ |
src/all_types.hpp+13| ... | ... | @@ -1205,6 +1205,7 @@ struct FnTableEntry { |
| 1205 | 1205 | uint32_t alignstack_value; |
| 1206 | 1206 | |
| 1207 | 1207 | ZigList<FnExport> export_list; |
| 1208 | bool calls_errorable_function; | |
| 1208 | 1209 | }; |
| 1209 | 1210 | |
| 1210 | 1211 | uint32_t fn_table_entry_hash(FnTableEntry*); |
| ... | ... | @@ -1273,6 +1274,7 @@ enum BuiltinFnId { |
| 1273 | 1274 | BuiltinFnIdSetAlignStack, |
| 1274 | 1275 | BuiltinFnIdArgType, |
| 1275 | 1276 | BuiltinFnIdExport, |
| 1277 | BuiltinFnIdErrorReturnTrace, | |
| 1276 | 1278 | }; |
| 1277 | 1279 | |
| 1278 | 1280 | struct BuiltinFnEntry { |
| ... | ... | @@ -1498,6 +1500,7 @@ struct CodeGen { |
| 1498 | 1500 | Buf triple_str; |
| 1499 | 1501 | BuildMode build_mode; |
| 1500 | 1502 | bool is_test_build; |
| 1503 | bool have_err_ret_tracing; | |
| 1501 | 1504 | uint32_t target_os_index; |
| 1502 | 1505 | uint32_t target_arch_index; |
| 1503 | 1506 | uint32_t target_environ_index; |
| ... | ... | @@ -1530,6 +1533,7 @@ struct CodeGen { |
| 1530 | 1533 | FnTableEntry *panic_fn; |
| 1531 | 1534 | LLVMValueRef cur_ret_ptr; |
| 1532 | 1535 | LLVMValueRef cur_fn_val; |
| 1536 | LLVMValueRef cur_err_ret_trace_val; | |
| 1533 | 1537 | bool c_want_stdint; |
| 1534 | 1538 | bool c_want_stdbool; |
| 1535 | 1539 | AstNode *root_export_decl; |
| ... | ... | @@ -1572,6 +1576,8 @@ struct CodeGen { |
| 1572 | 1576 | size_t largest_err_name_len; |
| 1573 | 1577 | LLVMValueRef safety_crash_err_fn; |
| 1574 | 1578 | |
| 1579 | LLVMValueRef return_err_fn; | |
| 1580 | ||
| 1575 | 1581 | IrInstruction *invalid_instruction; |
| 1576 | 1582 | ConstExprValue const_void_val; |
| 1577 | 1583 | |
| ... | ... | @@ -1595,6 +1601,8 @@ struct CodeGen { |
| 1595 | 1601 | ZigList<AstNode *> tld_ref_source_node_stack; |
| 1596 | 1602 | |
| 1597 | 1603 | TypeTableEntry *align_amt_type; |
| 1604 | TypeTableEntry *stack_trace_type; | |
| 1605 | TypeTableEntry *ptr_to_stack_trace_type; | |
| 1598 | 1606 | }; |
| 1599 | 1607 | |
| 1600 | 1608 | enum VarLinkage { |
| ... | ... | @@ -1896,6 +1904,7 @@ enum IrInstructionId { |
| 1896 | 1904 | IrInstructionIdSetAlignStack, |
| 1897 | 1905 | IrInstructionIdArgType, |
| 1898 | 1906 | IrInstructionIdExport, |
| 1907 | IrInstructionIdErrorReturnTrace, | |
| 1899 | 1908 | }; |
| 1900 | 1909 | |
| 1901 | 1910 | struct IrInstruction { |
| ... | ... | @@ -2717,6 +2726,10 @@ struct IrInstructionExport { |
| 2717 | 2726 | IrInstruction *target; |
| 2718 | 2727 | }; |
| 2719 | 2728 | |
| 2729 | struct IrInstructionErrorReturnTrace { | |
| 2730 | IrInstruction base; | |
| 2731 | }; | |
| 2732 | ||
| 2720 | 2733 | static const size_t slice_ptr_index = 0; |
| 2721 | 2734 | static const size_t slice_len_index = 1; |
| 2722 | 2735 |
src/analyze.cpp+48-12| ... | ... | @@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) { |
| 869 | 869 | zig_unreachable(); |
| 870 | 870 | } |
| 871 | 871 | |
| 872 | TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { | |
| 873 | if (g->stack_trace_type == nullptr) { | |
| 874 | ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); | |
| 875 | assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType); | |
| 876 | g->stack_trace_type = stack_trace_type_val->data.x_type; | |
| 877 | g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false); | |
| 878 | } | |
| 879 | return g->ptr_to_stack_trace_type; | |
| 880 | } | |
| 881 | ||
| 872 | 882 | TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 873 | 883 | auto table_entry = g->fn_type_table.maybe_get(fn_type_id); |
| 874 | 884 | if (table_entry) { |
| ... | ... | @@ -915,10 +925,16 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 915 | 925 | if (!skip_debug_info) { |
| 916 | 926 | bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && |
| 917 | 927 | handle_is_ptr(fn_type_id->return_type); |
| 928 | bool prefix_arg_error_return_trace = g->have_err_ret_tracing && | |
| 929 | (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || | |
| 930 | fn_type_id->return_type->id == TypeTableEntryIdPureError); | |
| 918 | 931 | // +1 for maybe making the first argument the return value |
| 919 | LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count); | |
| 920 | // +1 because 0 is the return type and +1 for maybe making first arg ret val | |
| 921 | ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(2 + fn_type_id->param_count); | |
| 932 | // +1 for maybe last argument the error return trace | |
| 933 | LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(2 + fn_type_id->param_count); | |
| 934 | // +1 because 0 is the return type and | |
| 935 | // +1 for maybe making first arg ret val and | |
| 936 | // +1 for maybe last argument the error return trace | |
| 937 | ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(3 + fn_type_id->param_count); | |
| 922 | 938 | param_di_types[0] = fn_type_id->return_type->di_type; |
| 923 | 939 | size_t gen_param_index = 0; |
| 924 | 940 | TypeTableEntry *gen_return_type; |
| ... | ... | @@ -936,6 +952,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 936 | 952 | } |
| 937 | 953 | fn_type->data.fn.gen_return_type = gen_return_type; |
| 938 | 954 | |
| 955 | if (prefix_arg_error_return_trace) { | |
| 956 | TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g); | |
| 957 | gen_param_types[gen_param_index] = gen_type->type_ref; | |
| 958 | gen_param_index += 1; | |
| 959 | // after the gen_param_index += 1 because 0 is the return type | |
| 960 | param_di_types[gen_param_index] = gen_type->di_type; | |
| 961 | } | |
| 962 | ||
| 939 | 963 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count); |
| 940 | 964 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { |
| 941 | 965 | FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i]; |
| ... | ... | @@ -1168,6 +1192,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1168 | 1192 | } |
| 1169 | 1193 | |
| 1170 | 1194 | TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); |
| 1195 | if (type_is_invalid(type_entry)) { | |
| 1196 | return g->builtin_types.entry_invalid; | |
| 1197 | } | |
| 1171 | 1198 | if (fn_type_id.cc != CallingConventionUnspecified) { |
| 1172 | 1199 | type_ensure_zero_bits_known(g, type_entry); |
| 1173 | 1200 | if (!type_has_bits(type_entry)) { |
| ... | ... | @@ -2204,6 +2231,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { |
| 2204 | 2231 | // is a pointer to this very struct, or a function pointer with parameters that |
| 2205 | 2232 | // reference such a type. |
| 2206 | 2233 | union_type->data.unionation.zero_bits_known = true; |
| 2234 | union_type->data.unionation.zero_bits_loop_flag = false; | |
| 2207 | 2235 | if (union_type->data.unionation.abi_alignment == 0) { |
| 2208 | 2236 | if (union_type->data.unionation.layout == ContainerLayoutPacked) { |
| 2209 | 2237 | union_type->data.unionation.abi_alignment = 1; |
| ... | ... | @@ -2558,7 +2586,7 @@ static bool scope_is_root_decls(Scope *scope) { |
| 2558 | 2586 | |
| 2559 | 2587 | static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) { |
| 2560 | 2588 | add_node_error(g, proto_node, |
| 2561 | buf_sprintf("expected 'fn([]const u8) -> unreachable', found '%s'", | |
| 2589 | buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) -> unreachable', found '%s'", | |
| 2562 | 2590 | buf_ptr(&fn_type->name))); |
| 2563 | 2591 | } |
| 2564 | 2592 | |
| ... | ... | @@ -2567,7 +2595,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { |
| 2567 | 2595 | assert(proto_node->type == NodeTypeFnProto); |
| 2568 | 2596 | TypeTableEntry *fn_type = panic_fn->type_entry; |
| 2569 | 2597 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; |
| 2570 | if (fn_type_id->param_count != 1) { | |
| 2598 | if (fn_type_id->param_count != 2) { | |
| 2571 | 2599 | return wrong_panic_prototype(g, proto_node, fn_type); |
| 2572 | 2600 | } |
| 2573 | 2601 | TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true); |
| ... | ... | @@ -2576,6 +2604,11 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { |
| 2576 | 2604 | return wrong_panic_prototype(g, proto_node, fn_type); |
| 2577 | 2605 | } |
| 2578 | 2606 | |
| 2607 | TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); | |
| 2608 | if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) { | |
| 2609 | return wrong_panic_prototype(g, proto_node, fn_type); | |
| 2610 | } | |
| 2611 | ||
| 2579 | 2612 | TypeTableEntry *actual_return_type = fn_type_id->return_type; |
| 2580 | 2613 | if (actual_return_type != g->builtin_types.entry_unreachable) { |
| 2581 | 2614 | return wrong_panic_prototype(g, proto_node, fn_type); |
| ... | ... | @@ -2680,13 +2713,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 2680 | 2713 | { |
| 2681 | 2714 | if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { |
| 2682 | 2715 | g->main_fn = fn_table_entry; |
| 2683 | TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); | |
| 2684 | TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; | |
| 2685 | if (actual_return_type != err_void) { | |
| 2686 | add_node_error(g, fn_proto->return_type, | |
| 2687 | buf_sprintf("expected return type of main to be '%%void', instead is '%s'", | |
| 2688 | buf_ptr(&actual_return_type->name))); | |
| 2689 | } | |
| 2690 | 2716 | } else if ((import->package == g->panic_package || g->have_pub_panic) && |
| 2691 | 2717 | buf_eql_str(&fn_table_entry->symbol_name, "panic")) |
| 2692 | 2718 | { |
| ... | ... | @@ -5527,3 +5553,13 @@ bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) { |
| 5527 | 5553 | return a == b; |
| 5528 | 5554 | } |
| 5529 | 5555 | |
| 5556 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { | |
| 5557 | Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); | |
| 5558 | resolve_top_level_decl(codegen, tld, false, nullptr); | |
| 5559 | assert(tld->id == TldIdVar); | |
| 5560 | TldVar *tld_var = (TldVar *)tld; | |
| 5561 | ConstExprValue *var_value = tld_var->var->value; | |
| 5562 | assert(var_value != nullptr); | |
| 5563 | return var_value; | |
| 5564 | } | |
| 5565 |
src/analyze.hpp+5| ... | ... | @@ -185,4 +185,9 @@ PackageTableEntry *new_anonymous_package(void); |
| 185 | 185 | Buf *const_value_to_buffer(ConstExprValue *const_val); |
| 186 | 186 | void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc); |
| 187 | 187 | |
| 188 | ||
| 189 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); | |
| 190 | TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); | |
| 191 | ||
| 192 | ||
| 188 | 193 | #endif |
src/bigint.cpp+440-10| ... | ... | @@ -12,6 +12,9 @@ |
| 12 | 12 | #include "os.hpp" |
| 13 | 13 | #include "softfloat.hpp" |
| 14 | 14 | |
| 15 | #include <limits> | |
| 16 | #include <algorithm> | |
| 17 | ||
| 15 | 18 | static void bigint_normalize(BigInt *dest) { |
| 16 | 19 | const uint64_t *digits = bigint_ptr(dest); |
| 17 | 20 | |
| ... | ... | @@ -539,7 +542,7 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 539 | 542 | dest->data.digits[i] = x; |
| 540 | 543 | i += 1; |
| 541 | 544 | |
| 542 | if (!found_digit) | |
| 545 | if (!found_digit || i >= bigger_op->digit_count) | |
| 543 | 546 | break; |
| 544 | 547 | } |
| 545 | 548 | assert(overflow == 0); |
| ... | ... | @@ -670,19 +673,417 @@ void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t |
| 670 | 673 | bigint_truncate(dest, &unwrapped, bit_count, is_signed); |
| 671 | 674 | } |
| 672 | 675 | |
| 676 | enum ZeroBehavior { | |
| 677 | /// \brief The returned value is undefined. | |
| 678 | ZB_Undefined, | |
| 679 | /// \brief The returned value is numeric_limits<T>::max() | |
| 680 | ZB_Max, | |
| 681 | /// \brief The returned value is numeric_limits<T>::digits | |
| 682 | ZB_Width | |
| 683 | }; | |
| 684 | ||
| 685 | template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter { | |
| 686 | static std::size_t count(T Val, ZeroBehavior) { | |
| 687 | if (!Val) | |
| 688 | return std::numeric_limits<T>::digits; | |
| 689 | ||
| 690 | // Bisection method. | |
| 691 | std::size_t ZeroBits = 0; | |
| 692 | for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) { | |
| 693 | T Tmp = Val >> Shift; | |
| 694 | if (Tmp) | |
| 695 | Val = Tmp; | |
| 696 | else | |
| 697 | ZeroBits |= Shift; | |
| 698 | } | |
| 699 | return ZeroBits; | |
| 700 | } | |
| 701 | }; | |
| 702 | ||
| 703 | #if __GNUC__ >= 4 || defined(_MSC_VER) | |
| 704 | template <typename T> struct LeadingZerosCounter<T, 4> { | |
| 705 | static std::size_t count(T Val, ZeroBehavior ZB) { | |
| 706 | if (ZB != ZB_Undefined && Val == 0) | |
| 707 | return 32; | |
| 708 | ||
| 709 | #if defined(_MSC_VER) | |
| 710 | unsigned long Index; | |
| 711 | _BitScanReverse(&Index, Val); | |
| 712 | return Index ^ 31; | |
| 713 | #else | |
| 714 | return __builtin_clz(Val); | |
| 715 | #endif | |
| 716 | } | |
| 717 | }; | |
| 718 | ||
| 719 | #if !defined(_MSC_VER) || defined(_M_X64) | |
| 720 | template <typename T> struct LeadingZerosCounter<T, 8> { | |
| 721 | static std::size_t count(T Val, ZeroBehavior ZB) { | |
| 722 | if (ZB != ZB_Undefined && Val == 0) | |
| 723 | return 64; | |
| 724 | ||
| 725 | #if defined(_MSC_VER) | |
| 726 | unsigned long Index; | |
| 727 | _BitScanReverse64(&Index, Val); | |
| 728 | return Index ^ 63; | |
| 729 | #else | |
| 730 | return __builtin_clzll(Val); | |
| 731 | #endif | |
| 732 | } | |
| 733 | }; | |
| 734 | #endif | |
| 735 | #endif | |
| 736 | ||
| 737 | /// \brief Count number of 0's from the most significant bit to the least | |
| 738 | /// stopping at the first 1. | |
| 739 | /// | |
| 740 | /// Only unsigned integral types are allowed. | |
| 741 | /// | |
| 742 | /// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are | |
| 743 | /// valid arguments. | |
| 744 | template <typename T> | |
| 745 | std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) { | |
| 746 | static_assert(std::numeric_limits<T>::is_integer && | |
| 747 | !std::numeric_limits<T>::is_signed, | |
| 748 | "Only unsigned integral types are allowed."); | |
| 749 | return LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB); | |
| 750 | } | |
| 751 | ||
| 752 | /// Make a 64-bit integer from a high / low pair of 32-bit integers. | |
| 753 | constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { | |
| 754 | return ((uint64_t)High << 32) | (uint64_t)Low; | |
| 755 | } | |
| 756 | ||
| 757 | /// Return the high 32 bits of a 64 bit value. | |
| 758 | constexpr inline uint32_t Hi_32(uint64_t Value) { | |
| 759 | return static_cast<uint32_t>(Value >> 32); | |
| 760 | } | |
| 761 | ||
| 762 | /// Return the low 32 bits of a 64 bit value. | |
| 763 | constexpr inline uint32_t Lo_32(uint64_t Value) { | |
| 764 | return static_cast<uint32_t>(Value); | |
| 765 | } | |
| 766 | ||
| 767 | /// Implementation of Knuth's Algorithm D (Division of nonnegative integers) | |
| 768 | /// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The | |
| 769 | /// variables here have the same names as in the algorithm. Comments explain | |
| 770 | /// the algorithm and any deviation from it. | |
| 771 | static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, | |
| 772 | unsigned m, unsigned n) | |
| 773 | { | |
| 774 | assert(u && "Must provide dividend"); | |
| 775 | assert(v && "Must provide divisor"); | |
| 776 | assert(q && "Must provide quotient"); | |
| 777 | assert(u != v && u != q && v != q && "Must use different memory"); | |
| 778 | assert(n>1 && "n must be > 1"); | |
| 779 | ||
| 780 | // b denotes the base of the number system. In our case b is 2^32. | |
| 781 | const uint64_t b = uint64_t(1) << 32; | |
| 782 | ||
| 783 | // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of | |
| 784 | // u and v by d. Note that we have taken Knuth's advice here to use a power | |
| 785 | // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of | |
| 786 | // 2 allows us to shift instead of multiply and it is easy to determine the | |
| 787 | // shift amount from the leading zeros. We are basically normalizing the u | |
| 788 | // and v so that its high bits are shifted to the top of v's range without | |
| 789 | // overflow. Note that this can require an extra word in u so that u must | |
| 790 | // be of length m+n+1. | |
| 791 | unsigned shift = countLeadingZeros(v[n-1]); | |
| 792 | uint32_t v_carry = 0; | |
| 793 | uint32_t u_carry = 0; | |
| 794 | if (shift) { | |
| 795 | for (unsigned i = 0; i < m+n; ++i) { | |
| 796 | uint32_t u_tmp = u[i] >> (32 - shift); | |
| 797 | u[i] = (u[i] << shift) | u_carry; | |
| 798 | u_carry = u_tmp; | |
| 799 | } | |
| 800 | for (unsigned i = 0; i < n; ++i) { | |
| 801 | uint32_t v_tmp = v[i] >> (32 - shift); | |
| 802 | v[i] = (v[i] << shift) | v_carry; | |
| 803 | v_carry = v_tmp; | |
| 804 | } | |
| 805 | } | |
| 806 | u[m+n] = u_carry; | |
| 807 | ||
| 808 | // D2. [Initialize j.] Set j to m. This is the loop counter over the places. | |
| 809 | int j = m; | |
| 810 | do { | |
| 811 | // D3. [Calculate q'.]. | |
| 812 | // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') | |
| 813 | // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') | |
| 814 | // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease | |
| 815 | // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test | |
| 816 | // on v[n-2] determines at high speed most of the cases in which the trial | |
| 817 | // value qp is one too large, and it eliminates all cases where qp is two | |
| 818 | // too large. | |
| 819 | uint64_t dividend = Make_64(u[j+n], u[j+n-1]); | |
| 820 | uint64_t qp = dividend / v[n-1]; | |
| 821 | uint64_t rp = dividend % v[n-1]; | |
| 822 | if (qp == b || qp*v[n-2] > b*rp + u[j+n-2]) { | |
| 823 | qp--; | |
| 824 | rp += v[n-1]; | |
| 825 | if (rp < b && (qp == b || qp*v[n-2] > b*rp + u[j+n-2])) | |
| 826 | qp--; | |
| 827 | } | |
| 828 | ||
| 829 | // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with | |
| 830 | // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation | |
| 831 | // consists of a simple multiplication by a one-place number, combined with | |
| 832 | // a subtraction. | |
| 833 | // The digits (u[j+n]...u[j]) should be kept positive; if the result of | |
| 834 | // this step is actually negative, (u[j+n]...u[j]) should be left as the | |
| 835 | // true value plus b**(n+1), namely as the b's complement of | |
| 836 | // the true value, and a "borrow" to the left should be remembered. | |
| 837 | int64_t borrow = 0; | |
| 838 | for (unsigned i = 0; i < n; ++i) { | |
| 839 | uint64_t p = uint64_t(qp) * uint64_t(v[i]); | |
| 840 | int64_t subres = int64_t(u[j+i]) - borrow - Lo_32(p); | |
| 841 | u[j+i] = Lo_32(subres); | |
| 842 | borrow = Hi_32(p) - Hi_32(subres); | |
| 843 | } | |
| 844 | bool isNeg = u[j+n] < borrow; | |
| 845 | u[j+n] -= Lo_32(borrow); | |
| 846 | ||
| 847 | // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was | |
| 848 | // negative, go to step D6; otherwise go on to step D7. | |
| 849 | q[j] = Lo_32(qp); | |
| 850 | if (isNeg) { | |
| 851 | // D6. [Add back]. The probability that this step is necessary is very | |
| 852 | // small, on the order of only 2/b. Make sure that test data accounts for | |
| 853 | // this possibility. Decrease q[j] by 1 | |
| 854 | q[j]--; | |
| 855 | // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). | |
| 856 | // A carry will occur to the left of u[j+n], and it should be ignored | |
| 857 | // since it cancels with the borrow that occurred in D4. | |
| 858 | bool carry = false; | |
| 859 | for (unsigned i = 0; i < n; i++) { | |
| 860 | uint32_t limit = std::min(u[j+i],v[i]); | |
| 861 | u[j+i] += v[i] + carry; | |
| 862 | carry = u[j+i] < limit || (carry && u[j+i] == limit); | |
| 863 | } | |
| 864 | u[j+n] += carry; | |
| 865 | } | |
| 866 | ||
| 867 | // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. | |
| 868 | } while (--j >= 0); | |
| 869 | ||
| 870 | // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired | |
| 871 | // remainder may be obtained by dividing u[...] by d. If r is non-null we | |
| 872 | // compute the remainder (urem uses this). | |
| 873 | if (r) { | |
| 874 | // The value d is expressed by the "shift" value above since we avoided | |
| 875 | // multiplication by d by using a shift left. So, all we have to do is | |
| 876 | // shift right here. | |
| 877 | if (shift) { | |
| 878 | uint32_t carry = 0; | |
| 879 | for (int i = n-1; i >= 0; i--) { | |
| 880 | r[i] = (u[i] >> shift) | carry; | |
| 881 | carry = u[i] << (32 - shift); | |
| 882 | } | |
| 883 | } else { | |
| 884 | for (int i = n-1; i >= 0; i--) { | |
| 885 | r[i] = u[i]; | |
| 886 | } | |
| 887 | } | |
| 888 | } | |
| 889 | } | |
| 890 | ||
| 891 | // Implementation ported from LLVM/lib/Support/APInt.cpp | |
| 892 | static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) { | |
| 893 | Cmp cmp = bigint_cmp(op1, op2); | |
| 894 | if (cmp == CmpLT) { | |
| 895 | if (Quotient != nullptr) { | |
| 896 | bigint_init_unsigned(Quotient, 0); | |
| 897 | } | |
| 898 | if (Remainder != nullptr) { | |
| 899 | bigint_init_bigint(Remainder, op1); | |
| 900 | } | |
| 901 | return; | |
| 902 | } | |
| 903 | if (cmp == CmpEQ) { | |
| 904 | if (Quotient != nullptr) { | |
| 905 | bigint_init_unsigned(Quotient, 1); | |
| 906 | } | |
| 907 | if (Remainder != nullptr) { | |
| 908 | bigint_init_unsigned(Remainder, 0); | |
| 909 | } | |
| 910 | return; | |
| 911 | } | |
| 912 | ||
| 913 | const uint64_t *LHS = bigint_ptr(op1); | |
| 914 | const uint64_t *RHS = bigint_ptr(op2); | |
| 915 | unsigned lhsWords = op1->digit_count; | |
| 916 | unsigned rhsWords = op2->digit_count; | |
| 917 | ||
| 918 | // First, compose the values into an array of 32-bit words instead of | |
| 919 | // 64-bit words. This is a necessity of both the "short division" algorithm | |
| 920 | // and the Knuth "classical algorithm" which requires there to be native | |
| 921 | // operations for +, -, and * on an m bit value with an m*2 bit result. We | |
| 922 | // can't use 64-bit operands here because we don't have native results of | |
| 923 | // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't | |
| 924 | // work on large-endian machines. | |
| 925 | unsigned n = rhsWords * 2; | |
| 926 | unsigned m = (lhsWords * 2) - n; | |
| 927 | ||
| 928 | // Allocate space for the temporary values we need either on the stack, if | |
| 929 | // it will fit, or on the heap if it won't. | |
| 930 | uint32_t SPACE[128]; | |
| 931 | uint32_t *U = nullptr; | |
| 932 | uint32_t *V = nullptr; | |
| 933 | uint32_t *Q = nullptr; | |
| 934 | uint32_t *R = nullptr; | |
| 935 | if ((Remainder?4:3)*n+2*m+1 <= 128) { | |
| 936 | U = &SPACE[0]; | |
| 937 | V = &SPACE[m+n+1]; | |
| 938 | Q = &SPACE[(m+n+1) + n]; | |
| 939 | if (Remainder) | |
| 940 | R = &SPACE[(m+n+1) + n + (m+n)]; | |
| 941 | } else { | |
| 942 | U = new uint32_t[m + n + 1]; | |
| 943 | V = new uint32_t[n]; | |
| 944 | Q = new uint32_t[m+n]; | |
| 945 | if (Remainder) | |
| 946 | R = new uint32_t[n]; | |
| 947 | } | |
| 948 | ||
| 949 | // Initialize the dividend | |
| 950 | memset(U, 0, (m+n+1)*sizeof(uint32_t)); | |
| 951 | for (unsigned i = 0; i < lhsWords; ++i) { | |
| 952 | uint64_t tmp = LHS[i]; | |
| 953 | U[i * 2] = Lo_32(tmp); | |
| 954 | U[i * 2 + 1] = Hi_32(tmp); | |
| 955 | } | |
| 956 | U[m+n] = 0; // this extra word is for "spill" in the Knuth algorithm. | |
| 957 | ||
| 958 | // Initialize the divisor | |
| 959 | memset(V, 0, (n)*sizeof(uint32_t)); | |
| 960 | for (unsigned i = 0; i < rhsWords; ++i) { | |
| 961 | uint64_t tmp = RHS[i]; | |
| 962 | V[i * 2] = Lo_32(tmp); | |
| 963 | V[i * 2 + 1] = Hi_32(tmp); | |
| 964 | } | |
| 965 | ||
| 966 | // initialize the quotient and remainder | |
| 967 | memset(Q, 0, (m+n) * sizeof(uint32_t)); | |
| 968 | if (Remainder) | |
| 969 | memset(R, 0, n * sizeof(uint32_t)); | |
| 970 | ||
| 971 | // Now, adjust m and n for the Knuth division. n is the number of words in | |
| 972 | // the divisor. m is the number of words by which the dividend exceeds the | |
| 973 | // divisor (i.e. m+n is the length of the dividend). These sizes must not | |
| 974 | // contain any zero words or the Knuth algorithm fails. | |
| 975 | for (unsigned i = n; i > 0 && V[i-1] == 0; i--) { | |
| 976 | n--; | |
| 977 | m++; | |
| 978 | } | |
| 979 | for (unsigned i = m+n; i > 0 && U[i-1] == 0; i--) | |
| 980 | m--; | |
| 981 | ||
| 982 | // If we're left with only a single word for the divisor, Knuth doesn't work | |
| 983 | // so we implement the short division algorithm here. This is much simpler | |
| 984 | // and faster because we are certain that we can divide a 64-bit quantity | |
| 985 | // by a 32-bit quantity at hardware speed and short division is simply a | |
| 986 | // series of such operations. This is just like doing short division but we | |
| 987 | // are using base 2^32 instead of base 10. | |
| 988 | assert(n != 0 && "Divide by zero?"); | |
| 989 | if (n == 1) { | |
| 990 | uint32_t divisor = V[0]; | |
| 991 | uint32_t remainder = 0; | |
| 992 | for (int i = m; i >= 0; i--) { | |
| 993 | uint64_t partial_dividend = Make_64(remainder, U[i]); | |
| 994 | if (partial_dividend == 0) { | |
| 995 | Q[i] = 0; | |
| 996 | remainder = 0; | |
| 997 | } else if (partial_dividend < divisor) { | |
| 998 | Q[i] = 0; | |
| 999 | remainder = Lo_32(partial_dividend); | |
| 1000 | } else if (partial_dividend == divisor) { | |
| 1001 | Q[i] = 1; | |
| 1002 | remainder = 0; | |
| 1003 | } else { | |
| 1004 | Q[i] = Lo_32(partial_dividend / divisor); | |
| 1005 | remainder = Lo_32(partial_dividend - (Q[i] * divisor)); | |
| 1006 | } | |
| 1007 | } | |
| 1008 | if (R) | |
| 1009 | R[0] = remainder; | |
| 1010 | } else { | |
| 1011 | // Now we're ready to invoke the Knuth classical divide algorithm. In this | |
| 1012 | // case n > 1. | |
| 1013 | KnuthDiv(U, V, Q, R, m, n); | |
| 1014 | } | |
| 1015 | ||
| 1016 | // If the caller wants the quotient | |
| 1017 | if (Quotient) { | |
| 1018 | Quotient->is_negative = false; | |
| 1019 | Quotient->digit_count = lhsWords; | |
| 1020 | if (lhsWords == 1) { | |
| 1021 | Quotient->data.digit = Make_64(Q[1], Q[0]); | |
| 1022 | } else { | |
| 1023 | Quotient->data.digits = allocate<uint64_t>(lhsWords); | |
| 1024 | for (size_t i = 0; i < lhsWords; i += 1) { | |
| 1025 | Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]); | |
| 1026 | } | |
| 1027 | } | |
| 1028 | } | |
| 1029 | ||
| 1030 | // If the caller wants the remainder | |
| 1031 | if (Remainder) { | |
| 1032 | Remainder->is_negative = false; | |
| 1033 | Remainder->digit_count = rhsWords; | |
| 1034 | if (rhsWords == 1) { | |
| 1035 | Remainder->data.digit = Make_64(R[1], R[0]); | |
| 1036 | } else { | |
| 1037 | Remainder->data.digits = allocate<uint64_t>(rhsWords); | |
| 1038 | for (size_t i = 0; i < rhsWords; i += 1) { | |
| 1039 | Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]); | |
| 1040 | } | |
| 1041 | } | |
| 1042 | } | |
| 1043 | } | |
| 1044 | ||
| 673 | 1045 | void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 674 | 1046 | assert(op2->digit_count != 0); // division by zero |
| 675 | 1047 | if (op1->digit_count == 0) { |
| 676 | 1048 | bigint_init_unsigned(dest, 0); |
| 677 | 1049 | return; |
| 678 | 1050 | } |
| 679 | if (op1->digit_count != 1 || op2->digit_count != 1) { | |
| 680 | zig_panic("TODO bigint div_trunc with >1 digits"); | |
| 681 | } | |
| 682 | 1051 | const uint64_t *op1_digits = bigint_ptr(op1); |
| 683 | 1052 | const uint64_t *op2_digits = bigint_ptr(op2); |
| 684 | dest->data.digit = op1_digits[0] / op2_digits[0]; | |
| 685 | dest->digit_count = 1; | |
| 1053 | if (op1->digit_count == 1 && op2->digit_count == 1) { | |
| 1054 | dest->data.digit = op1_digits[0] / op2_digits[0]; | |
| 1055 | dest->digit_count = 1; | |
| 1056 | dest->is_negative = op1->is_negative != op2->is_negative; | |
| 1057 | bigint_normalize(dest); | |
| 1058 | return; | |
| 1059 | } | |
| 1060 | if (op2->digit_count == 1 && op2_digits[0] == 1) { | |
| 1061 | // X / 1 == X | |
| 1062 | bigint_init_bigint(dest, op1); | |
| 1063 | dest->is_negative = op1->is_negative != op2->is_negative; | |
| 1064 | bigint_normalize(dest); | |
| 1065 | return; | |
| 1066 | } | |
| 1067 | ||
| 1068 | const BigInt *op1_positive; | |
| 1069 | BigInt op1_positive_data; | |
| 1070 | if (op1->is_negative) { | |
| 1071 | bigint_negate(&op1_positive_data, op1); | |
| 1072 | op1_positive = &op1_positive_data; | |
| 1073 | } else { | |
| 1074 | op1_positive = op1; | |
| 1075 | } | |
| 1076 | ||
| 1077 | const BigInt *op2_positive; | |
| 1078 | BigInt op2_positive_data; | |
| 1079 | if (op2->is_negative) { | |
| 1080 | bigint_negate(&op2_positive_data, op2); | |
| 1081 | op2_positive = &op2_positive_data; | |
| 1082 | } else { | |
| 1083 | op2_positive = op2; | |
| 1084 | } | |
| 1085 | ||
| 1086 | bigint_unsigned_division(op1_positive, op2_positive, dest, nullptr); | |
| 686 | 1087 | dest->is_negative = op1->is_negative != op2->is_negative; |
| 687 | 1088 | bigint_normalize(dest); |
| 688 | 1089 | } |
| ... | ... | @@ -714,6 +1115,14 @@ void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 714 | 1115 | } |
| 715 | 1116 | const uint64_t *op1_digits = bigint_ptr(op1); |
| 716 | 1117 | const uint64_t *op2_digits = bigint_ptr(op2); |
| 1118 | ||
| 1119 | if (op1->digit_count == 1 && op2->digit_count == 1) { | |
| 1120 | dest->data.digit = op1_digits[0] % op2_digits[0]; | |
| 1121 | dest->digit_count = 1; | |
| 1122 | dest->is_negative = op1->is_negative; | |
| 1123 | bigint_normalize(dest); | |
| 1124 | return; | |
| 1125 | } | |
| 717 | 1126 | if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) { |
| 718 | 1127 | // special case this divisor |
| 719 | 1128 | bigint_init_unsigned(dest, op1_digits[0]); |
| ... | ... | @@ -721,11 +1130,32 @@ void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 721 | 1130 | bigint_normalize(dest); |
| 722 | 1131 | return; |
| 723 | 1132 | } |
| 724 | if (op1->digit_count != 1 || op2->digit_count != 1) { | |
| 725 | zig_panic("TODO bigint rem with >1 digits"); | |
| 1133 | ||
| 1134 | if (op2->digit_count == 1 && op2_digits[0] == 1) { | |
| 1135 | // X % 1 == 0 | |
| 1136 | bigint_init_unsigned(dest, 0); | |
| 1137 | return; | |
| 726 | 1138 | } |
| 727 | dest->data.digit = op1_digits[0] % op2_digits[0]; | |
| 728 | dest->digit_count = 1; | |
| 1139 | ||
| 1140 | const BigInt *op1_positive; | |
| 1141 | BigInt op1_positive_data; | |
| 1142 | if (op1->is_negative) { | |
| 1143 | bigint_negate(&op1_positive_data, op1); | |
| 1144 | op1_positive = &op1_positive_data; | |
| 1145 | } else { | |
| 1146 | op1_positive = op1; | |
| 1147 | } | |
| 1148 | ||
| 1149 | const BigInt *op2_positive; | |
| 1150 | BigInt op2_positive_data; | |
| 1151 | if (op2->is_negative) { | |
| 1152 | bigint_negate(&op2_positive_data, op2); | |
| 1153 | op2_positive = &op2_positive_data; | |
| 1154 | } else { | |
| 1155 | op2_positive = op2; | |
| 1156 | } | |
| 1157 | ||
| 1158 | bigint_unsigned_division(op1_positive, op2_positive, nullptr, dest); | |
| 729 | 1159 | dest->is_negative = op1->is_negative; |
| 730 | 1160 | bigint_normalize(dest); |
| 731 | 1161 | } |
src/codegen.cpp+231-16| ... | ... | @@ -404,6 +404,19 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) { |
| 404 | 404 | zig_unreachable(); |
| 405 | 405 | } |
| 406 | 406 | |
| 407 | static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) { | |
| 408 | if (!g->have_err_ret_tracing) { | |
| 409 | return UINT32_MAX; | |
| 410 | } | |
| 411 | TypeTableEntry *fn_type = fn_table_entry->type_entry; | |
| 412 | TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; | |
| 413 | if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) { | |
| 414 | return UINT32_MAX; | |
| 415 | } | |
| 416 | bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type); | |
| 417 | return first_arg_ret ? 1 : 0; | |
| 418 | } | |
| 419 | ||
| 407 | 420 | static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 408 | 421 | if (fn_table_entry->llvm_value) |
| 409 | 422 | return fn_table_entry->llvm_value; |
| ... | ... | @@ -483,7 +496,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 483 | 496 | LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true); |
| 484 | 497 | } |
| 485 | 498 | |
| 486 | if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) { | |
| 499 | TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; | |
| 500 | if (return_type->id == TypeTableEntryIdUnreachable) { | |
| 487 | 501 | addLLVMFnAttr(fn_table_entry->llvm_value, "noreturn"); |
| 488 | 502 | } |
| 489 | 503 | |
| ... | ... | @@ -520,13 +534,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 520 | 534 | // use the ABI alignment, which is fine. |
| 521 | 535 | } |
| 522 | 536 | |
| 523 | if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) { | |
| 537 | if (!type_has_bits(return_type)) { | |
| 524 | 538 | // nothing to do |
| 525 | } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer || | |
| 526 | fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdFn) | |
| 527 | { | |
| 539 | } else if (return_type->id == TypeTableEntryIdPointer || return_type->id == TypeTableEntryIdFn) { | |
| 528 | 540 | addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); |
| 529 | } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) && | |
| 541 | } else if (handle_is_ptr(return_type) && | |
| 530 | 542 | calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) |
| 531 | 543 | { |
| 532 | 544 | addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); |
| ... | ... | @@ -563,6 +575,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 563 | 575 | } |
| 564 | 576 | } |
| 565 | 577 | |
| 578 | uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); | |
| 579 | if (err_ret_trace_arg_index != UINT32_MAX) { | |
| 580 | addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); | |
| 581 | } | |
| 582 | ||
| 566 | 583 | return fn_table_entry->llvm_value; |
| 567 | 584 | } |
| 568 | 585 | |
| ... | ... | @@ -864,16 +881,25 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { |
| 864 | 881 | return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); |
| 865 | 882 | } |
| 866 | 883 | |
| 867 | static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { | |
| 884 | static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) { | |
| 868 | 885 | assert(g->panic_fn != nullptr); |
| 869 | 886 | LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); |
| 870 | 887 | LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); |
| 871 | ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, ""); | |
| 888 | if (stack_trace_arg == nullptr) { | |
| 889 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); | |
| 890 | stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref); | |
| 891 | } | |
| 892 | LLVMValueRef args[] = { | |
| 893 | msg_arg, | |
| 894 | stack_trace_arg, | |
| 895 | }; | |
| 896 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); | |
| 897 | LLVMSetTailCall(call_instruction, true); | |
| 872 | 898 | LLVMBuildUnreachable(g->builder); |
| 873 | 899 | } |
| 874 | 900 | |
| 875 | 901 | static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { |
| 876 | gen_panic(g, get_panic_msg_ptr_val(g, msg_id)); | |
| 902 | gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); | |
| 877 | 903 | } |
| 878 | 904 | |
| 879 | 905 | static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { |
| ... | ... | @@ -895,6 +921,87 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { |
| 895 | 921 | return g->memcpy_fn_val; |
| 896 | 922 | } |
| 897 | 923 | |
| 924 | static LLVMValueRef get_return_err_fn(CodeGen *g) { | |
| 925 | if (g->return_err_fn != nullptr) | |
| 926 | return g->return_err_fn; | |
| 927 | ||
| 928 | assert(g->err_tag_type != nullptr); | |
| 929 | ||
| 930 | LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); | |
| 931 | ||
| 932 | LLVMTypeRef arg_types[] = { | |
| 933 | // error return trace pointer | |
| 934 | get_ptr_to_stack_trace_type(g)->type_ref, | |
| 935 | // return address | |
| 936 | ptr_u8, | |
| 937 | }; | |
| 938 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); | |
| 939 | ||
| 940 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false); | |
| 941 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); | |
| 942 | addLLVMFnAttr(fn_val, "cold"); | |
| 943 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); | |
| 944 | LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); | |
| 945 | addLLVMFnAttr(fn_val, "nounwind"); | |
| 946 | add_uwtable_attr(g, fn_val); | |
| 947 | addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); | |
| 948 | addLLVMArgAttr(fn_val, (unsigned)1, "nonnull"); | |
| 949 | if (g->build_mode == BuildModeDebug) { | |
| 950 | ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); | |
| 951 | ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr); | |
| 952 | } | |
| 953 | ||
| 954 | LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); | |
| 955 | LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); | |
| 956 | LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); | |
| 957 | LLVMPositionBuilderAtEnd(g->builder, entry_block); | |
| 958 | ZigLLVMClearCurrentDebugLocation(g->builder); | |
| 959 | ||
| 960 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; | |
| 961 | ||
| 962 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address; | |
| 963 | ||
| 964 | LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); | |
| 965 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; | |
| 966 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); | |
| 967 | size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; | |
| 968 | LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, ""); | |
| 969 | ||
| 970 | TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; | |
| 971 | size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; | |
| 972 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); | |
| 973 | size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; | |
| 974 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); | |
| 975 | ||
| 976 | LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); | |
| 977 | LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); | |
| 978 | LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, ""); | |
| 979 | LLVMValueRef address_indices[] = { | |
| 980 | modded_val, | |
| 981 | }; | |
| 982 | ||
| 983 | LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); | |
| 984 | LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); | |
| 985 | ||
| 986 | LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, LLVMGetParam(fn_val, 1), usize_type_ref, ""); | |
| 987 | ||
| 988 | LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, ""); | |
| 989 | gen_store_untyped(g, address_value, address_slot, 0, false); | |
| 990 | ||
| 991 | // stack_trace.index += 1; | |
| 992 | LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); | |
| 993 | gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); | |
| 994 | ||
| 995 | // return; | |
| 996 | LLVMBuildRetVoid(g->builder); | |
| 997 | ||
| 998 | LLVMPositionBuilderAtEnd(g->builder, prev_block); | |
| 999 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); | |
| 1000 | ||
| 1001 | g->return_err_fn = fn_val; | |
| 1002 | return fn_val; | |
| 1003 | } | |
| 1004 | ||
| 898 | 1005 | static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 899 | 1006 | if (g->safety_crash_err_fn != nullptr) |
| 900 | 1007 | return g->safety_crash_err_fn; |
| ... | ... | @@ -953,7 +1060,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 953 | 1060 | LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2); |
| 954 | 1061 | |
| 955 | 1062 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false); |
| 956 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false); | |
| 1063 | LLVMTypeRef arg_types[] = { | |
| 1064 | g->ptr_to_stack_trace_type->type_ref, | |
| 1065 | g->err_tag_type->type_ref, | |
| 1066 | }; | |
| 1067 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); | |
| 957 | 1068 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); |
| 958 | 1069 | addLLVMFnAttr(fn_val, "noreturn"); |
| 959 | 1070 | addLLVMFnAttr(fn_val, "cold"); |
| ... | ... | @@ -975,7 +1086,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 975 | 1086 | LLVMPositionBuilderAtEnd(g->builder, entry_block); |
| 976 | 1087 | ZigLLVMClearCurrentDebugLocation(g->builder); |
| 977 | 1088 | |
| 978 | LLVMValueRef err_val = LLVMGetParam(fn_val, 0); | |
| 1089 | LLVMValueRef err_val = LLVMGetParam(fn_val, 1); | |
| 979 | 1090 | |
| 980 | 1091 | LLVMValueRef err_table_indices[] = { |
| 981 | 1092 | LLVMConstNull(g->builtin_types.entry_usize->type_ref), |
| ... | ... | @@ -1005,7 +1116,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 1005 | 1116 | LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, ""); |
| 1006 | 1117 | gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type); |
| 1007 | 1118 | |
| 1008 | gen_panic(g, global_slice); | |
| 1119 | gen_panic(g, global_slice, LLVMGetParam(fn_val, 0)); | |
| 1009 | 1120 | |
| 1010 | 1121 | LLVMPositionBuilderAtEnd(g->builder, prev_block); |
| 1011 | 1122 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); |
| ... | ... | @@ -1016,8 +1127,18 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 1016 | 1127 | |
| 1017 | 1128 | static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { |
| 1018 | 1129 | LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); |
| 1019 | ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified), | |
| 1130 | LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val; | |
| 1131 | if (err_ret_trace_val == nullptr) { | |
| 1132 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); | |
| 1133 | err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref); | |
| 1134 | } | |
| 1135 | LLVMValueRef args[] = { | |
| 1136 | err_ret_trace_val, | |
| 1137 | err_val, | |
| 1138 | }; | |
| 1139 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified), | |
| 1020 | 1140 | ZigLLVM_FnInlineAuto, ""); |
| 1141 | LLVMSetTailCall(call_instruction, true); | |
| 1021 | 1142 | LLVMBuildUnreachable(g->builder); |
| 1022 | 1143 | } |
| 1023 | 1144 | |
| ... | ... | @@ -1296,6 +1417,35 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { |
| 1296 | 1417 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { |
| 1297 | 1418 | LLVMValueRef value = ir_llvm_value(g, return_instruction->value); |
| 1298 | 1419 | TypeTableEntry *return_type = return_instruction->value->value.type; |
| 1420 | ||
| 1421 | if (g->have_err_ret_tracing) { | |
| 1422 | bool is_err_return = false; | |
| 1423 | if (return_type->id == TypeTableEntryIdErrorUnion) { | |
| 1424 | if (return_instruction->value->value.special == ConstValSpecialStatic) { | |
| 1425 | is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; | |
| 1426 | } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { | |
| 1427 | is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; | |
| 1428 | // TODO: emit a branch to check if the return value is an error | |
| 1429 | } | |
| 1430 | } else if (return_type->id == TypeTableEntryIdPureError) { | |
| 1431 | is_err_return = true; | |
| 1432 | } | |
| 1433 | if (is_err_return) { | |
| 1434 | LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError"); | |
| 1435 | LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block); | |
| 1436 | ||
| 1437 | LLVMValueRef return_err_fn = get_return_err_fn(g); | |
| 1438 | LLVMValueRef args[] = { | |
| 1439 | g->cur_err_ret_trace_val, | |
| 1440 | block_address, | |
| 1441 | }; | |
| 1442 | LLVMBuildBr(g->builder, return_block); | |
| 1443 | LLVMPositionBuilderAtEnd(g->builder, return_block); | |
| 1444 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2, | |
| 1445 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); | |
| 1446 | LLVMSetTailCall(call_instruction, true); | |
| 1447 | } | |
| 1448 | } | |
| 1299 | 1449 | if (handle_is_ptr(return_type)) { |
| 1300 | 1450 | if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { |
| 1301 | 1451 | assert(g->cur_ret_ptr); |
| ... | ... | @@ -2330,7 +2480,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 2330 | 2480 | TypeTableEntry *src_return_type = fn_type_id->return_type; |
| 2331 | 2481 | bool ret_has_bits = type_has_bits(src_return_type); |
| 2332 | 2482 | bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type); |
| 2333 | size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0); | |
| 2483 | bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError); | |
| 2484 | size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0); | |
| 2334 | 2485 | bool is_var_args = fn_type_id->is_var_args; |
| 2335 | 2486 | LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count); |
| 2336 | 2487 | size_t gen_param_index = 0; |
| ... | ... | @@ -2338,6 +2489,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 2338 | 2489 | gen_param_values[gen_param_index] = instruction->tmp_ptr; |
| 2339 | 2490 | gen_param_index += 1; |
| 2340 | 2491 | } |
| 2492 | if (prefix_arg_err_ret_stack) { | |
| 2493 | gen_param_values[gen_param_index] = g->cur_err_ret_trace_val; | |
| 2494 | gen_param_index += 1; | |
| 2495 | } | |
| 2341 | 2496 | for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { |
| 2342 | 2497 | IrInstruction *param_instruction = instruction->args[call_i]; |
| 2343 | 2498 | TypeTableEntry *param_type = param_instruction->value.type; |
| ... | ... | @@ -2881,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I |
| 2881 | 3036 | return target_val; |
| 2882 | 3037 | } |
| 2883 | 3038 | |
| 3039 | static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable, | |
| 3040 | IrInstructionErrorReturnTrace *instruction) | |
| 3041 | { | |
| 3042 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); | |
| 3043 | if (g->cur_err_ret_trace_val == nullptr) { | |
| 3044 | return LLVMConstNull(ptr_to_stack_trace_type->type_ref); | |
| 3045 | } | |
| 3046 | return g->cur_err_ret_trace_val; | |
| 3047 | } | |
| 3048 | ||
| 2884 | 3049 | static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { |
| 2885 | 3050 | switch (atomic_order) { |
| 2886 | 3051 | case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered; |
| ... | ... | @@ -3474,7 +3639,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec |
| 3474 | 3639 | } |
| 3475 | 3640 | |
| 3476 | 3641 | static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { |
| 3477 | gen_panic(g, ir_llvm_value(g, instruction->msg)); | |
| 3642 | gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr); | |
| 3478 | 3643 | return nullptr; |
| 3479 | 3644 | } |
| 3480 | 3645 | |
| ... | ... | @@ -3654,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 3654 | 3819 | return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); |
| 3655 | 3820 | case IrInstructionIdAlignCast: |
| 3656 | 3821 | return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction); |
| 3822 | case IrInstructionIdErrorReturnTrace: | |
| 3823 | return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction); | |
| 3657 | 3824 | } |
| 3658 | 3825 | zig_unreachable(); |
| 3659 | 3826 | } |
| ... | ... | @@ -4493,7 +4660,8 @@ static void do_code_gen(CodeGen *g) { |
| 4493 | 4660 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); |
| 4494 | 4661 | g->cur_fn = fn_table_entry; |
| 4495 | 4662 | g->cur_fn_val = fn; |
| 4496 | if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { | |
| 4663 | TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; | |
| 4664 | if (handle_is_ptr(return_type)) { | |
| 4497 | 4665 | g->cur_ret_ptr = LLVMGetParam(fn, 0); |
| 4498 | 4666 | } else { |
| 4499 | 4667 | g->cur_ret_ptr = nullptr; |
| ... | ... | @@ -4502,6 +4670,42 @@ static void do_code_gen(CodeGen *g) { |
| 4502 | 4670 | build_all_basic_blocks(g, fn_table_entry); |
| 4503 | 4671 | clear_debug_source_node(g); |
| 4504 | 4672 | |
| 4673 | uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); | |
| 4674 | if (err_ret_trace_arg_index != UINT32_MAX) { | |
| 4675 | g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index); | |
| 4676 | } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) { | |
| 4677 | // TODO call graph analysis to find out what this number needs to be for every function | |
| 4678 | static const size_t stack_trace_ptr_count = 30; | |
| 4679 | ||
| 4680 | TypeTableEntry *usize = g->builtin_types.entry_usize; | |
| 4681 | TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count); | |
| 4682 | LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", | |
| 4683 | get_abi_alignment(g, array_type)); | |
| 4684 | g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); | |
| 4685 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; | |
| 4686 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); | |
| 4687 | gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false); | |
| 4688 | ||
| 4689 | size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; | |
| 4690 | LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, ""); | |
| 4691 | ||
| 4692 | TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; | |
| 4693 | size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; | |
| 4694 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); | |
| 4695 | LLVMValueRef zero = LLVMConstNull(usize->type_ref); | |
| 4696 | LLVMValueRef indices[] = {zero, zero}; | |
| 4697 | LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val, | |
| 4698 | indices, 2, ""); | |
| 4699 | gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, | |
| 4700 | get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false)); | |
| 4701 | ||
| 4702 | size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; | |
| 4703 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); | |
| 4704 | gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); | |
| 4705 | } else { | |
| 4706 | g->cur_err_ret_trace_val = nullptr; | |
| 4707 | } | |
| 4708 | ||
| 4505 | 4709 | // allocate temporary stack data |
| 4506 | 4710 | for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { |
| 4507 | 4711 | IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); |
| ... | ... | @@ -5064,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 5064 | 5268 | create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); |
| 5065 | 5269 | create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2); |
| 5066 | 5270 | create_builtin_fn(g, BuiltinFnIdExport, "export", 3); |
| 5271 | create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); | |
| 5067 | 5272 | } |
| 5068 | 5273 | |
| 5069 | 5274 | static const char *bool_to_str(bool b) { |
| ... | ... | @@ -5088,6 +5293,12 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5088 | 5293 | os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); |
| 5089 | 5294 | Buf *contents = buf_alloc(); |
| 5090 | 5295 | |
| 5296 | buf_append_str(contents, | |
| 5297 | "pub const StackTrace = struct {\n" | |
| 5298 | " index: usize,\n" | |
| 5299 | " instruction_addresses: []usize,\n" | |
| 5300 | "};\n\n"); | |
| 5301 | ||
| 5091 | 5302 | const char *cur_os = nullptr; |
| 5092 | 5303 | { |
| 5093 | 5304 | buf_appendf(contents, "pub const Os = enum {\n"); |
| ... | ... | @@ -5233,6 +5444,7 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5233 | 5444 | buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); |
| 5234 | 5445 | buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); |
| 5235 | 5446 | buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); |
| 5447 | buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); | |
| 5236 | 5448 | |
| 5237 | 5449 | buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); |
| 5238 | 5450 | |
| ... | ... | @@ -5251,6 +5463,7 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5251 | 5463 | g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 5252 | 5464 | g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 5253 | 5465 | g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents); |
| 5466 | scan_import(g, g->compile_var_import); | |
| 5254 | 5467 | } |
| 5255 | 5468 | |
| 5256 | 5469 | static void init(CodeGen *g) { |
| ... | ... | @@ -5359,6 +5572,8 @@ static void init(CodeGen *g) { |
| 5359 | 5572 | } |
| 5360 | 5573 | } |
| 5361 | 5574 | |
| 5575 | g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease; | |
| 5576 | ||
| 5362 | 5577 | define_builtin_fns(g); |
| 5363 | 5578 | define_builtin_compile_vars(g); |
| 5364 | 5579 | } |
src/ir.cpp+49-12| ... | ... | @@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) { |
| 572 | 572 | return IrInstructionIdArgType; |
| 573 | 573 | } |
| 574 | 574 | |
| 575 | static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) { | |
| 576 | return IrInstructionIdErrorReturnTrace; | |
| 577 | } | |
| 578 | ||
| 575 | 579 | template<typename T> |
| 576 | 580 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 577 | 581 | T *special_instruction = allocate<T>(1); |
| ... | ... | @@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s |
| 2305 | 2309 | return &instruction->base; |
| 2306 | 2310 | } |
| 2307 | 2311 | |
| 2312 | static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) { | |
| 2313 | IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node); | |
| 2314 | ||
| 2315 | return &instruction->base; | |
| 2316 | } | |
| 2317 | ||
| 2308 | 2318 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 2309 | 2319 | results[ReturnKindUnconditional] = 0; |
| 2310 | 2320 | results[ReturnKindError] = 0; |
| ... | ... | @@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3731 | 3741 | |
| 3732 | 3742 | return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); |
| 3733 | 3743 | } |
| 3744 | case BuiltinFnIdErrorReturnTrace: | |
| 3745 | { | |
| 3746 | return ir_build_error_return_trace(irb, scope, node); | |
| 3747 | } | |
| 3734 | 3748 | } |
| 3735 | 3749 | zig_unreachable(); |
| 3736 | 3750 | } |
| ... | ... | @@ -8230,16 +8244,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) |
| 8230 | 8244 | return ir_resolve_bool(ira, value, out); |
| 8231 | 8245 | } |
| 8232 | 8246 | |
| 8233 | static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { | |
| 8234 | Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); | |
| 8235 | resolve_top_level_decl(codegen, tld, false, nullptr); | |
| 8236 | assert(tld->id == TldIdVar); | |
| 8237 | TldVar *tld_var = (TldVar *)tld; | |
| 8238 | ConstExprValue *var_value = tld_var->var->value; | |
| 8239 | assert(var_value != nullptr); | |
| 8240 | return var_value; | |
| 8241 | } | |
| 8242 | ||
| 8243 | 8247 | static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { |
| 8244 | 8248 | if (type_is_invalid(value->value.type)) |
| 8245 | 8249 | return false; |
| ... | ... | @@ -9578,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi |
| 9578 | 9582 | return ira->codegen->builtin_types.entry_void; |
| 9579 | 9583 | } |
| 9580 | 9584 | |
| 9585 | static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, | |
| 9586 | IrInstructionErrorReturnTrace *instruction) | |
| 9587 | { | |
| 9588 | FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); | |
| 9589 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); | |
| 9590 | TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); | |
| 9591 | if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) { | |
| 9592 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 9593 | out_val->data.x_maybe = nullptr; | |
| 9594 | return nullable_type; | |
| 9595 | } | |
| 9596 | ||
| 9597 | IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, | |
| 9598 | instruction->base.source_node); | |
| 9599 | ir_link_new_instruction(new_instruction, &instruction->base); | |
| 9600 | return nullable_type; | |
| 9601 | } | |
| 9602 | ||
| 9581 | 9603 | static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, |
| 9582 | 9604 | IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) |
| 9583 | 9605 | { |
| ... | ... | @@ -9836,7 +9858,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 9836 | 9858 | |
| 9837 | 9859 | if (fn_proto_node->data.fn_proto.is_var_args) { |
| 9838 | 9860 | ir_add_error(ira, &call_instruction->base, |
| 9839 | buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/andrewrk/zig/issues/313")); | |
| 9861 | buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/zig-lang/zig/issues/313")); | |
| 9840 | 9862 | return ira->codegen->builtin_types.entry_invalid; |
| 9841 | 9863 | } |
| 9842 | 9864 | |
| ... | ... | @@ -10053,9 +10075,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 10053 | 10075 | TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; |
| 10054 | 10076 | ir_add_alloca(ira, new_call_instruction, return_type); |
| 10055 | 10077 | |
| 10078 | if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { | |
| 10079 | parent_fn_entry->calls_errorable_function = true; | |
| 10080 | } | |
| 10081 | ||
| 10056 | 10082 | return ir_finish_anal(ira, return_type); |
| 10057 | 10083 | } |
| 10058 | 10084 | |
| 10085 | FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); | |
| 10086 | assert(fn_type_id->return_type != nullptr); | |
| 10087 | assert(parent_fn_entry != nullptr); | |
| 10088 | if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { | |
| 10089 | parent_fn_entry->calls_errorable_function = true; | |
| 10090 | } | |
| 10091 | ||
| 10092 | ||
| 10059 | 10093 | IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count); |
| 10060 | 10094 | size_t next_arg_index = 0; |
| 10061 | 10095 | if (first_arg_ptr) { |
| ... | ... | @@ -13977,7 +14011,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc |
| 13977 | 14011 | return ira->codegen->builtin_types.entry_invalid; |
| 13978 | 14012 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); |
| 13979 | 14013 | |
| 13980 | ensure_complete_type(ira->codegen, type_entry); | |
| 14014 | type_ensure_zero_bits_known(ira->codegen, type_entry); | |
| 13981 | 14015 | if (type_is_invalid(type_entry)) |
| 13982 | 14016 | return ira->codegen->builtin_types.entry_invalid; |
| 13983 | 14017 | |
| ... | ... | @@ -15322,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 15322 | 15356 | return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction); |
| 15323 | 15357 | case IrInstructionIdExport: |
| 15324 | 15358 | return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction); |
| 15359 | case IrInstructionIdErrorReturnTrace: | |
| 15360 | return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction); | |
| 15325 | 15361 | } |
| 15326 | 15362 | zig_unreachable(); |
| 15327 | 15363 | } |
| ... | ... | @@ -15505,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 15505 | 15541 | case IrInstructionIdOpaqueType: |
| 15506 | 15542 | case IrInstructionIdArgType: |
| 15507 | 15543 | case IrInstructionIdTagType: |
| 15544 | case IrInstructionIdErrorReturnTrace: | |
| 15508 | 15545 | return false; |
| 15509 | 15546 | case IrInstructionIdAsm: |
| 15510 | 15547 | { |
src/ir_print.cpp+7| ... | ... | @@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { |
| 996 | 996 | } |
| 997 | 997 | } |
| 998 | 998 | |
| 999 | static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { | |
| 1000 | fprintf(irp->f, "@errorReturnTrace()"); | |
| 1001 | } | |
| 1002 | ||
| 999 | 1003 | |
| 1000 | 1004 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1001 | 1005 | ir_print_prefix(irp, instruction); |
| ... | ... | @@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1308 | 1312 | case IrInstructionIdExport: |
| 1309 | 1313 | ir_print_export(irp, (IrInstructionExport *)instruction); |
| 1310 | 1314 | break; |
| 1315 | case IrInstructionIdErrorReturnTrace: | |
| 1316 | ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction); | |
| 1317 | break; | |
| 1311 | 1318 | } |
| 1312 | 1319 | fprintf(irp->f, "\n"); |
| 1313 | 1320 | } |
src/translate_c.cpp+83-7| ... | ... | @@ -1964,6 +1964,8 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt |
| 1964 | 1964 | if (init_node == nullptr) |
| 1965 | 1965 | return ErrorUnexpected; |
| 1966 | 1966 | |
| 1967 | } else { | |
| 1968 | init_node = trans_create_node(c, NodeTypeUndefinedLiteral); | |
| 1967 | 1969 | } |
| 1968 | 1970 | AstNode *type_node = trans_qual_type(c, qual_type, stmt->getLocStart()); |
| 1969 | 1971 | if (type_node == nullptr) |
| ... | ... | @@ -2224,12 +2226,6 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt * |
| 2224 | 2226 | // if (c) t else e |
| 2225 | 2227 | AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr); |
| 2226 | 2228 | |
| 2227 | // TODO: condition != 0 | |
| 2228 | AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); | |
| 2229 | if (condition_node == nullptr) | |
| 2230 | return nullptr; | |
| 2231 | if_node->data.if_bool_expr.condition = condition_node; | |
| 2232 | ||
| 2233 | 2229 | TransScope *then_scope = trans_stmt(c, scope, stmt->getThen(), &if_node->data.if_bool_expr.then_block); |
| 2234 | 2230 | if (then_scope == nullptr) |
| 2235 | 2231 | return nullptr; |
| ... | ... | @@ -2240,7 +2236,87 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt * |
| 2240 | 2236 | return nullptr; |
| 2241 | 2237 | } |
| 2242 | 2238 | |
| 2243 | return if_node; | |
| 2239 | AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); | |
| 2240 | if (condition_node == nullptr) | |
| 2241 | return nullptr; | |
| 2242 | ||
| 2243 | switch (condition_node->type) { | |
| 2244 | case NodeTypeBinOpExpr: | |
| 2245 | switch (condition_node->data.bin_op_expr.bin_op) { | |
| 2246 | case BinOpTypeBoolOr: | |
| 2247 | case BinOpTypeBoolAnd: | |
| 2248 | case BinOpTypeCmpEq: | |
| 2249 | case BinOpTypeCmpNotEq: | |
| 2250 | case BinOpTypeCmpLessThan: | |
| 2251 | case BinOpTypeCmpGreaterThan: | |
| 2252 | case BinOpTypeCmpLessOrEq: | |
| 2253 | case BinOpTypeCmpGreaterOrEq: | |
| 2254 | if_node->data.if_bool_expr.condition = condition_node; | |
| 2255 | return if_node; | |
| 2256 | default: | |
| 2257 | goto convert_to_bitcast; | |
| 2258 | } | |
| 2259 | ||
| 2260 | case NodeTypePrefixOpExpr: | |
| 2261 | switch (condition_node->data.prefix_op_expr.prefix_op) { | |
| 2262 | case PrefixOpBoolNot: | |
| 2263 | if_node->data.if_bool_expr.condition = condition_node; | |
| 2264 | return if_node; | |
| 2265 | default: | |
| 2266 | goto convert_to_bitcast; | |
| 2267 | } | |
| 2268 | ||
| 2269 | case NodeTypeBoolLiteral: | |
| 2270 | if_node->data.if_bool_expr.condition = condition_node; | |
| 2271 | return if_node; | |
| 2272 | ||
| 2273 | default: { | |
| 2274 | // In Zig, float, int and pointer does not work in if statements. | |
| 2275 | // To make it work, we bitcast any value we get to an int of the right size | |
| 2276 | // and comp it to 0 | |
| 2277 | // TODO: This doesn't work for pointers, as they become nullable on | |
| 2278 | // translate | |
| 2279 | // c: if (cond) { } | |
| 2280 | // zig: { | |
| 2281 | // zig: const _tmp = cond; | |
| 2282 | // zig: if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { } | |
| 2283 | // zig: } | |
| 2284 | convert_to_bitcast: | |
| 2285 | TransScopeBlock *child_scope = trans_scope_block_create(c, scope); | |
| 2286 | ||
| 2287 | // const _tmp = cond; | |
| 2288 | // TODO: avoid name collisions with generated variable names | |
| 2289 | Buf* tmp_var_name = buf_create_from_str("_tmp"); | |
| 2290 | AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, condition_node); | |
| 2291 | child_scope->node->data.block.statements.append(tmp_var_decl); | |
| 2292 | ||
| 2293 | // @sizeOf(@typeOf(_tmp)) * 8 | |
| 2294 | AstNode *typeof_tmp = trans_create_node_builtin_fn_call_str(c, "typeOf"); | |
| 2295 | typeof_tmp->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); | |
| 2296 | AstNode *sizeof_tmp = trans_create_node_builtin_fn_call_str(c, "sizeOf"); | |
| 2297 | sizeof_tmp->data.fn_call_expr.params.append(typeof_tmp); | |
| 2298 | AstNode *sizeof_tmp_in_bits = trans_create_node_bin_op( | |
| 2299 | c, sizeof_tmp, BinOpTypeMult, | |
| 2300 | trans_create_node_unsigned_negative(c, 8, false)); | |
| 2301 | ||
| 2302 | // @IntType(false, @sizeOf(@typeOf(_tmp)) * 8) | |
| 2303 | AstNode *int_type = trans_create_node_builtin_fn_call_str(c, "IntType"); | |
| 2304 | int_type->data.fn_call_expr.params.append(trans_create_node_bool(c, false)); | |
| 2305 | int_type->data.fn_call_expr.params.append(sizeof_tmp_in_bits); | |
| 2306 | ||
| 2307 | // @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) | |
| 2308 | AstNode *bit_cast = trans_create_node_builtin_fn_call_str(c, "bitCast"); | |
| 2309 | bit_cast->data.fn_call_expr.params.append(int_type); | |
| 2310 | bit_cast->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); | |
| 2311 | ||
| 2312 | // if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { } | |
| 2313 | AstNode *not_eql_zero = trans_create_node_bin_op(c, bit_cast, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); | |
| 2314 | if_node->data.if_bool_expr.condition = not_eql_zero; | |
| 2315 | child_scope->node->data.block.statements.append(if_node); | |
| 2316 | ||
| 2317 | return child_scope->node; | |
| 2318 | } | |
| 2319 | } | |
| 2244 | 2320 | } |
| 2245 | 2321 | |
| 2246 | 2322 | static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) { |
std/build.zig+4-6| ... | ... | @@ -247,11 +247,11 @@ pub const Builder = struct { |
| 247 | 247 | defer wanted_steps.deinit(); |
| 248 | 248 | |
| 249 | 249 | if (step_names.len == 0) { |
| 250 | wanted_steps.append(&self.default_step) catch unreachable; | |
| 250 | try wanted_steps.append(&self.default_step); | |
| 251 | 251 | } else { |
| 252 | 252 | for (step_names) |step_name| { |
| 253 | 253 | const s = try self.getTopLevelStepByName(step_name); |
| 254 | wanted_steps.append(s) catch unreachable; | |
| 254 | try wanted_steps.append(s); | |
| 255 | 255 | } |
| 256 | 256 | } |
| 257 | 257 | |
| ... | ... | @@ -721,11 +721,9 @@ pub const Builder = struct { |
| 721 | 721 | return error.FileNotFound; |
| 722 | 722 | } |
| 723 | 723 | |
| 724 | pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 { | |
| 724 | pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 { | |
| 725 | 725 | const max_output_size = 100 * 1024; |
| 726 | const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| { | |
| 727 | std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err)); | |
| 728 | }; | |
| 726 | const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); | |
| 729 | 727 | switch (result.term) { |
| 730 | 728 | os.ChildProcess.Term.Exited => |code| { |
| 731 | 729 | if (code != 0) { |
std/crypto/blake2.zig created+445| ... | ... | @@ -0,0 +1,445 @@ |
| 1 | const mem = @import("../mem.zig"); | |
| 2 | const math = @import("../math/index.zig"); | |
| 3 | const endian = @import("../endian.zig"); | |
| 4 | const debug = @import("../debug/index.zig"); | |
| 5 | const builtin = @import("builtin"); | |
| 6 | const htest = @import("test.zig"); | |
| 7 | ||
| 8 | const RoundParam = struct { | |
| 9 | a: usize, b: usize, c: usize, d: usize, x: usize, y: usize, | |
| 10 | }; | |
| 11 | ||
| 12 | fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) -> RoundParam { | |
| 13 | return RoundParam { .a = a, .b = b, .c = c, .d = d, .x = x, .y = y, }; | |
| 14 | } | |
| 15 | ||
| 16 | ///////////////////// | |
| 17 | // Blake2s | |
| 18 | ||
| 19 | pub const Blake2s224 = Blake2s(224); | |
| 20 | pub const Blake2s256 = Blake2s(256); | |
| 21 | ||
| 22 | fn Blake2s(comptime out_len: usize) -> type { return struct { | |
| 23 | const Self = this; | |
| 24 | ||
| 25 | const iv = [8]u32 { | |
| 26 | 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, | |
| 27 | 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, | |
| 28 | }; | |
| 29 | ||
| 30 | const sigma = [10][16]u8 { | |
| 31 | []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, | |
| 32 | []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, | |
| 33 | []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, | |
| 34 | []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, | |
| 35 | []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, | |
| 36 | []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, | |
| 37 | []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, | |
| 38 | []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, | |
| 39 | []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, | |
| 40 | []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, | |
| 41 | }; | |
| 42 | ||
| 43 | h: [8]u32, | |
| 44 | t: u64, | |
| 45 | // Streaming cache | |
| 46 | buf: [64]u8, | |
| 47 | buf_len: u8, | |
| 48 | ||
| 49 | pub fn init() -> Self { | |
| 50 | debug.assert(8 <= out_len and out_len <= 512); | |
| 51 | ||
| 52 | var s: Self = undefined; | |
| 53 | s.reset(); | |
| 54 | return s; | |
| 55 | } | |
| 56 | ||
| 57 | pub fn reset(d: &Self) { | |
| 58 | mem.copy(u32, d.h[0..], iv[0..]); | |
| 59 | ||
| 60 | // No key plus default parameters | |
| 61 | d.h[0] ^= 0x01010000 ^ u32(out_len >> 3); | |
| 62 | d.t = 0; | |
| 63 | d.buf_len = 0; | |
| 64 | } | |
| 65 | ||
| 66 | pub fn hash(b: []const u8, out: []u8) { | |
| 67 | var d = Self.init(); | |
| 68 | d.update(b); | |
| 69 | d.final(out); | |
| 70 | } | |
| 71 | ||
| 72 | pub fn update(d: &Self, b: []const u8) { | |
| 73 | var off: usize = 0; | |
| 74 | ||
| 75 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 76 | if (d.buf_len != 0 and d.buf_len + b.len > 64) { | |
| 77 | off += 64 - d.buf_len; | |
| 78 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 79 | d.t += 64; | |
| 80 | d.round(d.buf[0..], false); | |
| 81 | d.buf_len = 0; | |
| 82 | } | |
| 83 | ||
| 84 | // Full middle blocks. | |
| 85 | while (off + 64 < b.len) : (off += 64) { | |
| 86 | d.t += 64; | |
| 87 | d.round(b[off..off + 64], false); | |
| 88 | } | |
| 89 | ||
| 90 | // Copy any remainder for next pass. | |
| 91 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 92 | d.buf_len += u8(b[off..].len); | |
| 93 | } | |
| 94 | ||
| 95 | pub fn final(d: &Self, out: []u8) { | |
| 96 | debug.assert(out.len >= out_len / 8); | |
| 97 | ||
| 98 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 99 | d.t += d.buf_len; | |
| 100 | d.round(d.buf[0..], true); | |
| 101 | ||
| 102 | const rr = d.h[0 .. out_len / 32]; | |
| 103 | ||
| 104 | for (rr) |s, j| { | |
| 105 | mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little); | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | fn round(d: &Self, b: []const u8, last: bool) { | |
| 110 | debug.assert(b.len == 64); | |
| 111 | ||
| 112 | var m: [16]u32 = undefined; | |
| 113 | var v: [16]u32 = undefined; | |
| 114 | ||
| 115 | for (m) |*r, i| { | |
| 116 | *r = mem.readIntLE(u32, b[4*i .. 4*i + 4]); | |
| 117 | } | |
| 118 | ||
| 119 | var k: usize = 0; | |
| 120 | while (k < 8) : (k += 1) { | |
| 121 | v[k] = d.h[k]; | |
| 122 | v[k+8] = iv[k]; | |
| 123 | } | |
| 124 | ||
| 125 | v[12] ^= @truncate(u32, d.t); | |
| 126 | v[13] ^= u32(d.t >> 32); | |
| 127 | if (last) v[14] = ~v[14]; | |
| 128 | ||
| 129 | const rounds = comptime []RoundParam { | |
| 130 | Rp(0, 4, 8, 12, 0, 1), | |
| 131 | Rp(1, 5, 9, 13, 2, 3), | |
| 132 | Rp(2, 6, 10, 14, 4, 5), | |
| 133 | Rp(3, 7, 11, 15, 6, 7), | |
| 134 | Rp(0, 5, 10, 15, 8, 9), | |
| 135 | Rp(1, 6, 11, 12, 10, 11), | |
| 136 | Rp(2, 7, 8, 13, 12, 13), | |
| 137 | Rp(3, 4, 9, 14, 14, 15), | |
| 138 | }; | |
| 139 | ||
| 140 | comptime var j: usize = 0; | |
| 141 | inline while (j < 10) : (j += 1) { | |
| 142 | inline for (rounds) |r| { | |
| 143 | v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; | |
| 144 | v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16)); | |
| 145 | v[r.c] = v[r.c] +% v[r.d]; | |
| 146 | v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12)); | |
| 147 | v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; | |
| 148 | v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8)); | |
| 149 | v[r.c] = v[r.c] +% v[r.d]; | |
| 150 | v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7)); | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | for (d.h) |*r, i| { | |
| 155 | *r ^= v[i] ^ v[i + 8]; | |
| 156 | } | |
| 157 | } | |
| 158 | };} | |
| 159 | ||
| 160 | test "blake2s224 single" { | |
| 161 | const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; | |
| 162 | htest.assertEqualHash(Blake2s224, h1, ""); | |
| 163 | ||
| 164 | const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55"; | |
| 165 | htest.assertEqualHash(Blake2s224, h2, "abc"); | |
| 166 | ||
| 167 | const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912"; | |
| 168 | htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog"); | |
| 169 | } | |
| 170 | ||
| 171 | test "blake2s224 streaming" { | |
| 172 | var h = Blake2s224.init(); | |
| 173 | var out: [28]u8 = undefined; | |
| 174 | ||
| 175 | const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; | |
| 176 | ||
| 177 | h.final(out[0..]); | |
| 178 | htest.assertEqual(h1, out[0..]); | |
| 179 | ||
| 180 | const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55"; | |
| 181 | ||
| 182 | h.reset(); | |
| 183 | h.update("abc"); | |
| 184 | h.final(out[0..]); | |
| 185 | htest.assertEqual(h2, out[0..]); | |
| 186 | ||
| 187 | h.reset(); | |
| 188 | h.update("a"); | |
| 189 | h.update("b"); | |
| 190 | h.update("c"); | |
| 191 | h.final(out[0..]); | |
| 192 | htest.assertEqual(h2, out[0..]); | |
| 193 | } | |
| 194 | ||
| 195 | test "blake2s256 single" { | |
| 196 | const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"; | |
| 197 | htest.assertEqualHash(Blake2s256, h1, ""); | |
| 198 | ||
| 199 | const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"; | |
| 200 | htest.assertEqualHash(Blake2s256, h2, "abc"); | |
| 201 | ||
| 202 | const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812"; | |
| 203 | htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog"); | |
| 204 | } | |
| 205 | ||
| 206 | test "blake2s256 streaming" { | |
| 207 | var h = Blake2s256.init(); | |
| 208 | var out: [32]u8 = undefined; | |
| 209 | ||
| 210 | const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"; | |
| 211 | ||
| 212 | h.final(out[0..]); | |
| 213 | htest.assertEqual(h1, out[0..]); | |
| 214 | ||
| 215 | const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"; | |
| 216 | ||
| 217 | h.reset(); | |
| 218 | h.update("abc"); | |
| 219 | h.final(out[0..]); | |
| 220 | htest.assertEqual(h2, out[0..]); | |
| 221 | ||
| 222 | h.reset(); | |
| 223 | h.update("a"); | |
| 224 | h.update("b"); | |
| 225 | h.update("c"); | |
| 226 | h.final(out[0..]); | |
| 227 | htest.assertEqual(h2, out[0..]); | |
| 228 | } | |
| 229 | ||
| 230 | ||
| 231 | ///////////////////// | |
| 232 | // Blake2b | |
| 233 | ||
| 234 | pub const Blake2b384 = Blake2b(384); | |
| 235 | pub const Blake2b512 = Blake2b(512); | |
| 236 | ||
| 237 | fn Blake2b(comptime out_len: usize) -> type { return struct { | |
| 238 | const Self = this; | |
| 239 | ||
| 240 | const iv = [8]u64 { | |
| 241 | 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, | |
| 242 | 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, | |
| 243 | 0x510e527fade682d1, 0x9b05688c2b3e6c1f, | |
| 244 | 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, | |
| 245 | }; | |
| 246 | ||
| 247 | const sigma = [12][16]u8 { | |
| 248 | []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, | |
| 249 | []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, | |
| 250 | []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, | |
| 251 | []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, | |
| 252 | []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, | |
| 253 | []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, | |
| 254 | []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, | |
| 255 | []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, | |
| 256 | []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, | |
| 257 | []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 }, | |
| 258 | []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, | |
| 259 | []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, | |
| 260 | }; | |
| 261 | ||
| 262 | h: [8]u64, | |
| 263 | t: u128, | |
| 264 | // Streaming cache | |
| 265 | buf: [128]u8, | |
| 266 | buf_len: u8, | |
| 267 | ||
| 268 | pub fn init() -> Self { | |
| 269 | debug.assert(8 <= out_len and out_len <= 512); | |
| 270 | ||
| 271 | var s: Self = undefined; | |
| 272 | s.reset(); | |
| 273 | return s; | |
| 274 | } | |
| 275 | ||
| 276 | pub fn reset(d: &Self) { | |
| 277 | mem.copy(u64, d.h[0..], iv[0..]); | |
| 278 | ||
| 279 | // No key plus default parameters | |
| 280 | d.h[0] ^= 0x01010000 ^ (out_len >> 3); | |
| 281 | d.t = 0; | |
| 282 | d.buf_len = 0; | |
| 283 | } | |
| 284 | ||
| 285 | pub fn hash(b: []const u8, out: []u8) { | |
| 286 | var d = Self.init(); | |
| 287 | d.update(b); | |
| 288 | d.final(out); | |
| 289 | } | |
| 290 | ||
| 291 | pub fn update(d: &Self, b: []const u8) { | |
| 292 | var off: usize = 0; | |
| 293 | ||
| 294 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 295 | if (d.buf_len != 0 and d.buf_len + b.len > 128) { | |
| 296 | off += 128 - d.buf_len; | |
| 297 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 298 | d.t += 128; | |
| 299 | d.round(d.buf[0..], false); | |
| 300 | d.buf_len = 0; | |
| 301 | } | |
| 302 | ||
| 303 | // Full middle blocks. | |
| 304 | while (off + 128 < b.len) : (off += 128) { | |
| 305 | d.t += 128; | |
| 306 | d.round(b[off..off + 128], false); | |
| 307 | } | |
| 308 | ||
| 309 | // Copy any remainder for next pass. | |
| 310 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 311 | d.buf_len += u8(b[off..].len); | |
| 312 | } | |
| 313 | ||
| 314 | pub fn final(d: &Self, out: []u8) { | |
| 315 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 316 | d.t += d.buf_len; | |
| 317 | d.round(d.buf[0..], true); | |
| 318 | ||
| 319 | const rr = d.h[0 .. out_len / 64]; | |
| 320 | ||
| 321 | for (rr) |s, j| { | |
| 322 | mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Little); | |
| 323 | } | |
| 324 | } | |
| 325 | ||
| 326 | fn round(d: &Self, b: []const u8, last: bool) { | |
| 327 | debug.assert(b.len == 128); | |
| 328 | ||
| 329 | var m: [16]u64 = undefined; | |
| 330 | var v: [16]u64 = undefined; | |
| 331 | ||
| 332 | for (m) |*r, i| { | |
| 333 | *r = mem.readIntLE(u64, b[8*i .. 8*i + 8]); | |
| 334 | } | |
| 335 | ||
| 336 | var k: usize = 0; | |
| 337 | while (k < 8) : (k += 1) { | |
| 338 | v[k] = d.h[k]; | |
| 339 | v[k+8] = iv[k]; | |
| 340 | } | |
| 341 | ||
| 342 | v[12] ^= @truncate(u64, d.t); | |
| 343 | v[13] ^= u64(d.t >> 64); | |
| 344 | if (last) v[14] = ~v[14]; | |
| 345 | ||
| 346 | const rounds = comptime []RoundParam { | |
| 347 | Rp(0, 4, 8, 12, 0, 1), | |
| 348 | Rp(1, 5, 9, 13, 2, 3), | |
| 349 | Rp(2, 6, 10, 14, 4, 5), | |
| 350 | Rp(3, 7, 11, 15, 6, 7), | |
| 351 | Rp(0, 5, 10, 15, 8, 9), | |
| 352 | Rp(1, 6, 11, 12, 10, 11), | |
| 353 | Rp(2, 7, 8, 13, 12, 13), | |
| 354 | Rp(3, 4, 9, 14, 14, 15), | |
| 355 | }; | |
| 356 | ||
| 357 | comptime var j: usize = 0; | |
| 358 | inline while (j < 12) : (j += 1) { | |
| 359 | inline for (rounds) |r| { | |
| 360 | v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; | |
| 361 | v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32)); | |
| 362 | v[r.c] = v[r.c] +% v[r.d]; | |
| 363 | v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24)); | |
| 364 | v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; | |
| 365 | v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16)); | |
| 366 | v[r.c] = v[r.c] +% v[r.d]; | |
| 367 | v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63)); | |
| 368 | } | |
| 369 | } | |
| 370 | ||
| 371 | for (d.h) |*r, i| { | |
| 372 | *r ^= v[i] ^ v[i + 8]; | |
| 373 | } | |
| 374 | } | |
| 375 | };} | |
| 376 | ||
| 377 | test "blake2b384 single" { | |
| 378 | const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; | |
| 379 | htest.assertEqualHash(Blake2b384, h1, ""); | |
| 380 | ||
| 381 | const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4"; | |
| 382 | htest.assertEqualHash(Blake2b384, h2, "abc"); | |
| 383 | ||
| 384 | const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d"; | |
| 385 | htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog"); | |
| 386 | } | |
| 387 | ||
| 388 | test "blake2b384 streaming" { | |
| 389 | var h = Blake2b384.init(); | |
| 390 | var out: [48]u8 = undefined; | |
| 391 | ||
| 392 | const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; | |
| 393 | ||
| 394 | h.final(out[0..]); | |
| 395 | htest.assertEqual(h1, out[0..]); | |
| 396 | ||
| 397 | const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4"; | |
| 398 | ||
| 399 | h.reset(); | |
| 400 | h.update("abc"); | |
| 401 | h.final(out[0..]); | |
| 402 | htest.assertEqual(h2, out[0..]); | |
| 403 | ||
| 404 | h.reset(); | |
| 405 | h.update("a"); | |
| 406 | h.update("b"); | |
| 407 | h.update("c"); | |
| 408 | h.final(out[0..]); | |
| 409 | htest.assertEqual(h2, out[0..]); | |
| 410 | } | |
| 411 | ||
| 412 | test "blake2b512 single" { | |
| 413 | const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"; | |
| 414 | htest.assertEqualHash(Blake2b512, h1, ""); | |
| 415 | ||
| 416 | const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"; | |
| 417 | htest.assertEqualHash(Blake2b512, h2, "abc"); | |
| 418 | ||
| 419 | const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918"; | |
| 420 | htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog"); | |
| 421 | } | |
| 422 | ||
| 423 | test "blake2b512 streaming" { | |
| 424 | var h = Blake2b512.init(); | |
| 425 | var out: [64]u8 = undefined; | |
| 426 | ||
| 427 | const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"; | |
| 428 | ||
| 429 | h.final(out[0..]); | |
| 430 | htest.assertEqual(h1, out[0..]); | |
| 431 | ||
| 432 | const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"; | |
| 433 | ||
| 434 | h.reset(); | |
| 435 | h.update("abc"); | |
| 436 | h.final(out[0..]); | |
| 437 | htest.assertEqual(h2, out[0..]); | |
| 438 | ||
| 439 | h.reset(); | |
| 440 | h.update("a"); | |
| 441 | h.update("b"); | |
| 442 | h.update("c"); | |
| 443 | h.final(out[0..]); | |
| 444 | htest.assertEqual(h2, out[0..]); | |
| 445 | } |
std/crypto/index.zig created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | pub const Md5 = @import("sha1.zig").Md5; | |
| 2 | pub const Sha1 = @import("md5.zig").Sha1; | |
| 3 | ||
| 4 | const sha2 = @import("sha2.zig"); | |
| 5 | pub const Sha224 = sha2.Sha224; | |
| 6 | pub const Sha256 = sha2.Sha256; | |
| 7 | pub const Sha384 = sha2.Sha384; | |
| 8 | pub const Sha512 = sha2.Sha512; | |
| 9 | ||
| 10 | const blake2 = @import("blake2.zig"); | |
| 11 | pub const Blake2s224 = blake2.Blake2s224; | |
| 12 | pub const Blake2s256 = blake2.Blake2s256; | |
| 13 | pub const Blake2b384 = blake2.Blake2b384; | |
| 14 | pub const Blake2b512 = blake2.Blake2b512; | |
| 15 | ||
| 16 | test "crypto" { | |
| 17 | _ = @import("md5.zig"); | |
| 18 | _ = @import("sha1.zig"); | |
| 19 | _ = @import("sha2.zig"); | |
| 20 | _ = @import("blake2.zig"); | |
| 21 | } |
std/crypto/md5.zig created+260| ... | ... | @@ -0,0 +1,260 @@ |
| 1 | const mem = @import("../mem.zig"); | |
| 2 | const math = @import("../math/index.zig"); | |
| 3 | const endian = @import("../endian.zig"); | |
| 4 | const builtin = @import("builtin"); | |
| 5 | const debug = @import("../debug/index.zig"); | |
| 6 | const fmt = @import("../fmt/index.zig"); | |
| 7 | ||
| 8 | const RoundParam = struct { | |
| 9 | a: usize, b: usize, c: usize, d: usize, | |
| 10 | k: usize, s: u32, t: u32 | |
| 11 | }; | |
| 12 | ||
| 13 | fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) -> RoundParam { | |
| 14 | return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t }; | |
| 15 | } | |
| 16 | ||
| 17 | /// const hash1 = Md5.hash("my input"); | |
| 18 | /// | |
| 19 | /// const hasher = Md5.init(); | |
| 20 | /// hasher.update("my "); | |
| 21 | /// hasher.update("input"); | |
| 22 | /// const hash2 = hasher.final(); | |
| 23 | pub const Md5 = struct { | |
| 24 | const Self = this; | |
| 25 | ||
| 26 | s: [4]u32, | |
| 27 | // Streaming Cache | |
| 28 | buf: [64]u8, | |
| 29 | buf_len: u8, | |
| 30 | total_len: u64, | |
| 31 | ||
| 32 | pub fn init() -> Self { | |
| 33 | var d: Self = undefined; | |
| 34 | d.reset(); | |
| 35 | return d; | |
| 36 | } | |
| 37 | ||
| 38 | pub fn reset(d: &Self) { | |
| 39 | d.s[0] = 0x67452301; | |
| 40 | d.s[1] = 0xEFCDAB89; | |
| 41 | d.s[2] = 0x98BADCFE; | |
| 42 | d.s[3] = 0x10325476; | |
| 43 | d.buf_len = 0; | |
| 44 | d.total_len = 0; | |
| 45 | } | |
| 46 | ||
| 47 | pub fn hash(b: []const u8, out: []u8) { | |
| 48 | var d = Md5.init(); | |
| 49 | d.update(b); | |
| 50 | d.final(out); | |
| 51 | } | |
| 52 | ||
| 53 | pub fn update(d: &Self, b: []const u8) { | |
| 54 | var off: usize = 0; | |
| 55 | ||
| 56 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 57 | if (d.buf_len != 0 and d.buf_len + b.len > 64) { | |
| 58 | off += 64 - d.buf_len; | |
| 59 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 60 | ||
| 61 | d.round(d.buf[0..]); | |
| 62 | d.buf_len = 0; | |
| 63 | } | |
| 64 | ||
| 65 | // Full middle blocks. | |
| 66 | while (off + 64 < b.len) : (off += 64) { | |
| 67 | d.round(b[off..off + 64]); | |
| 68 | } | |
| 69 | ||
| 70 | // Copy any remainder for next pass. | |
| 71 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 72 | d.buf_len += u8(b[off..].len); | |
| 73 | ||
| 74 | // Md5 uses the bottom 64-bits for length padding | |
| 75 | d.total_len +%= b.len; | |
| 76 | } | |
| 77 | ||
| 78 | pub fn final(d: &Self, out: []u8) { | |
| 79 | debug.assert(out.len >= 16); | |
| 80 | ||
| 81 | // The buffer here will never be completely full. | |
| 82 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 83 | ||
| 84 | // Append padding bits. | |
| 85 | d.buf[d.buf_len] = 0x80; | |
| 86 | d.buf_len += 1; | |
| 87 | ||
| 88 | // > 448 mod 512 so need to add an extra round to wrap around. | |
| 89 | if (64 - d.buf_len < 8) { | |
| 90 | d.round(d.buf[0..]); | |
| 91 | mem.set(u8, d.buf[0..], 0); | |
| 92 | } | |
| 93 | ||
| 94 | // Append message length. | |
| 95 | var i: usize = 1; | |
| 96 | var len = d.total_len >> 5; | |
| 97 | d.buf[56] = u8(d.total_len & 0x1f) << 3; | |
| 98 | while (i < 8) : (i += 1) { | |
| 99 | d.buf[56 + i] = u8(len & 0xff); | |
| 100 | len >>= 8; | |
| 101 | } | |
| 102 | ||
| 103 | d.round(d.buf[0..]); | |
| 104 | ||
| 105 | for (d.s) |s, j| { | |
| 106 | mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little); | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| 110 | fn round(d: &Self, b: []const u8) { | |
| 111 | debug.assert(b.len == 64); | |
| 112 | ||
| 113 | var s: [16]u32 = undefined; | |
| 114 | ||
| 115 | // ERROR: cannot unroll this at comptime | |
| 116 | var i: usize = 0; | |
| 117 | while (i < 16) : (i += 1) { | |
| 118 | // NOTE: Performing or's separately improves perf by ~10% | |
| 119 | s[i] = 0; | |
| 120 | s[i] |= u32(b[i*4+0]); | |
| 121 | s[i] |= u32(b[i*4+1]) << 8; | |
| 122 | s[i] |= u32(b[i*4+2]) << 16; | |
| 123 | s[i] |= u32(b[i*4+3]) << 24; | |
| 124 | } | |
| 125 | ||
| 126 | var v: [4]u32 = []u32 { | |
| 127 | d.s[0], d.s[1], d.s[2], d.s[3], | |
| 128 | }; | |
| 129 | ||
| 130 | const round0 = comptime []RoundParam { | |
| 131 | Rp(0, 1, 2, 3, 0, 7, 0xD76AA478), | |
| 132 | Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756), | |
| 133 | Rp(2, 3, 0, 1, 2, 17, 0x242070DB), | |
| 134 | Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE), | |
| 135 | Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF), | |
| 136 | Rp(3, 0, 1, 2, 5, 12, 0x4787C62A), | |
| 137 | Rp(2, 3, 0, 1, 6, 17, 0xA8304613), | |
| 138 | Rp(1, 2, 3, 0, 7, 22, 0xFD469501), | |
| 139 | Rp(0, 1, 2, 3, 8, 7, 0x698098D8), | |
| 140 | Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF), | |
| 141 | Rp(2, 3, 0, 1, 10, 17, 0xFFFF5BB1), | |
| 142 | Rp(1, 2, 3, 0, 11, 22, 0x895CD7BE), | |
| 143 | Rp(0, 1, 2, 3, 12, 7, 0x6B901122), | |
| 144 | Rp(3, 0, 1, 2, 13, 12, 0xFD987193), | |
| 145 | Rp(2, 3, 0, 1, 14, 17, 0xA679438E), | |
| 146 | Rp(1, 2, 3, 0, 15, 22, 0x49B40821), | |
| 147 | }; | |
| 148 | inline for (round0) |r| { | |
| 149 | v[r.a] = v[r.a] +% (v[r.d] ^ (v[r.b] & (v[r.c] ^ v[r.d]))) +% r.t +% s[r.k]; | |
| 150 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 151 | } | |
| 152 | ||
| 153 | const round1 = comptime []RoundParam { | |
| 154 | Rp(0, 1, 2, 3, 1, 5, 0xF61E2562), | |
| 155 | Rp(3, 0, 1, 2, 6, 9, 0xC040B340), | |
| 156 | Rp(2, 3, 0, 1, 11, 14, 0x265E5A51), | |
| 157 | Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA), | |
| 158 | Rp(0, 1, 2, 3, 5, 5, 0xD62F105D), | |
| 159 | Rp(3, 0, 1, 2, 10, 9, 0x02441453), | |
| 160 | Rp(2, 3, 0, 1, 15, 14, 0xD8A1E681), | |
| 161 | Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8), | |
| 162 | Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6), | |
| 163 | Rp(3, 0, 1, 2, 14, 9, 0xC33707D6), | |
| 164 | Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87), | |
| 165 | Rp(1, 2, 3, 0, 8, 20, 0x455A14ED), | |
| 166 | Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905), | |
| 167 | Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8), | |
| 168 | Rp(2, 3, 0, 1, 7, 14, 0x676F02D9), | |
| 169 | Rp(1, 2, 3, 0, 12, 20, 0x8D2A4C8A), | |
| 170 | }; | |
| 171 | inline for (round1) |r| { | |
| 172 | v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.d] & (v[r.b] ^ v[r.c]))) +% r.t +% s[r.k]; | |
| 173 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 174 | } | |
| 175 | ||
| 176 | const round2 = comptime []RoundParam { | |
| 177 | Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942), | |
| 178 | Rp(3, 0, 1, 2, 8, 11, 0x8771F681), | |
| 179 | Rp(2, 3, 0, 1, 11, 16, 0x6D9D6122), | |
| 180 | Rp(1, 2, 3, 0, 14, 23, 0xFDE5380C), | |
| 181 | Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44), | |
| 182 | Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9), | |
| 183 | Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60), | |
| 184 | Rp(1, 2, 3, 0, 10, 23, 0xBEBFBC70), | |
| 185 | Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6), | |
| 186 | Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA), | |
| 187 | Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085), | |
| 188 | Rp(1, 2, 3, 0, 6, 23, 0x04881D05), | |
| 189 | Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039), | |
| 190 | Rp(3, 0, 1, 2, 12, 11, 0xE6DB99E5), | |
| 191 | Rp(2, 3, 0, 1, 15, 16, 0x1FA27CF8), | |
| 192 | Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665), | |
| 193 | }; | |
| 194 | inline for (round2) |r| { | |
| 195 | v[r.a] = v[r.a] +% (v[r.b] ^ v[r.c] ^ v[r.d]) +% r.t +% s[r.k]; | |
| 196 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 197 | } | |
| 198 | ||
| 199 | const round3 = comptime []RoundParam { | |
| 200 | Rp(0, 1, 2, 3, 0, 6, 0xF4292244), | |
| 201 | Rp(3, 0, 1, 2, 7, 10, 0x432AFF97), | |
| 202 | Rp(2, 3, 0, 1, 14, 15, 0xAB9423A7), | |
| 203 | Rp(1, 2, 3, 0, 5, 21, 0xFC93A039), | |
| 204 | Rp(0, 1, 2, 3, 12, 6, 0x655B59C3), | |
| 205 | Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92), | |
| 206 | Rp(2, 3, 0, 1, 10, 15, 0xFFEFF47D), | |
| 207 | Rp(1, 2, 3, 0, 1, 21, 0x85845DD1), | |
| 208 | Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F), | |
| 209 | Rp(3, 0, 1, 2, 15, 10, 0xFE2CE6E0), | |
| 210 | Rp(2, 3, 0, 1, 6, 15, 0xA3014314), | |
| 211 | Rp(1, 2, 3, 0, 13, 21, 0x4E0811A1), | |
| 212 | Rp(0, 1, 2, 3, 4, 6, 0xF7537E82), | |
| 213 | Rp(3, 0, 1, 2, 11, 10, 0xBD3AF235), | |
| 214 | Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB), | |
| 215 | Rp(1, 2, 3, 0, 9, 21, 0xEB86D391), | |
| 216 | }; | |
| 217 | inline for (round3) |r| { | |
| 218 | v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.b] | ~v[r.d])) +% r.t +% s[r.k]; | |
| 219 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 220 | } | |
| 221 | ||
| 222 | d.s[0] +%= v[0]; | |
| 223 | d.s[1] +%= v[1]; | |
| 224 | d.s[2] +%= v[2]; | |
| 225 | d.s[3] +%= v[3]; | |
| 226 | } | |
| 227 | }; | |
| 228 | ||
| 229 | const htest = @import("test.zig"); | |
| 230 | ||
| 231 | test "md5 single" { | |
| 232 | htest.assertEqualHash(Md5, "d41d8cd98f00b204e9800998ecf8427e", ""); | |
| 233 | htest.assertEqualHash(Md5, "0cc175b9c0f1b6a831c399e269772661", "a"); | |
| 234 | htest.assertEqualHash(Md5, "900150983cd24fb0d6963f7d28e17f72", "abc"); | |
| 235 | htest.assertEqualHash(Md5, "f96b697d7cb7938d525a2f31aaf161d0", "message digest"); | |
| 236 | htest.assertEqualHash(Md5, "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"); | |
| 237 | htest.assertEqualHash(Md5, "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); | |
| 238 | htest.assertEqualHash(Md5, "57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"); | |
| 239 | } | |
| 240 | ||
| 241 | test "md5 streaming" { | |
| 242 | var h = Md5.init(); | |
| 243 | var out: [16]u8 = undefined; | |
| 244 | ||
| 245 | h.final(out[0..]); | |
| 246 | htest.assertEqual("d41d8cd98f00b204e9800998ecf8427e", out[0..]); | |
| 247 | ||
| 248 | h.reset(); | |
| 249 | h.update("abc"); | |
| 250 | h.final(out[0..]); | |
| 251 | htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]); | |
| 252 | ||
| 253 | h.reset(); | |
| 254 | h.update("a"); | |
| 255 | h.update("b"); | |
| 256 | h.update("c"); | |
| 257 | h.final(out[0..]); | |
| 258 | ||
| 259 | htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]); | |
| 260 | } |
std/crypto/sha1.zig created+284| ... | ... | @@ -0,0 +1,284 @@ |
| 1 | const mem = @import("../mem.zig"); | |
| 2 | const math = @import("../math/index.zig"); | |
| 3 | const endian = @import("../endian.zig"); | |
| 4 | const debug = @import("../debug/index.zig"); | |
| 5 | const builtin = @import("builtin"); | |
| 6 | ||
| 7 | pub const u160 = @IntType(false, 160); | |
| 8 | ||
| 9 | const RoundParam = struct { | |
| 10 | a: usize, b: usize, c: usize, d: usize, e: usize, i: u32, | |
| 11 | }; | |
| 12 | ||
| 13 | fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) -> RoundParam { | |
| 14 | return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i }; | |
| 15 | } | |
| 16 | ||
| 17 | pub const Sha1 = struct { | |
| 18 | const Self = this; | |
| 19 | ||
| 20 | s: [5]u32, | |
| 21 | // Streaming Cache | |
| 22 | buf: [64]u8, | |
| 23 | buf_len: u8, | |
| 24 | total_len: u64, | |
| 25 | ||
| 26 | pub fn init() -> Self { | |
| 27 | var d: Self = undefined; | |
| 28 | d.reset(); | |
| 29 | return d; | |
| 30 | } | |
| 31 | ||
| 32 | pub fn reset(d: &Self) { | |
| 33 | d.s[0] = 0x67452301; | |
| 34 | d.s[1] = 0xEFCDAB89; | |
| 35 | d.s[2] = 0x98BADCFE; | |
| 36 | d.s[3] = 0x10325476; | |
| 37 | d.s[4] = 0xC3D2E1F0; | |
| 38 | d.buf_len = 0; | |
| 39 | d.total_len = 0; | |
| 40 | } | |
| 41 | ||
| 42 | pub fn hash(b: []const u8, out: []u8) { | |
| 43 | var d = Sha1.init(); | |
| 44 | d.update(b); | |
| 45 | d.final(out); | |
| 46 | } | |
| 47 | ||
| 48 | pub fn update(d: &Self, b: []const u8) { | |
| 49 | var off: usize = 0; | |
| 50 | ||
| 51 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 52 | if (d.buf_len != 0 and d.buf_len + b.len > 64) { | |
| 53 | off += 64 - d.buf_len; | |
| 54 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 55 | ||
| 56 | d.round(d.buf[0..]); | |
| 57 | d.buf_len = 0; | |
| 58 | } | |
| 59 | ||
| 60 | // Full middle blocks. | |
| 61 | while (off + 64 < b.len) : (off += 64) { | |
| 62 | d.round(b[off..off + 64]); | |
| 63 | } | |
| 64 | ||
| 65 | // Copy any remainder for next pass. | |
| 66 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 67 | d.buf_len += u8(b[off..].len); | |
| 68 | ||
| 69 | d.total_len += b.len; | |
| 70 | } | |
| 71 | ||
| 72 | pub fn final(d: &Self, out: []u8) { | |
| 73 | debug.assert(out.len >= 20); | |
| 74 | ||
| 75 | // The buffer here will never be completely full. | |
| 76 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 77 | ||
| 78 | // Append padding bits. | |
| 79 | d.buf[d.buf_len] = 0x80; | |
| 80 | d.buf_len += 1; | |
| 81 | ||
| 82 | // > 448 mod 512 so need to add an extra round to wrap around. | |
| 83 | if (64 - d.buf_len < 8) { | |
| 84 | d.round(d.buf[0..]); | |
| 85 | mem.set(u8, d.buf[0..], 0); | |
| 86 | } | |
| 87 | ||
| 88 | // Append message length. | |
| 89 | var i: usize = 1; | |
| 90 | var len = d.total_len >> 5; | |
| 91 | d.buf[63] = u8(d.total_len & 0x1f) << 3; | |
| 92 | while (i < 8) : (i += 1) { | |
| 93 | d.buf[63 - i] = u8(len & 0xff); | |
| 94 | len >>= 8; | |
| 95 | } | |
| 96 | ||
| 97 | d.round(d.buf[0..]); | |
| 98 | ||
| 99 | for (d.s) |s, j| { | |
| 100 | mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big); | |
| 101 | } | |
| 102 | } | |
| 103 | ||
| 104 | fn round(d: &Self, b: []const u8) { | |
| 105 | debug.assert(b.len == 64); | |
| 106 | ||
| 107 | var s: [16]u32 = undefined; | |
| 108 | ||
| 109 | var v: [5]u32 = []u32 { | |
| 110 | d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], | |
| 111 | }; | |
| 112 | ||
| 113 | const round0a = comptime []RoundParam { | |
| 114 | Rp(0, 1, 2, 3, 4, 0), | |
| 115 | Rp(4, 0, 1, 2, 3, 1), | |
| 116 | Rp(3, 4, 0, 1, 2, 2), | |
| 117 | Rp(2, 3, 4, 0, 1, 3), | |
| 118 | Rp(1, 2, 3, 4, 0, 4), | |
| 119 | Rp(0, 1, 2, 3, 4, 5), | |
| 120 | Rp(4, 0, 1, 2, 3, 6), | |
| 121 | Rp(3, 4, 0, 1, 2, 7), | |
| 122 | Rp(2, 3, 4, 0, 1, 8), | |
| 123 | Rp(1, 2, 3, 4, 0, 9), | |
| 124 | Rp(0, 1, 2, 3, 4, 10), | |
| 125 | Rp(4, 0, 1, 2, 3, 11), | |
| 126 | Rp(3, 4, 0, 1, 2, 12), | |
| 127 | Rp(2, 3, 4, 0, 1, 13), | |
| 128 | Rp(1, 2, 3, 4, 0, 14), | |
| 129 | Rp(0, 1, 2, 3, 4, 15), | |
| 130 | }; | |
| 131 | inline for (round0a) |r| { | |
| 132 | s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | | |
| 133 | (u32(b[r.i * 4 + 1]) << 16) | | |
| 134 | (u32(b[r.i * 4 + 2]) << 8) | | |
| 135 | (u32(b[r.i * 4 + 3]) << 0); | |
| 136 | ||
| 137 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] | |
| 138 | +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); | |
| 139 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 140 | } | |
| 141 | ||
| 142 | const round0b = comptime []RoundParam { | |
| 143 | Rp(4, 0, 1, 2, 3, 16), | |
| 144 | Rp(3, 4, 0, 1, 2, 17), | |
| 145 | Rp(2, 3, 4, 0, 1, 18), | |
| 146 | Rp(1, 2, 3, 4, 0, 19), | |
| 147 | }; | |
| 148 | inline for (round0b) |r| { | |
| 149 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 150 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 151 | ||
| 152 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] | |
| 153 | +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); | |
| 154 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 155 | } | |
| 156 | ||
| 157 | const round1 = comptime []RoundParam { | |
| 158 | Rp(0, 1, 2, 3, 4, 20), | |
| 159 | Rp(4, 0, 1, 2, 3, 21), | |
| 160 | Rp(3, 4, 0, 1, 2, 22), | |
| 161 | Rp(2, 3, 4, 0, 1, 23), | |
| 162 | Rp(1, 2, 3, 4, 0, 24), | |
| 163 | Rp(0, 1, 2, 3, 4, 25), | |
| 164 | Rp(4, 0, 1, 2, 3, 26), | |
| 165 | Rp(3, 4, 0, 1, 2, 27), | |
| 166 | Rp(2, 3, 4, 0, 1, 28), | |
| 167 | Rp(1, 2, 3, 4, 0, 29), | |
| 168 | Rp(0, 1, 2, 3, 4, 30), | |
| 169 | Rp(4, 0, 1, 2, 3, 31), | |
| 170 | Rp(3, 4, 0, 1, 2, 32), | |
| 171 | Rp(2, 3, 4, 0, 1, 33), | |
| 172 | Rp(1, 2, 3, 4, 0, 34), | |
| 173 | Rp(0, 1, 2, 3, 4, 35), | |
| 174 | Rp(4, 0, 1, 2, 3, 36), | |
| 175 | Rp(3, 4, 0, 1, 2, 37), | |
| 176 | Rp(2, 3, 4, 0, 1, 38), | |
| 177 | Rp(1, 2, 3, 4, 0, 39), | |
| 178 | }; | |
| 179 | inline for (round1) |r| { | |
| 180 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 181 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 182 | ||
| 183 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] | |
| 184 | +% (v[r.b] ^ v[r.c] ^ v[r.d]); | |
| 185 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 186 | } | |
| 187 | ||
| 188 | const round2 = comptime []RoundParam { | |
| 189 | Rp(0, 1, 2, 3, 4, 40), | |
| 190 | Rp(4, 0, 1, 2, 3, 41), | |
| 191 | Rp(3, 4, 0, 1, 2, 42), | |
| 192 | Rp(2, 3, 4, 0, 1, 43), | |
| 193 | Rp(1, 2, 3, 4, 0, 44), | |
| 194 | Rp(0, 1, 2, 3, 4, 45), | |
| 195 | Rp(4, 0, 1, 2, 3, 46), | |
| 196 | Rp(3, 4, 0, 1, 2, 47), | |
| 197 | Rp(2, 3, 4, 0, 1, 48), | |
| 198 | Rp(1, 2, 3, 4, 0, 49), | |
| 199 | Rp(0, 1, 2, 3, 4, 50), | |
| 200 | Rp(4, 0, 1, 2, 3, 51), | |
| 201 | Rp(3, 4, 0, 1, 2, 52), | |
| 202 | Rp(2, 3, 4, 0, 1, 53), | |
| 203 | Rp(1, 2, 3, 4, 0, 54), | |
| 204 | Rp(0, 1, 2, 3, 4, 55), | |
| 205 | Rp(4, 0, 1, 2, 3, 56), | |
| 206 | Rp(3, 4, 0, 1, 2, 57), | |
| 207 | Rp(2, 3, 4, 0, 1, 58), | |
| 208 | Rp(1, 2, 3, 4, 0, 59), | |
| 209 | }; | |
| 210 | inline for (round2) |r| { | |
| 211 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 212 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 213 | ||
| 214 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] | |
| 215 | +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); | |
| 216 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 217 | } | |
| 218 | ||
| 219 | const round3 = comptime []RoundParam { | |
| 220 | Rp(0, 1, 2, 3, 4, 60), | |
| 221 | Rp(4, 0, 1, 2, 3, 61), | |
| 222 | Rp(3, 4, 0, 1, 2, 62), | |
| 223 | Rp(2, 3, 4, 0, 1, 63), | |
| 224 | Rp(1, 2, 3, 4, 0, 64), | |
| 225 | Rp(0, 1, 2, 3, 4, 65), | |
| 226 | Rp(4, 0, 1, 2, 3, 66), | |
| 227 | Rp(3, 4, 0, 1, 2, 67), | |
| 228 | Rp(2, 3, 4, 0, 1, 68), | |
| 229 | Rp(1, 2, 3, 4, 0, 69), | |
| 230 | Rp(0, 1, 2, 3, 4, 70), | |
| 231 | Rp(4, 0, 1, 2, 3, 71), | |
| 232 | Rp(3, 4, 0, 1, 2, 72), | |
| 233 | Rp(2, 3, 4, 0, 1, 73), | |
| 234 | Rp(1, 2, 3, 4, 0, 74), | |
| 235 | Rp(0, 1, 2, 3, 4, 75), | |
| 236 | Rp(4, 0, 1, 2, 3, 76), | |
| 237 | Rp(3, 4, 0, 1, 2, 77), | |
| 238 | Rp(2, 3, 4, 0, 1, 78), | |
| 239 | Rp(1, 2, 3, 4, 0, 79), | |
| 240 | }; | |
| 241 | inline for (round3) |r| { | |
| 242 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 243 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 244 | ||
| 245 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] | |
| 246 | +% (v[r.b] ^ v[r.c] ^ v[r.d]); | |
| 247 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 248 | } | |
| 249 | ||
| 250 | d.s[0] +%= v[0]; | |
| 251 | d.s[1] +%= v[1]; | |
| 252 | d.s[2] +%= v[2]; | |
| 253 | d.s[3] +%= v[3]; | |
| 254 | d.s[4] +%= v[4]; | |
| 255 | } | |
| 256 | }; | |
| 257 | ||
| 258 | const htest = @import("test.zig"); | |
| 259 | ||
| 260 | test "sha1 single" { | |
| 261 | htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""); | |
| 262 | htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc"); | |
| 263 | htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); | |
| 264 | } | |
| 265 | ||
| 266 | test "sha1 streaming" { | |
| 267 | var h = Sha1.init(); | |
| 268 | var out: [20]u8 = undefined; | |
| 269 | ||
| 270 | h.final(out[0..]); | |
| 271 | htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]); | |
| 272 | ||
| 273 | h.reset(); | |
| 274 | h.update("abc"); | |
| 275 | h.final(out[0..]); | |
| 276 | htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); | |
| 277 | ||
| 278 | h.reset(); | |
| 279 | h.update("a"); | |
| 280 | h.update("b"); | |
| 281 | h.update("c"); | |
| 282 | h.final(out[0..]); | |
| 283 | htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); | |
| 284 | } |
std/crypto/sha2.zig created+670| ... | ... | @@ -0,0 +1,670 @@ |
| 1 | const mem = @import("../mem.zig"); | |
| 2 | const math = @import("../math/index.zig"); | |
| 3 | const endian = @import("../endian.zig"); | |
| 4 | const debug = @import("../debug/index.zig"); | |
| 5 | const builtin = @import("builtin"); | |
| 6 | const htest = @import("test.zig"); | |
| 7 | ||
| 8 | ///////////////////// | |
| 9 | // Sha224 + Sha256 | |
| 10 | ||
| 11 | const RoundParam256 = struct { | |
| 12 | a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, | |
| 13 | i: usize, k: u32, | |
| 14 | }; | |
| 15 | ||
| 16 | fn Rp256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u32) -> RoundParam256 { | |
| 17 | return RoundParam256 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k }; | |
| 18 | } | |
| 19 | ||
| 20 | const Sha2Params32 = struct { | |
| 21 | iv0: u32, | |
| 22 | iv1: u32, | |
| 23 | iv2: u32, | |
| 24 | iv3: u32, | |
| 25 | iv4: u32, | |
| 26 | iv5: u32, | |
| 27 | iv6: u32, | |
| 28 | iv7: u32, | |
| 29 | out_len: usize, | |
| 30 | }; | |
| 31 | ||
| 32 | const Sha224Params = Sha2Params32 { | |
| 33 | .iv0 = 0xC1059ED8, | |
| 34 | .iv1 = 0x367CD507, | |
| 35 | .iv2 = 0x3070DD17, | |
| 36 | .iv3 = 0xF70E5939, | |
| 37 | .iv4 = 0xFFC00B31, | |
| 38 | .iv5 = 0x68581511, | |
| 39 | .iv6 = 0x64F98FA7, | |
| 40 | .iv7 = 0xBEFA4FA4, | |
| 41 | .out_len = 224, | |
| 42 | }; | |
| 43 | ||
| 44 | const Sha256Params = Sha2Params32 { | |
| 45 | .iv0 = 0x6A09E667, | |
| 46 | .iv1 = 0xBB67AE85, | |
| 47 | .iv2 = 0x3C6EF372, | |
| 48 | .iv3 = 0xA54FF53A, | |
| 49 | .iv4 = 0x510E527F, | |
| 50 | .iv5 = 0x9B05688C, | |
| 51 | .iv6 = 0x1F83D9AB, | |
| 52 | .iv7 = 0x5BE0CD19, | |
| 53 | .out_len = 256, | |
| 54 | }; | |
| 55 | ||
| 56 | pub const Sha224 = Sha2_32(Sha224Params); | |
| 57 | pub const Sha256 = Sha2_32(Sha256Params); | |
| 58 | ||
| 59 | fn Sha2_32(comptime params: Sha2Params32) -> type { return struct { | |
| 60 | const Self = this; | |
| 61 | ||
| 62 | s: [8]u32, | |
| 63 | // Streaming Cache | |
| 64 | buf: [64]u8, | |
| 65 | buf_len: u8, | |
| 66 | total_len: u64, | |
| 67 | ||
| 68 | pub fn init() -> Self { | |
| 69 | var d: Self = undefined; | |
| 70 | d.reset(); | |
| 71 | return d; | |
| 72 | } | |
| 73 | ||
| 74 | pub fn reset(d: &Self) { | |
| 75 | d.s[0] = params.iv0; | |
| 76 | d.s[1] = params.iv1; | |
| 77 | d.s[2] = params.iv2; | |
| 78 | d.s[3] = params.iv3; | |
| 79 | d.s[4] = params.iv4; | |
| 80 | d.s[5] = params.iv5; | |
| 81 | d.s[6] = params.iv6; | |
| 82 | d.s[7] = params.iv7; | |
| 83 | d.buf_len = 0; | |
| 84 | d.total_len = 0; | |
| 85 | } | |
| 86 | ||
| 87 | pub fn hash(b: []const u8, out: []u8) { | |
| 88 | var d = Self.init(); | |
| 89 | d.update(b); | |
| 90 | d.final(out); | |
| 91 | } | |
| 92 | ||
| 93 | pub fn update(d: &Self, b: []const u8) { | |
| 94 | var off: usize = 0; | |
| 95 | ||
| 96 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 97 | if (d.buf_len != 0 and d.buf_len + b.len > 64) { | |
| 98 | off += 64 - d.buf_len; | |
| 99 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 100 | ||
| 101 | d.round(d.buf[0..]); | |
| 102 | d.buf_len = 0; | |
| 103 | } | |
| 104 | ||
| 105 | // Full middle blocks. | |
| 106 | while (off + 64 < b.len) : (off += 64) { | |
| 107 | d.round(b[off..off + 64]); | |
| 108 | } | |
| 109 | ||
| 110 | // Copy any remainder for next pass. | |
| 111 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 112 | d.buf_len += u8(b[off..].len); | |
| 113 | ||
| 114 | d.total_len += b.len; | |
| 115 | } | |
| 116 | ||
| 117 | pub fn final(d: &Self, out: []u8) { | |
| 118 | debug.assert(out.len >= params.out_len / 8); | |
| 119 | ||
| 120 | // The buffer here will never be completely full. | |
| 121 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 122 | ||
| 123 | // Append padding bits. | |
| 124 | d.buf[d.buf_len] = 0x80; | |
| 125 | d.buf_len += 1; | |
| 126 | ||
| 127 | // > 448 mod 512 so need to add an extra round to wrap around. | |
| 128 | if (64 - d.buf_len < 8) { | |
| 129 | d.round(d.buf[0..]); | |
| 130 | mem.set(u8, d.buf[0..], 0); | |
| 131 | } | |
| 132 | ||
| 133 | // Append message length. | |
| 134 | var i: usize = 1; | |
| 135 | var len = d.total_len >> 5; | |
| 136 | d.buf[63] = u8(d.total_len & 0x1f) << 3; | |
| 137 | while (i < 8) : (i += 1) { | |
| 138 | d.buf[63 - i] = u8(len & 0xff); | |
| 139 | len >>= 8; | |
| 140 | } | |
| 141 | ||
| 142 | d.round(d.buf[0..]); | |
| 143 | ||
| 144 | // May truncate for possible 224 output | |
| 145 | const rr = d.s[0 .. params.out_len / 32]; | |
| 146 | ||
| 147 | for (rr) |s, j| { | |
| 148 | mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big); | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | fn round(d: &Self, b: []const u8) { | |
| 153 | debug.assert(b.len == 64); | |
| 154 | ||
| 155 | var s: [64]u32 = undefined; | |
| 156 | ||
| 157 | // ERROR: Cannot unroll at compile-time. | |
| 158 | var i: usize = 0; | |
| 159 | while (i < 16) : (i += 1) { | |
| 160 | s[i] = 0; | |
| 161 | s[i] |= u32(b[i*4+0]) << 24; | |
| 162 | s[i] |= u32(b[i*4+1]) << 16; | |
| 163 | s[i] |= u32(b[i*4+2]) << 8; | |
| 164 | s[i] |= u32(b[i*4+3]) << 0; | |
| 165 | } | |
| 166 | while (i < 64) : (i += 1) { | |
| 167 | s[i] = | |
| 168 | s[i-16] +% s[i-7] +% | |
| 169 | (math.rotr(u32, s[i-15], u32(7)) ^ math.rotr(u32, s[i-15], u32(18)) ^ (s[i-15] >> 3)) +% | |
| 170 | (math.rotr(u32, s[i-2], u32(17)) ^ math.rotr(u32, s[i-2], u32(19)) ^ (s[i-2] >> 10)); | |
| 171 | } | |
| 172 | ||
| 173 | var v: [8]u32 = []u32 { | |
| 174 | d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7], | |
| 175 | }; | |
| 176 | ||
| 177 | const round0 = comptime []RoundParam256 { | |
| 178 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98), | |
| 179 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x71374491), | |
| 180 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCF), | |
| 181 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA5), | |
| 182 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25B), | |
| 183 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1), | |
| 184 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4), | |
| 185 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5), | |
| 186 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98), | |
| 187 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B01), | |
| 188 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE), | |
| 189 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3), | |
| 190 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74), | |
| 191 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE), | |
| 192 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A7), | |
| 193 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174), | |
| 194 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C1), | |
| 195 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786), | |
| 196 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC6), | |
| 197 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC), | |
| 198 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F), | |
| 199 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA), | |
| 200 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DC), | |
| 201 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA), | |
| 202 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152), | |
| 203 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D), | |
| 204 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C8), | |
| 205 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7), | |
| 206 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF3), | |
| 207 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147), | |
| 208 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351), | |
| 209 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x14292967), | |
| 210 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A85), | |
| 211 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B2138), | |
| 212 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC), | |
| 213 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D13), | |
| 214 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A7354), | |
| 215 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB), | |
| 216 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E), | |
| 217 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C85), | |
| 218 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A1), | |
| 219 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664B), | |
| 220 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70), | |
| 221 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A3), | |
| 222 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819), | |
| 223 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD6990624), | |
| 224 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E3585), | |
| 225 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA070), | |
| 226 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116), | |
| 227 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C08), | |
| 228 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774C), | |
| 229 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5), | |
| 230 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3), | |
| 231 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4A), | |
| 232 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F), | |
| 233 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3), | |
| 234 | Rp256(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE), | |
| 235 | Rp256(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F), | |
| 236 | Rp256(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814), | |
| 237 | Rp256(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC70208), | |
| 238 | Rp256(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA), | |
| 239 | Rp256(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEB), | |
| 240 | Rp256(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7), | |
| 241 | Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2), | |
| 242 | }; | |
| 243 | inline for (round0) |r| { | |
| 244 | v[r.h] = | |
| 245 | v[r.h] +% | |
| 246 | (math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +% | |
| 247 | (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% | |
| 248 | r.k +% s[r.i]; | |
| 249 | ||
| 250 | v[r.d] = v[r.d] +% v[r.h]; | |
| 251 | ||
| 252 | v[r.h] = | |
| 253 | v[r.h] +% | |
| 254 | (math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +% | |
| 255 | ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); | |
| 256 | } | |
| 257 | ||
| 258 | d.s[0] +%= v[0]; | |
| 259 | d.s[1] +%= v[1]; | |
| 260 | d.s[2] +%= v[2]; | |
| 261 | d.s[3] +%= v[3]; | |
| 262 | d.s[4] +%= v[4]; | |
| 263 | d.s[5] +%= v[5]; | |
| 264 | d.s[6] +%= v[6]; | |
| 265 | d.s[7] +%= v[7]; | |
| 266 | } | |
| 267 | };} | |
| 268 | ||
| 269 | test "sha224 single" { | |
| 270 | htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""); | |
| 271 | htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"); | |
| 272 | htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); | |
| 273 | } | |
| 274 | ||
| 275 | test "sha224 streaming" { | |
| 276 | var h = Sha224.init(); | |
| 277 | var out: [28]u8 = undefined; | |
| 278 | ||
| 279 | h.final(out[0..]); | |
| 280 | htest.assertEqual("d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", out[0..]); | |
| 281 | ||
| 282 | h.reset(); | |
| 283 | h.update("abc"); | |
| 284 | h.final(out[0..]); | |
| 285 | htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]); | |
| 286 | ||
| 287 | h.reset(); | |
| 288 | h.update("a"); | |
| 289 | h.update("b"); | |
| 290 | h.update("c"); | |
| 291 | h.final(out[0..]); | |
| 292 | htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]); | |
| 293 | } | |
| 294 | ||
| 295 | test "sha256 single" { | |
| 296 | htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""); | |
| 297 | htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"); | |
| 298 | htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); | |
| 299 | } | |
| 300 | ||
| 301 | test "sha256 streaming" { | |
| 302 | var h = Sha256.init(); | |
| 303 | var out: [32]u8 = undefined; | |
| 304 | ||
| 305 | h.final(out[0..]); | |
| 306 | htest.assertEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", out[0..]); | |
| 307 | ||
| 308 | h.reset(); | |
| 309 | h.update("abc"); | |
| 310 | h.final(out[0..]); | |
| 311 | htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]); | |
| 312 | ||
| 313 | h.reset(); | |
| 314 | h.update("a"); | |
| 315 | h.update("b"); | |
| 316 | h.update("c"); | |
| 317 | h.final(out[0..]); | |
| 318 | htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]); | |
| 319 | } | |
| 320 | ||
| 321 | ||
| 322 | ///////////////////// | |
| 323 | // Sha384 + Sha512 | |
| 324 | ||
| 325 | const RoundParam512 = struct { | |
| 326 | a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, | |
| 327 | i: usize, k: u64, | |
| 328 | }; | |
| 329 | ||
| 330 | fn Rp512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) -> RoundParam512 { | |
| 331 | return RoundParam512 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k }; | |
| 332 | } | |
| 333 | ||
| 334 | const Sha2Params64 = struct { | |
| 335 | iv0: u64, | |
| 336 | iv1: u64, | |
| 337 | iv2: u64, | |
| 338 | iv3: u64, | |
| 339 | iv4: u64, | |
| 340 | iv5: u64, | |
| 341 | iv6: u64, | |
| 342 | iv7: u64, | |
| 343 | out_len: usize, | |
| 344 | }; | |
| 345 | ||
| 346 | const Sha384Params = Sha2Params64 { | |
| 347 | .iv0 = 0xCBBB9D5DC1059ED8, | |
| 348 | .iv1 = 0x629A292A367CD507, | |
| 349 | .iv2 = 0x9159015A3070DD17, | |
| 350 | .iv3 = 0x152FECD8F70E5939, | |
| 351 | .iv4 = 0x67332667FFC00B31, | |
| 352 | .iv5 = 0x8EB44A8768581511, | |
| 353 | .iv6 = 0xDB0C2E0D64F98FA7, | |
| 354 | .iv7 = 0x47B5481DBEFA4FA4, | |
| 355 | .out_len = 384, | |
| 356 | }; | |
| 357 | ||
| 358 | const Sha512Params = Sha2Params64 { | |
| 359 | .iv0 = 0x6A09E667F3BCC908, | |
| 360 | .iv1 = 0xBB67AE8584CAA73B, | |
| 361 | .iv2 = 0x3C6EF372FE94F82B, | |
| 362 | .iv3 = 0xA54FF53A5F1D36F1, | |
| 363 | .iv4 = 0x510E527FADE682D1, | |
| 364 | .iv5 = 0x9B05688C2B3E6C1F, | |
| 365 | .iv6 = 0x1F83D9ABFB41BD6B, | |
| 366 | .iv7 = 0x5BE0CD19137E2179, | |
| 367 | .out_len = 512 | |
| 368 | }; | |
| 369 | ||
| 370 | pub const Sha384 = Sha2_64(Sha384Params); | |
| 371 | pub const Sha512 = Sha2_64(Sha512Params); | |
| 372 | ||
| 373 | fn Sha2_64(comptime params: Sha2Params64) -> type { return struct { | |
| 374 | const Self = this; | |
| 375 | const u9 = @IntType(false, 9); | |
| 376 | ||
| 377 | s: [8]u64, | |
| 378 | // Streaming Cache | |
| 379 | buf: [128]u8, | |
| 380 | buf_len: u8, | |
| 381 | total_len: u128, | |
| 382 | ||
| 383 | pub fn init() -> Self { | |
| 384 | var d: Self = undefined; | |
| 385 | d.reset(); | |
| 386 | return d; | |
| 387 | } | |
| 388 | ||
| 389 | pub fn reset(d: &Self) { | |
| 390 | d.s[0] = params.iv0; | |
| 391 | d.s[1] = params.iv1; | |
| 392 | d.s[2] = params.iv2; | |
| 393 | d.s[3] = params.iv3; | |
| 394 | d.s[4] = params.iv4; | |
| 395 | d.s[5] = params.iv5; | |
| 396 | d.s[6] = params.iv6; | |
| 397 | d.s[7] = params.iv7; | |
| 398 | d.buf_len = 0; | |
| 399 | d.total_len = 0; | |
| 400 | } | |
| 401 | ||
| 402 | pub fn hash(b: []const u8, out: []u8) { | |
| 403 | var d = Self.init(); | |
| 404 | d.update(b); | |
| 405 | d.final(out); | |
| 406 | } | |
| 407 | ||
| 408 | pub fn update(d: &Self, b: []const u8) { | |
| 409 | var off: usize = 0; | |
| 410 | ||
| 411 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 412 | if (d.buf_len != 0 and d.buf_len + b.len > 128) { | |
| 413 | off += 128 - d.buf_len; | |
| 414 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 415 | ||
| 416 | d.round(d.buf[0..]); | |
| 417 | d.buf_len = 0; | |
| 418 | } | |
| 419 | ||
| 420 | // Full middle blocks. | |
| 421 | while (off + 128 < b.len) : (off += 128) { | |
| 422 | d.round(b[off..off + 128]); | |
| 423 | } | |
| 424 | ||
| 425 | // Copy any remainder for next pass. | |
| 426 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 427 | d.buf_len += u8(b[off..].len); | |
| 428 | ||
| 429 | d.total_len += b.len; | |
| 430 | } | |
| 431 | ||
| 432 | pub fn final(d: &Self, out: []u8) { | |
| 433 | debug.assert(out.len >= params.out_len / 8); | |
| 434 | ||
| 435 | // The buffer here will never be completely full. | |
| 436 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 437 | ||
| 438 | // Append padding bits. | |
| 439 | d.buf[d.buf_len] = 0x80; | |
| 440 | d.buf_len += 1; | |
| 441 | ||
| 442 | // > 896 mod 1024 so need to add an extra round to wrap around. | |
| 443 | if (128 - d.buf_len < 16) { | |
| 444 | d.round(d.buf[0..]); | |
| 445 | mem.set(u8, d.buf[0..], 0); | |
| 446 | } | |
| 447 | ||
| 448 | // Append message length. | |
| 449 | var i: usize = 1; | |
| 450 | var len = d.total_len >> 5; | |
| 451 | d.buf[127] = u8(d.total_len & 0x1f) << 3; | |
| 452 | while (i < 16) : (i += 1) { | |
| 453 | d.buf[127 - i] = u8(len & 0xff); | |
| 454 | len >>= 8; | |
| 455 | } | |
| 456 | ||
| 457 | d.round(d.buf[0..]); | |
| 458 | ||
| 459 | // May truncate for possible 384 output | |
| 460 | const rr = d.s[0 .. params.out_len / 64]; | |
| 461 | ||
| 462 | for (rr) |s, j| { | |
| 463 | mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Big); | |
| 464 | } | |
| 465 | } | |
| 466 | ||
| 467 | fn round(d: &Self, b: []const u8) { | |
| 468 | debug.assert(b.len == 128); | |
| 469 | ||
| 470 | var s: [80]u64 = undefined; | |
| 471 | ||
| 472 | // ERROR: Cannot unroll at compile-time. | |
| 473 | var i: usize = 0; | |
| 474 | while (i < 16) : (i += 1) { | |
| 475 | s[i] = 0; | |
| 476 | s[i] |= u64(b[i*8+0]) << 56; | |
| 477 | s[i] |= u64(b[i*8+1]) << 48; | |
| 478 | s[i] |= u64(b[i*8+2]) << 40; | |
| 479 | s[i] |= u64(b[i*8+3]) << 32; | |
| 480 | s[i] |= u64(b[i*8+4]) << 24; | |
| 481 | s[i] |= u64(b[i*8+5]) << 16; | |
| 482 | s[i] |= u64(b[i*8+6]) << 8; | |
| 483 | s[i] |= u64(b[i*8+7]) << 0; | |
| 484 | } | |
| 485 | while (i < 80) : (i += 1) { | |
| 486 | s[i] = | |
| 487 | s[i-16] +% s[i-7] +% | |
| 488 | (math.rotr(u64, s[i-15], u64(1)) ^ math.rotr(u64, s[i-15], u64(8)) ^ (s[i-15] >> 7)) +% | |
| 489 | (math.rotr(u64, s[i-2], u64(19)) ^ math.rotr(u64, s[i-2], u64(61)) ^ (s[i-2] >> 6)); | |
| 490 | } | |
| 491 | ||
| 492 | var v: [8]u64 = []u64 { | |
| 493 | d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7], | |
| 494 | }; | |
| 495 | ||
| 496 | const round0 = comptime []RoundParam512 { | |
| 497 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22), | |
| 498 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD), | |
| 499 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F), | |
| 500 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC), | |
| 501 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538), | |
| 502 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019), | |
| 503 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B), | |
| 504 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118), | |
| 505 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242), | |
| 506 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE), | |
| 507 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C), | |
| 508 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2), | |
| 509 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F), | |
| 510 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1), | |
| 511 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235), | |
| 512 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694), | |
| 513 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2), | |
| 514 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3), | |
| 515 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5), | |
| 516 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65), | |
| 517 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275), | |
| 518 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483), | |
| 519 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4), | |
| 520 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5), | |
| 521 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB), | |
| 522 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210), | |
| 523 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F), | |
| 524 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4), | |
| 525 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2), | |
| 526 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725), | |
| 527 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F), | |
| 528 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70), | |
| 529 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC), | |
| 530 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926), | |
| 531 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED), | |
| 532 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF), | |
| 533 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE), | |
| 534 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8), | |
| 535 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6), | |
| 536 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B), | |
| 537 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364), | |
| 538 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001), | |
| 539 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791), | |
| 540 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30), | |
| 541 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218), | |
| 542 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910), | |
| 543 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A), | |
| 544 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8), | |
| 545 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8), | |
| 546 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53), | |
| 547 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99), | |
| 548 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8), | |
| 549 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63), | |
| 550 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB), | |
| 551 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373), | |
| 552 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3), | |
| 553 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC), | |
| 554 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60), | |
| 555 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72), | |
| 556 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC), | |
| 557 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28), | |
| 558 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9), | |
| 559 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915), | |
| 560 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B), | |
| 561 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C), | |
| 562 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207), | |
| 563 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E), | |
| 564 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178), | |
| 565 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA), | |
| 566 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6), | |
| 567 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE), | |
| 568 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B), | |
| 569 | Rp512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84), | |
| 570 | Rp512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493), | |
| 571 | Rp512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC), | |
| 572 | Rp512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C), | |
| 573 | Rp512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6), | |
| 574 | Rp512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A), | |
| 575 | Rp512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC), | |
| 576 | Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817), | |
| 577 | }; | |
| 578 | inline for (round0) |r| { | |
| 579 | v[r.h] = | |
| 580 | v[r.h] +% | |
| 581 | (math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +% | |
| 582 | (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% | |
| 583 | r.k +% s[r.i]; | |
| 584 | ||
| 585 | v[r.d] = v[r.d] +% v[r.h]; | |
| 586 | ||
| 587 | v[r.h] = | |
| 588 | v[r.h] +% | |
| 589 | (math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +% | |
| 590 | ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); | |
| 591 | } | |
| 592 | ||
| 593 | d.s[0] +%= v[0]; | |
| 594 | d.s[1] +%= v[1]; | |
| 595 | d.s[2] +%= v[2]; | |
| 596 | d.s[3] +%= v[3]; | |
| 597 | d.s[4] +%= v[4]; | |
| 598 | d.s[5] +%= v[5]; | |
| 599 | d.s[6] +%= v[6]; | |
| 600 | d.s[7] +%= v[7]; | |
| 601 | } | |
| 602 | };} | |
| 603 | ||
| 604 | test "sha384 single" { | |
| 605 | const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; | |
| 606 | htest.assertEqualHash(Sha384, h1, ""); | |
| 607 | ||
| 608 | const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"; | |
| 609 | htest.assertEqualHash(Sha384, h2, "abc"); | |
| 610 | ||
| 611 | const h3 = "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039"; | |
| 612 | htest.assertEqualHash(Sha384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); | |
| 613 | } | |
| 614 | ||
| 615 | test "sha384 streaming" { | |
| 616 | var h = Sha384.init(); | |
| 617 | var out: [48]u8 = undefined; | |
| 618 | ||
| 619 | const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; | |
| 620 | h.final(out[0..]); | |
| 621 | htest.assertEqual(h1, out[0..]); | |
| 622 | ||
| 623 | const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"; | |
| 624 | ||
| 625 | h.reset(); | |
| 626 | h.update("abc"); | |
| 627 | h.final(out[0..]); | |
| 628 | htest.assertEqual(h2, out[0..]); | |
| 629 | ||
| 630 | h.reset(); | |
| 631 | h.update("a"); | |
| 632 | h.update("b"); | |
| 633 | h.update("c"); | |
| 634 | h.final(out[0..]); | |
| 635 | htest.assertEqual(h2, out[0..]); | |
| 636 | } | |
| 637 | ||
| 638 | test "sha512 single" { | |
| 639 | const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; | |
| 640 | htest.assertEqualHash(Sha512, h1, ""); | |
| 641 | ||
| 642 | const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; | |
| 643 | htest.assertEqualHash(Sha512, h2, "abc"); | |
| 644 | ||
| 645 | const h3 = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"; | |
| 646 | htest.assertEqualHash(Sha512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); | |
| 647 | } | |
| 648 | ||
| 649 | test "sha512 streaming" { | |
| 650 | var h = Sha512.init(); | |
| 651 | var out: [64]u8 = undefined; | |
| 652 | ||
| 653 | const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; | |
| 654 | h.final(out[0..]); | |
| 655 | htest.assertEqual(h1, out[0..]); | |
| 656 | ||
| 657 | const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; | |
| 658 | ||
| 659 | h.reset(); | |
| 660 | h.update("abc"); | |
| 661 | h.final(out[0..]); | |
| 662 | htest.assertEqual(h2, out[0..]); | |
| 663 | ||
| 664 | h.reset(); | |
| 665 | h.update("a"); | |
| 666 | h.update("b"); | |
| 667 | h.update("c"); | |
| 668 | h.final(out[0..]); | |
| 669 | htest.assertEqual(h2, out[0..]); | |
| 670 | } |
std/crypto/test.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | const debug = @import("../debug/index.zig"); | |
| 2 | const mem = @import("../mem.zig"); | |
| 3 | const fmt = @import("../fmt/index.zig"); | |
| 4 | ||
| 5 | // Hash using the specified hasher `H` asserting `expected == H(input)`. | |
| 6 | pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) { | |
| 7 | var h: [expected.len / 2]u8 = undefined; | |
| 8 | Hasher.hash(input, h[0..]); | |
| 9 | ||
| 10 | assertEqual(expected, h); | |
| 11 | } | |
| 12 | ||
| 13 | // Assert `expected` == `input` where `input` is a bytestring. | |
| 14 | pub fn assertEqual(comptime expected: []const u8, input: []const u8) { | |
| 15 | var expected_bytes: [expected.len / 2]u8 = undefined; | |
| 16 | for (expected_bytes) |*r, i| { | |
| 17 | *r = fmt.parseInt(u8, expected[2*i .. 2*i+2], 16) catch unreachable; | |
| 18 | } | |
| 19 | ||
| 20 | debug.assert(mem.eql(u8, expected_bytes, input)); | |
| 21 | } | |
| 22 |
std/debug/index.zig+149-63| ... | ... | @@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; |
| 13 | 13 | error MissingDebugInfo; |
| 14 | 14 | error InvalidDebugInfo; |
| 15 | 15 | error UnsupportedDebugInfo; |
| 16 | error UnknownObjectFormat; | |
| 17 | error TodoSupportCoffDebugInfo; | |
| 18 | error TodoSupportMachoDebugInfo; | |
| 19 | error TodoSupportCOFFDebugInfo; | |
| 16 | 20 | |
| 17 | 21 | |
| 18 | 22 | /// Tries to write to stderr, unbuffered, and ignores any error returned. |
| ... | ... | @@ -37,10 +41,43 @@ fn getStderrStream() -> %&io.OutStream { |
| 37 | 41 | } |
| 38 | 42 | } |
| 39 | 43 | |
| 44 | var self_debug_info: ?&ElfStackTrace = null; | |
| 45 | pub fn getSelfDebugInfo() -> %&ElfStackTrace { | |
| 46 | if (self_debug_info) |info| { | |
| 47 | return info; | |
| 48 | } else { | |
| 49 | const info = try openSelfDebugInfo(global_allocator); | |
| 50 | self_debug_info = info; | |
| 51 | return info; | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 55 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. | |
| 56 | pub fn dumpCurrentStackTrace() { | |
| 57 | const stderr = getStderrStream() catch return; | |
| 58 | const debug_info = getSelfDebugInfo() catch |err| { | |
| 59 | stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; | |
| 60 | return; | |
| 61 | }; | |
| 62 | defer debug_info.close(); | |
| 63 | writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| { | |
| 64 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | |
| 65 | return; | |
| 66 | }; | |
| 67 | } | |
| 68 | ||
| 40 | 69 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. |
| 41 | pub fn dumpStackTrace() { | |
| 70 | pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) { | |
| 42 | 71 | const stderr = getStderrStream() catch return; |
| 43 | writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; | |
| 72 | const debug_info = getSelfDebugInfo() catch |err| { | |
| 73 | stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; | |
| 74 | return; | |
| 75 | }; | |
| 76 | defer debug_info.close(); | |
| 77 | writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| { | |
| 78 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | |
| 79 | return; | |
| 80 | }; | |
| 44 | 81 | } |
| 45 | 82 | |
| 46 | 83 | /// This function invokes undefined behavior when `ok` is `false`. |
| ... | ... | @@ -88,7 +125,21 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { |
| 88 | 125 | |
| 89 | 126 | const stderr = getStderrStream() catch os.abort(); |
| 90 | 127 | stderr.print(format ++ "\n", args) catch os.abort(); |
| 91 | writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); | |
| 128 | dumpCurrentStackTrace(); | |
| 129 | ||
| 130 | os.abort(); | |
| 131 | } | |
| 132 | ||
| 133 | pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) -> noreturn { | |
| 134 | if (panicking) { | |
| 135 | os.abort(); | |
| 136 | } else { | |
| 137 | panicking = true; | |
| 138 | } | |
| 139 | const stderr = getStderrStream() catch os.abort(); | |
| 140 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 141 | dumpStackTrace(trace); | |
| 142 | dumpCurrentStackTrace(); | |
| 92 | 143 | |
| 93 | 144 | os.abort(); |
| 94 | 145 | } |
| ... | ... | @@ -101,12 +152,91 @@ const RESET = "\x1b[0m"; |
| 101 | 152 | error PathNotFound; |
| 102 | 153 | error InvalidDebugInfo; |
| 103 | 154 | |
| 104 | pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, | |
| 105 | ignore_frame_count: usize) -> %void | |
| 155 | pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, | |
| 156 | debug_info: &ElfStackTrace, tty_color: bool) -> %void | |
| 106 | 157 | { |
| 158 | var frame_index: usize = undefined; | |
| 159 | var frames_left: usize = undefined; | |
| 160 | if (stack_trace.index < stack_trace.instruction_addresses.len) { | |
| 161 | frame_index = 0; | |
| 162 | frames_left = stack_trace.index; | |
| 163 | } else { | |
| 164 | frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len; | |
| 165 | frames_left = stack_trace.instruction_addresses.len; | |
| 166 | } | |
| 167 | ||
| 168 | while (frames_left != 0) : ({ | |
| 169 | frames_left -= 1; | |
| 170 | frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; | |
| 171 | }) { | |
| 172 | const return_address = stack_trace.instruction_addresses[frame_index]; | |
| 173 | try printSourceAtAddress(debug_info, out_stream, return_address); | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, | |
| 178 | debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void | |
| 179 | { | |
| 180 | var ignored_count: usize = 0; | |
| 181 | ||
| 182 | var fp = @ptrToInt(@frameAddress()); | |
| 183 | while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { | |
| 184 | if (ignored_count < ignore_frame_count) { | |
| 185 | ignored_count += 1; | |
| 186 | continue; | |
| 187 | } | |
| 188 | ||
| 189 | const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); | |
| 190 | try printSourceAtAddress(debug_info, out_stream, return_address); | |
| 191 | } | |
| 192 | } | |
| 193 | ||
| 194 | fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void { | |
| 195 | if (builtin.os == builtin.Os.windows) { | |
| 196 | return error.UnsupportedDebugInfo; | |
| 197 | } | |
| 198 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal | |
| 199 | // at compile time. I'll call it issue #313 | |
| 200 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; | |
| 201 | ||
| 202 | const compile_unit = findCompileUnit(debug_info, address) catch { | |
| 203 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", | |
| 204 | address); | |
| 205 | return; | |
| 206 | }; | |
| 207 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | |
| 208 | if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { | |
| 209 | defer line_info.deinit(); | |
| 210 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ | |
| 211 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", | |
| 212 | line_info.file_name, line_info.line, line_info.column, | |
| 213 | address, compile_unit_name); | |
| 214 | if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { | |
| 215 | if (line_info.column == 0) { | |
| 216 | try out_stream.write("\n"); | |
| 217 | } else { | |
| 218 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { | |
| 219 | try out_stream.writeByte(' '); | |
| 220 | }} | |
| 221 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | |
| 222 | } | |
| 223 | } else |err| switch (err) { | |
| 224 | error.EndOfFile, error.PathNotFound => {}, | |
| 225 | else => return err, | |
| 226 | } | |
| 227 | } else |err| switch (err) { | |
| 228 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 229 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); | |
| 230 | }, | |
| 231 | else => return err, | |
| 232 | } | |
| 233 | } | |
| 234 | ||
| 235 | pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace { | |
| 107 | 236 | switch (builtin.object_format) { |
| 108 | 237 | builtin.ObjectFormat.elf => { |
| 109 | var stack_trace = ElfStackTrace { | |
| 238 | const st = try allocator.create(ElfStackTrace); | |
| 239 | *st = ElfStackTrace { | |
| 110 | 240 | .self_exe_file = undefined, |
| 111 | 241 | .elf = undefined, |
| 112 | 242 | .debug_info = undefined, |
| ... | ... | @@ -117,12 +247,11 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty |
| 117 | 247 | .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), |
| 118 | 248 | .compile_unit_list = ArrayList(CompileUnit).init(allocator), |
| 119 | 249 | }; |
| 120 | const st = &stack_trace; | |
| 121 | 250 | st.self_exe_file = try os.openSelfExe(); |
| 122 | defer st.self_exe_file.close(); | |
| 251 | %defer st.self_exe_file.close(); | |
| 123 | 252 | |
| 124 | 253 | try st.elf.openFile(allocator, &st.self_exe_file); |
| 125 | defer st.elf.close(); | |
| 254 | %defer st.elf.close(); | |
| 126 | 255 | |
| 127 | 256 | st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| 128 | 257 | st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; |
| ... | ... | @@ -130,67 +259,19 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty |
| 130 | 259 | st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; |
| 131 | 260 | st.debug_ranges = (try st.elf.findSection(".debug_ranges")); |
| 132 | 261 | try scanAllCompileUnits(st); |
| 133 | ||
| 134 | var ignored_count: usize = 0; | |
| 135 | ||
| 136 | var fp = @ptrToInt(@frameAddress()); | |
| 137 | while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { | |
| 138 | if (ignored_count < ignore_frame_count) { | |
| 139 | ignored_count += 1; | |
| 140 | continue; | |
| 141 | } | |
| 142 | ||
| 143 | const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); | |
| 144 | ||
| 145 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal | |
| 146 | // at compile time. I'll call it issue #313 | |
| 147 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; | |
| 148 | ||
| 149 | const compile_unit = findCompileUnit(st, return_address) catch { | |
| 150 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", | |
| 151 | return_address); | |
| 152 | continue; | |
| 153 | }; | |
| 154 | const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); | |
| 155 | if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { | |
| 156 | defer line_info.deinit(); | |
| 157 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ | |
| 158 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", | |
| 159 | line_info.file_name, line_info.line, line_info.column, | |
| 160 | return_address, compile_unit_name); | |
| 161 | if (printLineFromFile(st.allocator(), out_stream, line_info)) { | |
| 162 | if (line_info.column == 0) { | |
| 163 | try out_stream.write("\n"); | |
| 164 | } else { | |
| 165 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { | |
| 166 | try out_stream.writeByte(' '); | |
| 167 | }} | |
| 168 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | |
| 169 | } | |
| 170 | } else |err| switch (err) { | |
| 171 | error.EndOfFile, error.PathNotFound => {}, | |
| 172 | else => return err, | |
| 173 | } | |
| 174 | } else |err| switch (err) { | |
| 175 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 176 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", | |
| 177 | return_address, compile_unit_name); | |
| 178 | }, | |
| 179 | else => return err, | |
| 180 | } | |
| 181 | } | |
| 262 | return st; | |
| 182 | 263 | }, |
| 183 | 264 | builtin.ObjectFormat.coff => { |
| 184 | try out_stream.write("(stack trace unavailable for COFF object format)\n"); | |
| 265 | return error.TodoSupportCoffDebugInfo; | |
| 185 | 266 | }, |
| 186 | 267 | builtin.ObjectFormat.macho => { |
| 187 | try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); | |
| 268 | return error.TodoSupportMachoDebugInfo; | |
| 188 | 269 | }, |
| 189 | 270 | builtin.ObjectFormat.wasm => { |
| 190 | try out_stream.write("(stack trace unavailable for WASM object format)\n"); | |
| 271 | return error.TodoSupportCOFFDebugInfo; | |
| 191 | 272 | }, |
| 192 | 273 | builtin.ObjectFormat.unknown => { |
| 193 | try out_stream.write("(stack trace unavailable for unknown object format)\n"); | |
| 274 | return error.UnknownObjectFormat; | |
| 194 | 275 | }, |
| 195 | 276 | } |
| 196 | 277 | } |
| ... | ... | @@ -228,7 +309,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_ |
| 228 | 309 | } |
| 229 | 310 | } |
| 230 | 311 | |
| 231 | const ElfStackTrace = struct { | |
| 312 | pub const ElfStackTrace = struct { | |
| 232 | 313 | self_exe_file: io.File, |
| 233 | 314 | elf: elf.Elf, |
| 234 | 315 | debug_info: &elf.SectionHeader, |
| ... | ... | @@ -248,6 +329,11 @@ const ElfStackTrace = struct { |
| 248 | 329 | const in_stream = &in_file_stream.stream; |
| 249 | 330 | return readStringRaw(self.allocator(), in_stream); |
| 250 | 331 | } |
| 332 | ||
| 333 | pub fn close(self: &ElfStackTrace) { | |
| 334 | self.self_exe_file.close(); | |
| 335 | self.elf.close(); | |
| 336 | } | |
| 251 | 337 | }; |
| 252 | 338 | |
| 253 | 339 | const PcRange = struct { |
std/index.zig+2| ... | ... | @@ -10,6 +10,7 @@ pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 10 | 10 | pub const base64 = @import("base64.zig"); |
| 11 | 11 | pub const build = @import("build.zig"); |
| 12 | 12 | pub const c = @import("c/index.zig"); |
| 13 | pub const crypto = @import("crypto/index.zig"); | |
| 13 | 14 | pub const cstr = @import("cstr.zig"); |
| 14 | 15 | pub const debug = @import("debug/index.zig"); |
| 15 | 16 | pub const dwarf = @import("dwarf.zig"); |
| ... | ... | @@ -39,6 +40,7 @@ test "std" { |
| 39 | 40 | _ = @import("base64.zig"); |
| 40 | 41 | _ = @import("build.zig"); |
| 41 | 42 | _ = @import("c/index.zig"); |
| 43 | _ = @import("crypto/index.zig"); | |
| 42 | 44 | _ = @import("cstr.zig"); |
| 43 | 45 | _ = @import("debug/index.zig"); |
| 44 | 46 | _ = @import("dwarf.zig"); |
std/io.zig+1-1| ... | ... | @@ -224,7 +224,7 @@ pub const File = struct { |
| 224 | 224 | }; |
| 225 | 225 | } |
| 226 | 226 | }, |
| 227 | else => @compileError("unsupported OS"), | |
| 227 | else => @compileError("unsupported OS: " ++ @tagName(builtin.os)), | |
| 228 | 228 | } |
| 229 | 229 | } |
| 230 | 230 |
std/io_test.zig-5| ... | ... | @@ -8,11 +8,6 @@ const os = std.os; |
| 8 | 8 | const builtin = @import("builtin"); |
| 9 | 9 | |
| 10 | 10 | test "write a file, read it, then delete it" { |
| 11 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 12 | // TODO get this test passing | |
| 13 | // https://github.com/zig-lang/zig/issues/537 | |
| 14 | return; | |
| 15 | } | |
| 16 | 11 | var data: [1024]u8 = undefined; |
| 17 | 12 | var rng = Rand.init(1234); |
| 18 | 13 | rng.fillBytes(data[0..]); |
std/math/acosh.zig-5| ... | ... | @@ -55,11 +55,6 @@ fn acosh64(x: f64) -> f64 { |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | test "math.acosh" { |
| 58 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 59 | // TODO get this test passing | |
| 60 | // https://github.com/zig-lang/zig/issues/537 | |
| 61 | return; | |
| 62 | } | |
| 63 | 58 | assert(acosh(f32(1.5)) == acosh32(1.5)); |
| 64 | 59 | assert(acosh(f64(1.5)) == acosh64(1.5)); |
| 65 | 60 | } |
std/math/cos.zig-5| ... | ... | @@ -146,11 +146,6 @@ test "math.cos" { |
| 146 | 146 | } |
| 147 | 147 | |
| 148 | 148 | test "math.cos32" { |
| 149 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 150 | // TODO get this test passing | |
| 151 | // https://github.com/zig-lang/zig/issues/537 | |
| 152 | return; | |
| 153 | } | |
| 154 | 149 | const epsilon = 0.000001; |
| 155 | 150 | |
| 156 | 151 | assert(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); |
std/math/cosh.zig-5| ... | ... | @@ -81,11 +81,6 @@ fn cosh64(x: f64) -> f64 { |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | 83 | test "math.cosh" { |
| 84 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 85 | // TODO get this test passing | |
| 86 | // https://github.com/zig-lang/zig/issues/537 | |
| 87 | return; | |
| 88 | } | |
| 89 | 84 | assert(cosh(f32(1.5)) == cosh32(1.5)); |
| 90 | 85 | assert(cosh(f64(1.5)) == cosh64(1.5)); |
| 91 | 86 | } |
std/math/index.zig+39-15| ... | ... | @@ -267,6 +267,45 @@ test "math.shr" { |
| 267 | 267 | assert(shr(u8, 0b11111111, isize(-2)) == 0b11111100); |
| 268 | 268 | } |
| 269 | 269 | |
| 270 | /// Rotates right. Only unsigned values can be rotated. | |
| 271 | /// Negative shift values results in shift modulo the bit count. | |
| 272 | pub fn rotr(comptime T: type, x: T, r: var) -> T { | |
| 273 | if (T.is_signed) { | |
| 274 | @compileError("cannot rotate signed integer"); | |
| 275 | } else { | |
| 276 | const ar = @mod(r, T.bit_count); | |
| 277 | return shr(T, x, ar) | shl(T, x, T.bit_count - ar); | |
| 278 | } | |
| 279 | } | |
| 280 | ||
| 281 | test "math.rotr" { | |
| 282 | assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); | |
| 283 | assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); | |
| 284 | assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); | |
| 285 | assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); | |
| 286 | assert(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); | |
| 287 | } | |
| 288 | ||
| 289 | /// Rotates left. Only unsigned values can be rotated. | |
| 290 | /// Negative shift values results in shift modulo the bit count. | |
| 291 | pub fn rotl(comptime T: type, x: T, r: var) -> T { | |
| 292 | if (T.is_signed) { | |
| 293 | @compileError("cannot rotate signed integer"); | |
| 294 | } else { | |
| 295 | const ar = @mod(r, T.bit_count); | |
| 296 | return shl(T, x, ar) | shr(T, x, T.bit_count - ar); | |
| 297 | } | |
| 298 | } | |
| 299 | ||
| 300 | test "math.rotl" { | |
| 301 | assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); | |
| 302 | assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); | |
| 303 | assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); | |
| 304 | assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); | |
| 305 | assert(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); | |
| 306 | } | |
| 307 | ||
| 308 | ||
| 270 | 309 | pub fn Log2Int(comptime T: type) -> type { |
| 271 | 310 | return @IntType(false, log2(T.bit_count)); |
| 272 | 311 | } |
| ... | ... | @@ -320,11 +359,6 @@ pub fn divTrunc(comptime T: type, numerator: T, denominator: T) -> %T { |
| 320 | 359 | } |
| 321 | 360 | |
| 322 | 361 | test "math.divTrunc" { |
| 323 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 324 | // TODO get this test passing | |
| 325 | // https://github.com/zig-lang/zig/issues/537 | |
| 326 | return; | |
| 327 | } | |
| 328 | 362 | testDivTrunc(); |
| 329 | 363 | comptime testDivTrunc(); |
| 330 | 364 | } |
| ... | ... | @@ -350,11 +384,6 @@ pub fn divFloor(comptime T: type, numerator: T, denominator: T) -> %T { |
| 350 | 384 | } |
| 351 | 385 | |
| 352 | 386 | test "math.divFloor" { |
| 353 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 354 | // TODO get this test passing | |
| 355 | // https://github.com/zig-lang/zig/issues/537 | |
| 356 | return; | |
| 357 | } | |
| 358 | 387 | testDivFloor(); |
| 359 | 388 | comptime testDivFloor(); |
| 360 | 389 | } |
| ... | ... | @@ -384,11 +413,6 @@ pub fn divExact(comptime T: type, numerator: T, denominator: T) -> %T { |
| 384 | 413 | } |
| 385 | 414 | |
| 386 | 415 | test "math.divExact" { |
| 387 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 388 | // TODO get this test passing | |
| 389 | // https://github.com/zig-lang/zig/issues/537 | |
| 390 | return; | |
| 391 | } | |
| 392 | 416 | testDivExact(); |
| 393 | 417 | comptime testDivExact(); |
| 394 | 418 | } |
std/math/ln.zig-5| ... | ... | @@ -147,11 +147,6 @@ pub fn ln_64(x_: f64) -> f64 { |
| 147 | 147 | } |
| 148 | 148 | |
| 149 | 149 | test "math.ln" { |
| 150 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 151 | // TODO get this test passing | |
| 152 | // https://github.com/zig-lang/zig/issues/537 | |
| 153 | return; | |
| 154 | } | |
| 155 | 150 | assert(ln(f32(0.2)) == ln_32(0.2)); |
| 156 | 151 | assert(ln(f64(0.2)) == ln_64(0.2)); |
| 157 | 152 | } |
std/math/log.zig-5| ... | ... | @@ -56,11 +56,6 @@ test "math.log float" { |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | test "math.log float_special" { |
| 59 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 60 | // TODO get this test passing | |
| 61 | // https://github.com/zig-lang/zig/issues/537 | |
| 62 | return; | |
| 63 | } | |
| 64 | 59 | assert(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974))); |
| 65 | 60 | assert(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974))); |
| 66 | 61 |
std/math/log10.zig-5| ... | ... | @@ -172,11 +172,6 @@ pub fn log10_64(x_: f64) -> f64 { |
| 172 | 172 | } |
| 173 | 173 | |
| 174 | 174 | test "math.log10" { |
| 175 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 176 | // TODO get this test passing | |
| 177 | // https://github.com/zig-lang/zig/issues/537 | |
| 178 | return; | |
| 179 | } | |
| 180 | 175 | assert(log10(f32(0.2)) == log10_32(0.2)); |
| 181 | 176 | assert(log10(f64(0.2)) == log10_64(0.2)); |
| 182 | 177 | } |
std/math/log2.zig-5| ... | ... | @@ -170,11 +170,6 @@ pub fn log2_64(x_: f64) -> f64 { |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | 172 | test "math.log2" { |
| 173 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 174 | // TODO get this test passing | |
| 175 | // https://github.com/zig-lang/zig/issues/537 | |
| 176 | return; | |
| 177 | } | |
| 178 | 173 | assert(log2(f32(0.2)) == log2_32(0.2)); |
| 179 | 174 | assert(log2(f64(0.2)) == log2_64(0.2)); |
| 180 | 175 | } |
std/math/pow.zig-6| ... | ... | @@ -176,12 +176,6 @@ fn isOddInteger(x: f64) -> bool { |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | 178 | test "math.pow" { |
| 179 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 180 | // TODO get this test passing | |
| 181 | // https://github.com/zig-lang/zig/issues/537 | |
| 182 | return; | |
| 183 | } | |
| 184 | ||
| 185 | 179 | const epsilon = 0.000001; |
| 186 | 180 | |
| 187 | 181 | assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); |
std/math/round.zig-5| ... | ... | @@ -98,11 +98,6 @@ test "math.round" { |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | 100 | test "math.round32" { |
| 101 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 102 | // TODO get this test passing | |
| 103 | // https://github.com/zig-lang/zig/issues/537 | |
| 104 | return; | |
| 105 | } | |
| 106 | 101 | assert(round32(1.3) == 1.0); |
| 107 | 102 | assert(round32(-1.3) == -1.0); |
| 108 | 103 | assert(round32(0.2) == 0.0); |
std/math/sin.zig-5| ... | ... | @@ -150,11 +150,6 @@ test "math.sin" { |
| 150 | 150 | } |
| 151 | 151 | |
| 152 | 152 | test "math.sin32" { |
| 153 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 154 | // TODO get this test passing | |
| 155 | // https://github.com/zig-lang/zig/issues/537 | |
| 156 | return; | |
| 157 | } | |
| 158 | 153 | const epsilon = 0.000001; |
| 159 | 154 | |
| 160 | 155 | assert(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); |
std/math/sinh.zig-5| ... | ... | @@ -88,11 +88,6 @@ fn sinh64(x: f64) -> f64 { |
| 88 | 88 | } |
| 89 | 89 | |
| 90 | 90 | test "math.sinh" { |
| 91 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 92 | // TODO get this test passing | |
| 93 | // https://github.com/zig-lang/zig/issues/537 | |
| 94 | return; | |
| 95 | } | |
| 96 | 91 | assert(sinh(f32(1.5)) == sinh32(1.5)); |
| 97 | 92 | assert(sinh(f64(1.5)) == sinh64(1.5)); |
| 98 | 93 | } |
std/math/tan.zig-5| ... | ... | @@ -136,11 +136,6 @@ test "math.tan" { |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | 138 | test "math.tan32" { |
| 139 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 140 | // TODO get this test passing | |
| 141 | // https://github.com/zig-lang/zig/issues/537 | |
| 142 | return; | |
| 143 | } | |
| 144 | 139 | const epsilon = 0.000001; |
| 145 | 140 | |
| 146 | 141 | assert(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); |
std/math/tanh.zig-5| ... | ... | @@ -112,11 +112,6 @@ fn tanh64(x: f64) -> f64 { |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | 114 | test "math.tanh" { |
| 115 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 116 | // TODO get this test passing | |
| 117 | // https://github.com/zig-lang/zig/issues/537 | |
| 118 | return; | |
| 119 | } | |
| 120 | 115 | assert(tanh(f32(1.5)) == tanh32(1.5)); |
| 121 | 116 | assert(tanh(f64(1.5)) == tanh64(1.5)); |
| 122 | 117 | } |
std/os/index.zig+2-9| ... | ... | @@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn { |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | /// Exits the program cleanly with the specified status code. |
| 151 | pub coldcc fn exit(status: i32) -> noreturn { | |
| 151 | pub coldcc fn exit(status: u8) -> noreturn { | |
| 152 | 152 | if (builtin.link_libc) { |
| 153 | 153 | c.exit(status); |
| 154 | 154 | } |
| ... | ... | @@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn { |
| 157 | 157 | posix.exit(status); |
| 158 | 158 | }, |
| 159 | 159 | Os.windows => { |
| 160 | // Map a possibly negative status code to a non-negative status for the systems default | |
| 161 | // integer width. | |
| 162 | const p_status = if (@sizeOf(c_uint) < @sizeOf(u32)) | |
| 163 | @truncate(c_uint, @bitCast(u32, status)) | |
| 164 | else | |
| 165 | c_uint(@bitCast(u32, status)); | |
| 166 | ||
| 167 | windows.ExitProcess(p_status); | |
| 160 | windows.ExitProcess(status); | |
| 168 | 161 | }, |
| 169 | 162 | else => @compileError("Unsupported OS"), |
| 170 | 163 | } |
std/rand.zig-20| ... | ... | @@ -194,11 +194,6 @@ fn MersenneTwister( |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | 196 | test "rand float 32" { |
| 197 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 198 | // TODO get this test passing | |
| 199 | // https://github.com/zig-lang/zig/issues/537 | |
| 200 | return; | |
| 201 | } | |
| 202 | 197 | var r = Rand.init(42); |
| 203 | 198 | var i: usize = 0; |
| 204 | 199 | while (i < 1000) : (i += 1) { |
| ... | ... | @@ -209,11 +204,6 @@ test "rand float 32" { |
| 209 | 204 | } |
| 210 | 205 | |
| 211 | 206 | test "rand.MT19937_64" { |
| 212 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 213 | // TODO get this test passing | |
| 214 | // https://github.com/zig-lang/zig/issues/537 | |
| 215 | return; | |
| 216 | } | |
| 217 | 207 | var rng = MT19937_64.init(rand_test.mt64_seed); |
| 218 | 208 | for (rand_test.mt64_data) |value| { |
| 219 | 209 | assert(value == rng.get()); |
| ... | ... | @@ -221,11 +211,6 @@ test "rand.MT19937_64" { |
| 221 | 211 | } |
| 222 | 212 | |
| 223 | 213 | test "rand.MT19937_32" { |
| 224 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 225 | // TODO get this test passing | |
| 226 | // https://github.com/zig-lang/zig/issues/537 | |
| 227 | return; | |
| 228 | } | |
| 229 | 214 | var rng = MT19937_32.init(rand_test.mt32_seed); |
| 230 | 215 | for (rand_test.mt32_data) |value| { |
| 231 | 216 | assert(value == rng.get()); |
| ... | ... | @@ -233,11 +218,6 @@ test "rand.MT19937_32" { |
| 233 | 218 | } |
| 234 | 219 | |
| 235 | 220 | test "rand.Rand.range" { |
| 236 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 237 | // TODO get this test passing | |
| 238 | // https://github.com/zig-lang/zig/issues/537 | |
| 239 | return; | |
| 240 | } | |
| 241 | 221 | var r = Rand.init(42); |
| 242 | 222 | testRange(&r, -4, 3); |
| 243 | 223 | testRange(&r, -4, -1); |
std/sort.zig-20| ... | ... | @@ -1020,11 +1020,6 @@ fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> bool { |
| 1020 | 1020 | } |
| 1021 | 1021 | |
| 1022 | 1022 | test "std.sort" { |
| 1023 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 1024 | // TODO get this test passing | |
| 1025 | // https://github.com/zig-lang/zig/issues/537 | |
| 1026 | return; | |
| 1027 | } | |
| 1028 | 1023 | const u8cases = [][]const []const u8 { |
| 1029 | 1024 | [][]const u8{"", ""}, |
| 1030 | 1025 | [][]const u8{"a", "a"}, |
| ... | ... | @@ -1061,11 +1056,6 @@ test "std.sort" { |
| 1061 | 1056 | } |
| 1062 | 1057 | |
| 1063 | 1058 | test "std.sort descending" { |
| 1064 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 1065 | // TODO get this test passing | |
| 1066 | // https://github.com/zig-lang/zig/issues/537 | |
| 1067 | return; | |
| 1068 | } | |
| 1069 | 1059 | const rev_cases = [][]const []const i32 { |
| 1070 | 1060 | [][]const i32{[]i32{}, []i32{}}, |
| 1071 | 1061 | [][]const i32{[]i32{1}, []i32{1}}, |
| ... | ... | @@ -1085,11 +1075,6 @@ test "std.sort descending" { |
| 1085 | 1075 | } |
| 1086 | 1076 | |
| 1087 | 1077 | test "another sort case" { |
| 1088 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 1089 | // TODO get this test passing | |
| 1090 | // https://github.com/zig-lang/zig/issues/537 | |
| 1091 | return; | |
| 1092 | } | |
| 1093 | 1078 | var arr = []i32{ 5, 3, 1, 2, 4 }; |
| 1094 | 1079 | sort(i32, arr[0..], i32asc); |
| 1095 | 1080 | |
| ... | ... | @@ -1097,11 +1082,6 @@ test "another sort case" { |
| 1097 | 1082 | } |
| 1098 | 1083 | |
| 1099 | 1084 | test "sort fuzz testing" { |
| 1100 | if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { | |
| 1101 | // TODO get this test passing | |
| 1102 | // https://github.com/zig-lang/zig/issues/537 | |
| 1103 | return; | |
| 1104 | } | |
| 1105 | 1085 | var rng = std.rand.Rand.init(0x12345678); |
| 1106 | 1086 | const test_case_count = 10; |
| 1107 | 1087 | var i: usize = 0; |
std/special/bootstrap.zig+35-10| ... | ... | @@ -21,8 +21,7 @@ comptime { |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | extern fn zenMain() -> noreturn { |
| 24 | root.main() catch std.os.posix.exit(1); | |
| 25 | std.os.posix.exit(0); | |
| 24 | std.os.posix.exit(callMain()); | |
| 26 | 25 | } |
| 27 | 26 | |
| 28 | 27 | nakedcc fn _start() -> noreturn { |
| ... | ... | @@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn { |
| 43 | 42 | extern fn WinMainCRTStartup() -> noreturn { |
| 44 | 43 | @setAlignStack(16); |
| 45 | 44 | |
| 46 | root.main() catch std.os.windows.ExitProcess(1); | |
| 47 | std.os.windows.ExitProcess(0); | |
| 45 | std.os.windows.ExitProcess(callMain()); | |
| 48 | 46 | } |
| 49 | 47 | |
| 50 | 48 | fn posixCallMainAndExit() -> noreturn { |
| 51 | 49 | const argc = *argc_ptr; |
| 52 | 50 | const argv = @ptrCast(&&u8, &argc_ptr[1]); |
| 53 | 51 | const envp = @ptrCast(&?&u8, &argv[argc + 1]); |
| 54 | callMain(argc, argv, envp) catch std.os.posix.exit(1); | |
| 55 | std.os.posix.exit(0); | |
| 52 | std.os.posix.exit(callMainWithArgs(argc, argv, envp)); | |
| 56 | 53 | } |
| 57 | 54 | |
| 58 | fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { | |
| 55 | fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 { | |
| 59 | 56 | std.os.ArgIteratorPosix.raw = argv[0..argc]; |
| 60 | 57 | |
| 61 | 58 | var env_count: usize = 0; |
| 62 | 59 | while (envp[env_count] != null) : (env_count += 1) {} |
| 63 | 60 | std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count]; |
| 64 | 61 | |
| 65 | return root.main(); | |
| 62 | return callMain(); | |
| 66 | 63 | } |
| 67 | 64 | |
| 68 | 65 | extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 { |
| 69 | callMain(usize(c_argc), c_argv, c_envp) catch return 1; | |
| 70 | return 0; | |
| 66 | return callMainWithArgs(usize(c_argc), c_argv, c_envp); | |
| 67 | } | |
| 68 | ||
| 69 | fn callMain() -> u8 { | |
| 70 | switch (@typeId(@typeOf(root.main).ReturnType)) { | |
| 71 | builtin.TypeId.NoReturn => { | |
| 72 | root.main(); | |
| 73 | }, | |
| 74 | builtin.TypeId.Void => { | |
| 75 | root.main(); | |
| 76 | return 0; | |
| 77 | }, | |
| 78 | builtin.TypeId.Int => { | |
| 79 | if (@typeOf(root.main).ReturnType.bit_count != 8) { | |
| 80 | @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); | |
| 81 | } | |
| 82 | return root.main(); | |
| 83 | }, | |
| 84 | builtin.TypeId.ErrorUnion => { | |
| 85 | root.main() catch |err| { | |
| 86 | std.debug.warn("error: {}\n", @errorName(err)); | |
| 87 | if (@errorReturnTrace()) |trace| { | |
| 88 | std.debug.dumpStackTrace(trace); | |
| 89 | } | |
| 90 | return 1; | |
| 91 | }; | |
| 92 | return 0; | |
| 93 | }, | |
| 94 | else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"), | |
| 95 | } | |
| 71 | 96 | } |
std/special/build_runner.zig+4-4| ... | ... | @@ -14,7 +14,7 @@ pub fn main() -> %void { |
| 14 | 14 | var arg_it = os.args(); |
| 15 | 15 | |
| 16 | 16 | // TODO use a more general purpose allocator here |
| 17 | var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable; | |
| 17 | var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024); | |
| 18 | 18 | defer inc_allocator.deinit(); |
| 19 | 19 | |
| 20 | 20 | const allocator = &inc_allocator.allocator; |
| ... | ... | @@ -107,12 +107,12 @@ pub fn main() -> %void { |
| 107 | 107 | return usageAndErr(&builder, false, try stderr_stream); |
| 108 | 108 | } |
| 109 | 109 | } else { |
| 110 | targets.append(arg) catch unreachable; | |
| 110 | try targets.append(arg); | |
| 111 | 111 | } |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | 114 | builder.setInstallPrefix(prefix); |
| 115 | root.build(&builder); | |
| 115 | try root.build(&builder); | |
| 116 | 116 | |
| 117 | 117 | if (builder.validateUserInputDidItFail()) |
| 118 | 118 | return usageAndErr(&builder, true, try stderr_stream); |
| ... | ... | @@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) |
| 129 | 129 | // run the build script to collect the options |
| 130 | 130 | if (!already_ran_build) { |
| 131 | 131 | builder.setInstallPrefix(null); |
| 132 | root.build(builder); | |
| 132 | try root.build(builder); | |
| 133 | 133 | } |
| 134 | 134 | |
| 135 | 135 | // This usage text has to be synchronized with src/main.cpp |
std/special/builtin.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ const builtin = @import("builtin"); |
| 5 | 5 | |
| 6 | 6 | // Avoid dragging in the debug safety mechanisms into this .o file, |
| 7 | 7 | // unless we're trying to test this file. |
| 8 | pub coldcc fn panic(msg: []const u8) -> noreturn { | |
| 8 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { | |
| 9 | 9 | if (builtin.is_test) { |
| 10 | 10 | @import("std").debug.panic("{}", msg); |
| 11 | 11 | } else { |
std/special/compiler_rt/index.zig+1-1| ... | ... | @@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; |
| 74 | 74 | |
| 75 | 75 | // Avoid dragging in the debug safety mechanisms into this .o file, |
| 76 | 76 | // unless we're trying to test this file. |
| 77 | pub coldcc fn panic(msg: []const u8) -> noreturn { | |
| 77 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { | |
| 78 | 78 | if (is_test) { |
| 79 | 79 | @import("std").debug.panic("{}", msg); |
| 80 | 80 | } else { |
std/special/panic.zig+5-1| ... | ... | @@ -4,14 +4,18 @@ |
| 4 | 4 | // have to be added in the compiler. |
| 5 | 5 | |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("std"); | |
| 7 | 8 | |
| 8 | pub coldcc fn panic(msg: []const u8) -> noreturn { | |
| 9 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { | |
| 9 | 10 | switch (builtin.os) { |
| 10 | 11 | // TODO: fix panic in zen. |
| 11 | 12 | builtin.Os.freestanding, builtin.Os.zen => { |
| 12 | 13 | while (true) {} |
| 13 | 14 | }, |
| 14 | 15 | else => { |
| 16 | if (error_return_trace) |trace| { | |
| 17 | @import("std").debug.panicWithTrace(trace, "{}", msg); | |
| 18 | } | |
| 15 | 19 | @import("std").debug.panic("{}", msg); |
| 16 | 20 | }, |
| 17 | 21 | } |
std/special/test_runner.zig+1-4| ... | ... | @@ -8,10 +8,7 @@ pub fn main() -> %void { |
| 8 | 8 | for (test_fn_list) |test_fn, i| { |
| 9 | 9 | warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); |
| 10 | 10 | |
| 11 | test_fn.func() catch |err| { | |
| 12 | warn("{}\n", err); | |
| 13 | return err; | |
| 14 | }; | |
| 11 | try test_fn.func(); | |
| 15 | 12 | |
| 16 | 13 | warn("OK\n"); |
| 17 | 14 | } |
test/build_examples.zig+1| ... | ... | @@ -16,4 +16,5 @@ pub fn addCases(cases: &tests.BuildExamplesContext) { |
| 16 | 16 | cases.addBuildFile("test/standalone/issue_339/build.zig"); |
| 17 | 17 | cases.addBuildFile("test/standalone/pkg_import/build.zig"); |
| 18 | 18 | cases.addBuildFile("test/standalone/use_alias/build.zig"); |
| 19 | cases.addBuildFile("test/standalone/brace_expansion/build.zig"); | |
| 19 | 20 | } |
test/cases/math.zig+23| ... | ... | @@ -26,6 +26,29 @@ fn testDivision() { |
| 26 | 26 | assert(divTrunc(i32, -5, 3) == -1); |
| 27 | 27 | assert(divTrunc(f32, 5.0, 3.0) == 1.0); |
| 28 | 28 | assert(divTrunc(f32, -5.0, 3.0) == -1.0); |
| 29 | ||
| 30 | comptime { | |
| 31 | assert( | |
| 32 | 1194735857077236777412821811143690633098347576 % | |
| 33 | 508740759824825164163191790951174292733114988 == | |
| 34 | 177254337427586449086438229241342047632117600); | |
| 35 | assert(@rem(-1194735857077236777412821811143690633098347576, | |
| 36 | 508740759824825164163191790951174292733114988) == | |
| 37 | -177254337427586449086438229241342047632117600); | |
| 38 | assert(1194735857077236777412821811143690633098347576 / | |
| 39 | 508740759824825164163191790951174292733114988 == | |
| 40 | 2); | |
| 41 | assert(@divTrunc(-1194735857077236777412821811143690633098347576, | |
| 42 | 508740759824825164163191790951174292733114988) == | |
| 43 | -2); | |
| 44 | assert(@divTrunc(1194735857077236777412821811143690633098347576, | |
| 45 | -508740759824825164163191790951174292733114988) == | |
| 46 | -2); | |
| 47 | assert(@divTrunc(-1194735857077236777412821811143690633098347576, | |
| 48 | -508740759824825164163191790951174292733114988) == | |
| 49 | 2); | |
| 50 | assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1); | |
| 51 | } | |
| 29 | 52 | } |
| 30 | 53 | fn div(comptime T: type, a: T, b: T) -> T { |
| 31 | 54 | return a / b; |
test/compile_errors.zig+9-9| ... | ... | @@ -1,6 +1,15 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: &tests.CompileErrorContext) { |
| 4 | cases.add("wrong return type for main", | |
| 5 | \\pub fn main() -> f32 { } | |
| 6 | , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); | |
| 7 | ||
| 8 | cases.add("double ?? on main return value", | |
| 9 | \\pub fn main() -> ??void { | |
| 10 | \\} | |
| 11 | , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); | |
| 12 | ||
| 4 | 13 | cases.add("bad identifier in function with struct defined inside function which references local const", |
| 5 | 14 | \\export fn entry() { |
| 6 | 15 | \\ const BlockKind = u32; |
| ... | ... | @@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 1059 | 1068 | , |
| 1060 | 1069 | ".tmp_source.zig:2:5: error: expected type 'void', found 'error'"); |
| 1061 | 1070 | |
| 1062 | cases.add("wrong return type for main", | |
| 1063 | \\pub fn main() { } | |
| 1064 | , ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'"); | |
| 1065 | ||
| 1066 | cases.add("double ?? on main return value", | |
| 1067 | \\pub fn main() -> ??void { | |
| 1068 | \\} | |
| 1069 | , ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'"); | |
| 1070 | ||
| 1071 | 1071 | cases.add("invalid pointer for var type", |
| 1072 | 1072 | \\extern fn ext() -> usize; |
| 1073 | 1073 | \\var bytes: [ext()]u8 = undefined; |
test/debug_safety.zig+20-20| ... | ... | @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: &tests.CompareOutputContext) { |
| 4 | 4 | cases.addDebugSafety("calling panic", |
| 5 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 5 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 6 | 6 | \\ @import("std").os.exit(126); |
| 7 | 7 | \\} |
| 8 | 8 | \\pub fn main() -> %void { |
| ... | ... | @@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 11 | 11 | ); |
| 12 | 12 | |
| 13 | 13 | cases.addDebugSafety("out of bounds slice access", |
| 14 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 14 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 15 | 15 | \\ @import("std").os.exit(126); |
| 16 | 16 | \\} |
| 17 | 17 | \\pub fn main() -> %void { |
| ... | ... | @@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 25 | 25 | ); |
| 26 | 26 | |
| 27 | 27 | cases.addDebugSafety("integer addition overflow", |
| 28 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 28 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 29 | 29 | \\ @import("std").os.exit(126); |
| 30 | 30 | \\} |
| 31 | 31 | \\error Whatever; |
| ... | ... | @@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 39 | 39 | ); |
| 40 | 40 | |
| 41 | 41 | cases.addDebugSafety("integer subtraction overflow", |
| 42 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 42 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 43 | 43 | \\ @import("std").os.exit(126); |
| 44 | 44 | \\} |
| 45 | 45 | \\error Whatever; |
| ... | ... | @@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 53 | 53 | ); |
| 54 | 54 | |
| 55 | 55 | cases.addDebugSafety("integer multiplication overflow", |
| 56 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 56 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 57 | 57 | \\ @import("std").os.exit(126); |
| 58 | 58 | \\} |
| 59 | 59 | \\error Whatever; |
| ... | ... | @@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 67 | 67 | ); |
| 68 | 68 | |
| 69 | 69 | cases.addDebugSafety("integer negation overflow", |
| 70 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 70 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 71 | 71 | \\ @import("std").os.exit(126); |
| 72 | 72 | \\} |
| 73 | 73 | \\error Whatever; |
| ... | ... | @@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 81 | 81 | ); |
| 82 | 82 | |
| 83 | 83 | cases.addDebugSafety("signed integer division overflow", |
| 84 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 84 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 85 | 85 | \\ @import("std").os.exit(126); |
| 86 | 86 | \\} |
| 87 | 87 | \\error Whatever; |
| ... | ... | @@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 95 | 95 | ); |
| 96 | 96 | |
| 97 | 97 | cases.addDebugSafety("signed shift left overflow", |
| 98 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 98 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 99 | 99 | \\ @import("std").os.exit(126); |
| 100 | 100 | \\} |
| 101 | 101 | \\error Whatever; |
| ... | ... | @@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 109 | 109 | ); |
| 110 | 110 | |
| 111 | 111 | cases.addDebugSafety("unsigned shift left overflow", |
| 112 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 112 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 113 | 113 | \\ @import("std").os.exit(126); |
| 114 | 114 | \\} |
| 115 | 115 | \\error Whatever; |
| ... | ... | @@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 123 | 123 | ); |
| 124 | 124 | |
| 125 | 125 | cases.addDebugSafety("signed shift right overflow", |
| 126 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 126 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 127 | 127 | \\ @import("std").os.exit(126); |
| 128 | 128 | \\} |
| 129 | 129 | \\error Whatever; |
| ... | ... | @@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 137 | 137 | ); |
| 138 | 138 | |
| 139 | 139 | cases.addDebugSafety("unsigned shift right overflow", |
| 140 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 140 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 141 | 141 | \\ @import("std").os.exit(126); |
| 142 | 142 | \\} |
| 143 | 143 | \\error Whatever; |
| ... | ... | @@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 151 | 151 | ); |
| 152 | 152 | |
| 153 | 153 | cases.addDebugSafety("integer division by zero", |
| 154 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 154 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 155 | 155 | \\ @import("std").os.exit(126); |
| 156 | 156 | \\} |
| 157 | 157 | \\error Whatever; |
| ... | ... | @@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 164 | 164 | ); |
| 165 | 165 | |
| 166 | 166 | cases.addDebugSafety("exact division failure", |
| 167 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 167 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 168 | 168 | \\ @import("std").os.exit(126); |
| 169 | 169 | \\} |
| 170 | 170 | \\error Whatever; |
| ... | ... | @@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 178 | 178 | ); |
| 179 | 179 | |
| 180 | 180 | cases.addDebugSafety("cast []u8 to bigger slice of wrong size", |
| 181 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 181 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 182 | 182 | \\ @import("std").os.exit(126); |
| 183 | 183 | \\} |
| 184 | 184 | \\error Whatever; |
| ... | ... | @@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 192 | 192 | ); |
| 193 | 193 | |
| 194 | 194 | cases.addDebugSafety("value does not fit in shortening cast", |
| 195 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 195 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 196 | 196 | \\ @import("std").os.exit(126); |
| 197 | 197 | \\} |
| 198 | 198 | \\error Whatever; |
| ... | ... | @@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 206 | 206 | ); |
| 207 | 207 | |
| 208 | 208 | cases.addDebugSafety("signed integer not fitting in cast to unsigned integer", |
| 209 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 209 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 210 | 210 | \\ @import("std").os.exit(126); |
| 211 | 211 | \\} |
| 212 | 212 | \\error Whatever; |
| ... | ... | @@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 220 | 220 | ); |
| 221 | 221 | |
| 222 | 222 | cases.addDebugSafety("unwrap error", |
| 223 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 223 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 224 | 224 | \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) { |
| 225 | 225 | \\ @import("std").os.exit(126); // good |
| 226 | 226 | \\ } |
| ... | ... | @@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 236 | 236 | ); |
| 237 | 237 | |
| 238 | 238 | cases.addDebugSafety("cast integer to error and no code matches", |
| 239 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 239 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 240 | 240 | \\ @import("std").os.exit(126); |
| 241 | 241 | \\} |
| 242 | 242 | \\pub fn main() -> %void { |
| ... | ... | @@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 248 | 248 | ); |
| 249 | 249 | |
| 250 | 250 | cases.addDebugSafety("@alignCast misaligned", |
| 251 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 251 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 252 | 252 | \\ @import("std").os.exit(126); |
| 253 | 253 | \\} |
| 254 | 254 | \\error Wrong; |
| ... | ... | @@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 265 | 265 | ); |
| 266 | 266 | |
| 267 | 267 | cases.addDebugSafety("bad union field access", |
| 268 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 268 | \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { | |
| 269 | 269 | \\ @import("std").os.exit(126); |
| 270 | 270 | \\} |
| 271 | 271 | \\ |
test/standalone/brace_expansion/build.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: &Builder) -> %void { | |
| 4 | const main = b.addTest("main.zig"); | |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test it"); | |
| 8 | test_step.dependOn(&main.step); | |
| 9 | } |
test/standalone/brace_expansion/main.zig created+254| ... | ... | @@ -0,0 +1,254 @@ |
| 1 | const std = @import("std"); | |
| 2 | const io = std.io; | |
| 3 | const mem = std.mem; | |
| 4 | const debug = std.debug; | |
| 5 | const assert = debug.assert; | |
| 6 | const Buffer = std.Buffer; | |
| 7 | const ArrayList = std.ArrayList; | |
| 8 | ||
| 9 | error InvalidInput; | |
| 10 | error OutOfMem; | |
| 11 | ||
| 12 | const Token = union(enum) { | |
| 13 | Word: []const u8, | |
| 14 | OpenBrace, | |
| 15 | CloseBrace, | |
| 16 | Comma, | |
| 17 | Eof, | |
| 18 | }; | |
| 19 | ||
| 20 | var global_allocator: &mem.Allocator = undefined; | |
| 21 | ||
| 22 | fn tokenize(input:[] const u8) -> %ArrayList(Token) { | |
| 23 | const State = enum { | |
| 24 | Start, | |
| 25 | Word, | |
| 26 | }; | |
| 27 | ||
| 28 | var token_list = ArrayList(Token).init(global_allocator); | |
| 29 | var tok_begin: usize = undefined; | |
| 30 | var state = State.Start; | |
| 31 | ||
| 32 | for (input) |b, i| { | |
| 33 | switch (state) { | |
| 34 | State.Start => switch (b) { | |
| 35 | 'a'...'z', 'A'...'Z' => { | |
| 36 | state = State.Word; | |
| 37 | tok_begin = i; | |
| 38 | }, | |
| 39 | '{' => try token_list.append(Token.OpenBrace), | |
| 40 | '}' => try token_list.append(Token.CloseBrace), | |
| 41 | ',' => try token_list.append(Token.Comma), | |
| 42 | else => return error.InvalidInput, | |
| 43 | }, | |
| 44 | State.Word => switch (b) { | |
| 45 | 'a'...'z', 'A'...'Z' => {}, | |
| 46 | '{', '}', ',' => { | |
| 47 | try token_list.append(Token { .Word = input[tok_begin..i] }); | |
| 48 | switch (b) { | |
| 49 | '{' => try token_list.append(Token.OpenBrace), | |
| 50 | '}' => try token_list.append(Token.CloseBrace), | |
| 51 | ',' => try token_list.append(Token.Comma), | |
| 52 | else => unreachable, | |
| 53 | } | |
| 54 | state = State.Start; | |
| 55 | }, | |
| 56 | else => return error.InvalidInput, | |
| 57 | }, | |
| 58 | } | |
| 59 | } | |
| 60 | switch (state) { | |
| 61 | State.Start => {}, | |
| 62 | State.Word => try token_list.append(Token {.Word = input[tok_begin..] }), | |
| 63 | } | |
| 64 | try token_list.append(Token.Eof); | |
| 65 | return token_list; | |
| 66 | } | |
| 67 | ||
| 68 | const Node = union(enum) { | |
| 69 | Scalar: []const u8, | |
| 70 | List: ArrayList(Node), | |
| 71 | Combine: []Node, | |
| 72 | }; | |
| 73 | ||
| 74 | fn parse(tokens: &const ArrayList(Token), token_index: &usize) -> %Node { | |
| 75 | const first_token = tokens.items[*token_index]; | |
| 76 | *token_index += 1; | |
| 77 | ||
| 78 | const result_node = switch (first_token) { | |
| 79 | Token.Word => |word| Node { .Scalar = word }, | |
| 80 | Token.OpenBrace => blk: { | |
| 81 | var list = ArrayList(Node).init(global_allocator); | |
| 82 | while (true) { | |
| 83 | try list.append(try parse(tokens, token_index)); | |
| 84 | ||
| 85 | const token = tokens.items[*token_index]; | |
| 86 | *token_index += 1; | |
| 87 | ||
| 88 | switch (token) { | |
| 89 | Token.CloseBrace => break, | |
| 90 | Token.Comma => continue, | |
| 91 | else => return error.InvalidInput, | |
| 92 | } | |
| 93 | } | |
| 94 | break :blk Node { .List = list }; | |
| 95 | }, | |
| 96 | else => return error.InvalidInput, | |
| 97 | }; | |
| 98 | ||
| 99 | switch (tokens.items[*token_index]) { | |
| 100 | Token.Word, Token.OpenBrace => { | |
| 101 | const pair = try global_allocator.alloc(Node, 2); | |
| 102 | pair[0] = result_node; | |
| 103 | pair[1] = try parse(tokens, token_index); | |
| 104 | return Node { .Combine = pair }; | |
| 105 | }, | |
| 106 | else => return result_node, | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| 110 | fn expandString(input: []const u8, output: &Buffer) -> %void { | |
| 111 | const tokens = try tokenize(input); | |
| 112 | if (tokens.len == 1) { | |
| 113 | return output.resize(0); | |
| 114 | } | |
| 115 | ||
| 116 | var token_index: usize = 0; | |
| 117 | const root = try parse(tokens, &token_index); | |
| 118 | const last_token = tokens.items[token_index]; | |
| 119 | switch (last_token) { | |
| 120 | Token.Eof => {}, | |
| 121 | else => return error.InvalidInput, | |
| 122 | } | |
| 123 | ||
| 124 | var result_list = ArrayList(Buffer).init(global_allocator); | |
| 125 | defer result_list.deinit(); | |
| 126 | ||
| 127 | try expandNode(root, &result_list); | |
| 128 | ||
| 129 | try output.resize(0); | |
| 130 | for (result_list.toSliceConst()) |buf, i| { | |
| 131 | if (i != 0) { | |
| 132 | try output.appendByte(' '); | |
| 133 | } | |
| 134 | try output.append(buf.toSliceConst()); | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | const ListOfBuffer0 = ArrayList(Buffer); // TODO this is working around a compiler bug, fix and delete this | |
| 139 | ||
| 140 | fn expandNode(node: &const Node, output: &ListOfBuffer0) -> %void { | |
| 141 | assert(output.len == 0); | |
| 142 | switch (*node) { | |
| 143 | Node.Scalar => |scalar| { | |
| 144 | try output.append(try Buffer.init(global_allocator, scalar)); | |
| 145 | }, | |
| 146 | Node.Combine => |pair| { | |
| 147 | const a_node = pair[0]; | |
| 148 | const b_node = pair[1]; | |
| 149 | ||
| 150 | var child_list_a = ArrayList(Buffer).init(global_allocator); | |
| 151 | try expandNode(a_node, &child_list_a); | |
| 152 | ||
| 153 | var child_list_b = ArrayList(Buffer).init(global_allocator); | |
| 154 | try expandNode(b_node, &child_list_b); | |
| 155 | ||
| 156 | for (child_list_a.toSliceConst()) |buf_a| { | |
| 157 | for (child_list_b.toSliceConst()) |buf_b| { | |
| 158 | var combined_buf = try Buffer.initFromBuffer(buf_a); | |
| 159 | try combined_buf.append(buf_b.toSliceConst()); | |
| 160 | try output.append(combined_buf); | |
| 161 | } | |
| 162 | } | |
| 163 | }, | |
| 164 | Node.List => |list| { | |
| 165 | for (list.toSliceConst()) |child_node| { | |
| 166 | var child_list = ArrayList(Buffer).init(global_allocator); | |
| 167 | try expandNode(child_node, &child_list); | |
| 168 | ||
| 169 | for (child_list.toSliceConst()) |buf| { | |
| 170 | try output.append(buf); | |
| 171 | } | |
| 172 | } | |
| 173 | }, | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | pub fn main() -> %void { | |
| 178 | var stdin_file = try io.getStdIn(); | |
| 179 | var stdout_file = try io.getStdOut(); | |
| 180 | ||
| 181 | var inc_allocator = try std.heap.IncrementingAllocator.init(2 * 1024 * 1024); | |
| 182 | defer inc_allocator.deinit(); | |
| 183 | ||
| 184 | global_allocator = &inc_allocator.allocator; | |
| 185 | ||
| 186 | var stdin_buf = try Buffer.initSize(global_allocator, 0); | |
| 187 | defer stdin_buf.deinit(); | |
| 188 | ||
| 189 | var stdin_adapter = io.FileInStream.init(&stdin_file); | |
| 190 | try stdin_adapter.stream.readAllBuffer(&stdin_buf, @maxValue(usize)); | |
| 191 | ||
| 192 | var result_buf = try Buffer.initSize(global_allocator, 0); | |
| 193 | defer result_buf.deinit(); | |
| 194 | ||
| 195 | try expandString(stdin_buf.toSlice(), &result_buf); | |
| 196 | try stdout_file.write(result_buf.toSliceConst()); | |
| 197 | } | |
| 198 | ||
| 199 | test "invalid inputs" { | |
| 200 | global_allocator = std.debug.global_allocator; | |
| 201 | ||
| 202 | expectError("}ABC", error.InvalidInput); | |
| 203 | expectError("{ABC", error.InvalidInput); | |
| 204 | expectError("}{", error.InvalidInput); | |
| 205 | expectError("{}", error.InvalidInput); | |
| 206 | expectError("A,B,C", error.InvalidInput); | |
| 207 | expectError("{A{B,C}", error.InvalidInput); | |
| 208 | expectError("{A,}", error.InvalidInput); | |
| 209 | ||
| 210 | expectError("\n", error.InvalidInput); | |
| 211 | } | |
| 212 | ||
| 213 | fn expectError(test_input: []const u8, expected_err: error) { | |
| 214 | var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable; | |
| 215 | defer output_buf.deinit(); | |
| 216 | ||
| 217 | if (expandString("}ABC", &output_buf)) { | |
| 218 | unreachable; | |
| 219 | } else |err| { | |
| 220 | assert(expected_err == err); | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | test "valid inputs" { | |
| 225 | global_allocator = std.debug.global_allocator; | |
| 226 | ||
| 227 | expectExpansion("{x,y,z}", "x y z"); | |
| 228 | expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); | |
| 229 | expectExpansion("{A,B{x,y}}", "A Bx By"); | |
| 230 | ||
| 231 | expectExpansion("{ABC}", "ABC"); | |
| 232 | expectExpansion("{A,B,C}", "A B C"); | |
| 233 | expectExpansion("ABC", "ABC"); | |
| 234 | ||
| 235 | expectExpansion("", ""); | |
| 236 | expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh"); | |
| 237 | expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh"); | |
| 238 | expectExpansion("{A,B}a", "Aa Ba"); | |
| 239 | expectExpansion("{C,{x,y}}", "C x y"); | |
| 240 | expectExpansion("z{C,{x,y}}", "zC zx zy"); | |
| 241 | expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg"); | |
| 242 | expectExpansion("a{x,y}b", "axb ayb"); | |
| 243 | expectExpansion("z{{a,b}}", "za zb"); | |
| 244 | expectExpansion("a{b}", "ab"); | |
| 245 | } | |
| 246 | ||
| 247 | fn expectExpansion(test_input: []const u8, expected_result: []const u8) { | |
| 248 | var result = Buffer.initSize(global_allocator, 0) catch unreachable; | |
| 249 | defer result.deinit(); | |
| 250 | ||
| 251 | expandString(test_input, &result) catch unreachable; | |
| 252 | ||
| 253 | assert(mem.eql(u8, result.toSlice(), expected_result)); | |
| 254 | } |
test/standalone/issue_339/build.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | |
| 3 | pub fn build(b: &Builder) { | |
| 3 | pub fn build(b: &Builder) -> %void { | |
| 4 | 4 | const obj = b.addObject("test", "test.zig"); |
| 5 | 5 | |
| 6 | 6 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_339/test.zig+2-1| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} } | |
| 1 | const StackTrace = @import("builtin").StackTrace; | |
| 2 | pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} } | |
| 2 | 3 | |
| 3 | 4 | fn bar() -> %void {} |
| 4 | 5 |
test/standalone/pkg_import/build.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | |
| 3 | pub fn build(b: &Builder) { | |
| 3 | pub fn build(b: &Builder) -> %void { | |
| 4 | 4 | const exe = b.addExecutable("test", "test.zig"); |
| 5 | 5 | exe.addPackagePath("my_pkg", "pkg.zig"); |
| 6 | 6 |
test/standalone/use_alias/build.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | |
| 3 | pub fn build(b: &Builder) { | |
| 3 | pub fn build(b: &Builder) -> %void { | |
| 4 | 4 | b.addCIncludePath("."); |
| 5 | 5 | |
| 6 | 6 | const main = b.addTest("main.zig"); |
test/tests.zig+2-7| ... | ... | @@ -42,14 +42,10 @@ const test_targets = []TestTarget { |
| 42 | 42 | .arch = builtin.Arch.x86_64, |
| 43 | 43 | .environ = builtin.Environ.msvc, |
| 44 | 44 | }, |
| 45 | TestTarget { | |
| 46 | .os = builtin.Os.windows, | |
| 47 | .arch = builtin.Arch.i386, | |
| 48 | .environ = builtin.Environ.msvc, | |
| 49 | }, | |
| 50 | 45 | }; |
| 51 | 46 | |
| 52 | 47 | error TestFailed; |
| 48 | error CompilationIncorrectlySucceeded; | |
| 53 | 49 | |
| 54 | 50 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB |
| 55 | 51 | |
| ... | ... | @@ -607,8 +603,7 @@ pub const CompileErrorContext = struct { |
| 607 | 603 | switch (term) { |
| 608 | 604 | Term.Exited => |code| { |
| 609 | 605 | if (code == 0) { |
| 610 | warn("Compilation incorrectly succeeded\n"); | |
| 611 | return error.TestFailed; | |
| 606 | return error.CompilationIncorrectlySucceeded; | |
| 612 | 607 | } |
| 613 | 608 | }, |
| 614 | 609 | else => { |
test/translate_c.zig+26-5| ... | ... | @@ -408,7 +408,7 @@ pub fn addCases(cases: &tests.TranslateCContext) { |
| 408 | 408 | \\} |
| 409 | 409 | , |
| 410 | 410 | \\pub export fn s(a: c_int, b: c_int) -> c_int { |
| 411 | \\ var c: c_int; | |
| 411 | \\ var c: c_int = undefined; | |
| 412 | 412 | \\ c = (a + b); |
| 413 | 413 | \\ c = (a - b); |
| 414 | 414 | \\ c = (a * b); |
| ... | ... | @@ -416,7 +416,7 @@ pub fn addCases(cases: &tests.TranslateCContext) { |
| 416 | 416 | \\ c = @rem(a, b); |
| 417 | 417 | \\} |
| 418 | 418 | \\pub export fn u(a: c_uint, b: c_uint) -> c_uint { |
| 419 | \\ var c: c_uint; | |
| 419 | \\ var c: c_uint = undefined; | |
| 420 | 420 | \\ c = (a +% b); |
| 421 | 421 | \\ c = (a -% b); |
| 422 | 422 | \\ c = (a *% b); |
| ... | ... | @@ -460,7 +460,7 @@ pub fn addCases(cases: &tests.TranslateCContext) { |
| 460 | 460 | , |
| 461 | 461 | \\pub export fn max(_arg_a: c_int) -> c_int { |
| 462 | 462 | \\ var a = _arg_a; |
| 463 | \\ var tmp: c_int; | |
| 463 | \\ var tmp: c_int = undefined; | |
| 464 | 464 | \\ tmp = a; |
| 465 | 465 | \\ a = tmp; |
| 466 | 466 | \\} |
| ... | ... | @@ -473,8 +473,8 @@ pub fn addCases(cases: &tests.TranslateCContext) { |
| 473 | 473 | \\} |
| 474 | 474 | , |
| 475 | 475 | \\pub export fn max(a: c_int) { |
| 476 | \\ var b: c_int; | |
| 477 | \\ var c: c_int; | |
| 476 | \\ var b: c_int = undefined; | |
| 477 | \\ var c: c_int = undefined; | |
| 478 | 478 | \\ c = x: { |
| 479 | 479 | \\ const _tmp = a; |
| 480 | 480 | \\ b = _tmp; |
| ... | ... | @@ -1114,4 +1114,25 @@ pub fn addCases(cases: &tests.TranslateCContext) { |
| 1114 | 1114 | , |
| 1115 | 1115 | \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE); |
| 1116 | 1116 | ); |
| 1117 | ||
| 1118 | cases.add("if on int", | |
| 1119 | \\int if_int(int i) { | |
| 1120 | \\ if (i) { | |
| 1121 | \\ return 0; | |
| 1122 | \\ } else { | |
| 1123 | \\ return 1; | |
| 1124 | \\ } | |
| 1125 | \\} | |
| 1126 | , | |
| 1127 | \\pub fn if_int(i: c_int) -> c_int { | |
| 1128 | \\ { | |
| 1129 | \\ const _tmp = i; | |
| 1130 | \\ if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { | |
| 1131 | \\ return 0; | |
| 1132 | \\ } else { | |
| 1133 | \\ return 1; | |
| 1134 | \\ }; | |
| 1135 | \\ }; | |
| 1136 | \\} | |
| 1137 | ); | |
| 1117 | 1138 | } |