authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-04 11:58:31-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-04 11:58:31-04:00
logdbde8254d02241d893140cb499ad25e8cbb3438a
treeaf5be3723d32a378891fac4320e6a4b9a4edd674
parentf7f11e237c96a357e9a5e8b4a8ce2c6a7499de3b
parent2bd2a8ea3430b92f0c41d602d12982f776a9a524
signaturelock-open Commit is signed but in an unrecognized format.

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


62 files changed, 4721 insertions(+), 964 deletions(-)

CMakeLists.txt+6-2
......@@ -447,13 +447,17 @@ set(ZIG_STD_FILES
447447 "c/index.zig"
448448 "c/linux.zig"
449449 "c/windows.zig"
450 "coff.zig"
450451 "crypto/blake2.zig"
452 "crypto/chacha20.zig"
451453 "crypto/hmac.zig"
452454 "crypto/index.zig"
453455 "crypto/md5.zig"
454456 "crypto/sha1.zig"
455457 "crypto/sha2.zig"
456458 "crypto/sha3.zig"
459 "crypto/poly1305.zig"
460 "crypto/x25519.zig"
457461 "cstr.zig"
458462 "debug/failing_allocator.zig"
459463 "debug/index.zig"
......@@ -579,12 +583,12 @@ set(ZIG_STD_FILES
579583 "os/windows/error.zig"
580584 "os/windows/index.zig"
581585 "os/windows/kernel32.zig"
586 "os/windows/ntdll.zig"
582587 "os/windows/ole32.zig"
583588 "os/windows/shell32.zig"
584 "os/windows/shlwapi.zig"
585 "os/windows/user32.zig"
586589 "os/windows/util.zig"
587590 "os/zen.zig"
591 "pdb.zig"
588592 "rand/index.zig"
589593 "rand/ziggurat.zig"
590594 "segmented_list.zig"
doc/docgen.zig+2-2
......@@ -40,11 +40,11 @@ pub fn main() !void {
4040 var out_file = try os.File.openWrite(out_file_name);
4141 defer out_file.close();
4242
43 var file_in_stream = io.FileInStream.init(&in_file);
43 var file_in_stream = io.FileInStream.init(in_file);
4444
4545 const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);
4646
47 var file_out_stream = io.FileOutStream.init(&out_file);
47 var file_out_stream = io.FileOutStream.init(out_file);
4848 var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
4949
5050 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
doc/langref.html.in+170-2
......@@ -566,7 +566,7 @@ const c_string_literal =
566566 {#header_close#}
567567 {#header_close#}
568568 {#header_open|Assignment#}
569 <p>Use <code>const</code> to assign a value to an identifier:</p>
569 <p>Use the <code>const</code> keyword to assign a value to an identifier:</p>
570570 {#code_begin|test_err|cannot assign to constant#}
571571const x = 1234;
572572
......@@ -582,7 +582,8 @@ test "assignment" {
582582 foo();
583583}
584584 {#code_end#}
585 <p>If you need a variable that you can modify, use <code>var</code>:</p>
585 <p><code>const</code> applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p>
586 <p>If you need a variable that you can modify, use the <code>var</code> keyword:</p>
586587 {#code_begin|test#}
587588const assert = @import("std").debug.assert;
588589
......@@ -1918,6 +1919,32 @@ test "linked list" {
19181919 assert(list2.first.?.data == 1234);
19191920}
19201921 {#code_end#}
1922 {#header_open|struct Naming#}
1923 <p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>
1924 <ul>
1925 <li>If the struct is in the initialization expression of a variable, it gets named after
1926 that variable.</li>
1927 <li>If the struct is in the <code>return</code> expression, it gets named after
1928 the function it is returning from, with the parameter values serialized.</li>
1929 <li>Otherwise, the struct gets a same such as <code>(anonymous struct at file.zig:7:38)</code>.</li>
1930 </ul>
1931 {#code_begin|exe|struct_name#}
1932const std = @import("std");
1933
1934pub fn main() void {
1935 const Foo = struct {};
1936 std.debug.warn("variable: {}\n", @typeName(Foo));
1937 std.debug.warn("anonymous: {}\n", @typeName(struct {}));
1938 std.debug.warn("function: {}\n", @typeName(List(i32)));
1939}
1940
1941fn List(comptime T: type) type {
1942 return struct {
1943 x: T,
1944 };
1945}
1946 {#code_end#}
1947 {#header_close#}
19211948 {#see_also|comptime|@fieldParentPtr#}
19221949 {#header_close#}
19231950 {#header_open|enum#}
......@@ -2179,6 +2206,39 @@ test "@tagName" {
21792206 sorts the order of the tag and union field by the largest alignment.
21802207 </p>
21812208 {#header_close#}
2209 {#header_open|blocks#}
2210 <p>
2211 Blocks are used to limit the scope of variable declarations:
2212 </p>
2213 {#code_begin|test_err|undeclared identifier#}
2214test "access variable after block scope" {
2215 {
2216 var x: i32 = 1;
2217 }
2218 x += 1;
2219}
2220 {#code_end#}
2221 <p>Blocks are expressions. When labeled, <code>break</code> can be used
2222 to return a value from the block:
2223 </p>
2224 {#code_begin|test#}
2225const std = @import("std");
2226const assert = std.debug.assert;
2227
2228test "labeled break from labeled block expression" {
2229 var y: i32 = 123;
2230
2231 const x = blk: {
2232 y += 1;
2233 break :blk y;
2234 };
2235 assert(x == 124);
2236 assert(y == 124);
2237}
2238 {#code_end#}
2239 <p>Here, <code>blk</code> can be any name.</p>
2240 {#see_also|Labeled while|Labeled for#}
2241 {#header_close#}
21822242 {#header_open|switch#}
21832243 {#code_begin|test|switch#}
21842244const assert = @import("std").debug.assert;
......@@ -2374,6 +2434,28 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
23742434 } else false;
23752435}
23762436 {#code_end#}
2437 {#header_open|Labeled while#}
2438 <p>When a <code>while</code> loop is labeled, it can be referenced from a <code>break</code>
2439 or <code>continue</code> from within a nested loop:</p>
2440 {#code_begin|test#}
2441test "nested break" {
2442 outer: while (true) {
2443 while (true) {
2444 break :outer;
2445 }
2446 }
2447}
2448
2449test "nested continue" {
2450 var i: usize = 0;
2451 outer: while (i < 10) : (i += 1) {
2452 while (true) {
2453 continue :outer;
2454 }
2455 }
2456}
2457 {#code_end#}
2458 {#header_close#}
23772459 {#header_open|while with Optionals#}
23782460 <p>
23792461 Just like {#link|if#} expressions, while loops can take an optional as the
......@@ -2560,6 +2642,37 @@ test "for else" {
25602642 };
25612643}
25622644 {#code_end#}
2645 {#header_open|Labeled for#}
2646 <p>When a <code>for</code> loop is labeled, it can be referenced from a <code>break</code>
2647 or <code>continue</code> from within a nested loop:</p>
2648 {#code_begin|test#}
2649const std = @import("std");
2650const assert = std.debug.assert;
2651
2652test "nested break" {
2653 var count: usize = 0;
2654 outer: for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
2655 for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
2656 count += 1;
2657 break :outer;
2658 }
2659 }
2660 assert(count == 1);
2661}
2662
2663test "nested continue" {
2664 var count: usize = 0;
2665 outer: for ([]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
2666 for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
2667 count += 1;
2668 continue :outer;
2669 }
2670 }
2671
2672 assert(count == 8);
2673}
2674 {#code_end#}
2675 {#header_close#}
25632676 {#header_open|inline for#}
25642677 <p>
25652678 For loops can be inlined. This causes the loop to be unrolled, which
......@@ -7057,6 +7170,61 @@ const c = @cImport({
70577170});
70587171 {#code_end#}
70597172 {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}
7173 {#header_close#}
7174 {#header_open|Exporting a C Library#}
7175 <p>
7176 One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages
7177 to call into. The <code>export</code> keyword in front of functions, variables, and types causes them to
7178 be part of the library API:
7179 </p>
7180 <p class="file">mathtest.zig</p>
7181 {#code_begin|syntax#}
7182export fn add(a: i32, b: i32) i32 {
7183 return a + b;
7184}
7185 {#code_end#}
7186 <p>To make a shared library:</p>
7187 <pre><code class="shell">$ zig build-lib mathtest.zig
7188</code></pre>
7189 <p>To make a static library:</p>
7190 <pre><code class="shell">$ zig build-lib mathtest.zig --static
7191</code></pre>
7192 <p>Here is an example with the {#link|Zig Build System#}:</p>
7193 <p class="file">test.c</p>
7194 <pre><code class="cpp">// This header is generated by zig from mathtest.zig
7195#include "mathtest.h"
7196#include &lt;assert.h&gt;
7197
7198int main(int argc, char **argv) {
7199 assert(add(42, 1337) == 1379);
7200 return 0;
7201}</code></pre>
7202 <p class="file">build.zig</p>
7203 {#code_begin|syntax#}
7204const Builder = @import("std").build.Builder;
7205
7206pub fn build(b: *Builder) void {
7207 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
7208
7209 const exe = b.addCExecutable("test");
7210 exe.addCompileFlags([][]const u8{"-std=c99"});
7211 exe.addSourceFile("test.c");
7212 exe.linkLibrary(lib);
7213
7214 b.default_step.dependOn(&exe.step);
7215
7216 const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
7217 run_cmd.step.dependOn(&exe.step);
7218
7219 const test_step = b.step("test", "Test the program");
7220 test_step.dependOn(&run_cmd.step);
7221}
7222 {#code_end#}
7223 <p class="file">terminal</p>
7224 <pre><code class="shell">$ zig build
7225$ ./test
7226$ echo $?
72270</code></pre>
70607228 {#header_close#}
70617229 {#header_open|Mixing Object Files#}
70627230 <p>
example/guess_number/main.zig+1-1
......@@ -6,7 +6,7 @@ const os = std.os;
66
77pub fn main() !void {
88 var stdout_file = try io.getStdOut();
9 var stdout_file_stream = io.FileOutStream.init(&stdout_file);
9 var stdout_file_stream = io.FileOutStream.init(stdout_file);
1010 const stdout = &stdout_file_stream.stream;
1111
1212 try stdout.print("Welcome to the Guess Number Game in Zig.\n");
src-self-hosted/errmsg.zig+1-1
......@@ -272,7 +272,7 @@ pub const Msg = struct {
272272 try stream.write("\n");
273273 }
274274
275 pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
275 pub fn printToFile(msg: *const Msg, file: os.File, color: Color) !void {
276276 const color_on = switch (color) {
277277 Color.Auto => file.isTty(),
278278 Color.On => true,
src-self-hosted/main.zig+6-6
......@@ -55,11 +55,11 @@ pub fn main() !void {
5555 const allocator = std.heap.c_allocator;
5656
5757 var stdout_file = try std.io.getStdOut();
58 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
58 var stdout_out_stream = std.io.FileOutStream.init(stdout_file);
5959 stdout = &stdout_out_stream.stream;
6060
6161 stderr_file = try std.io.getStdErr();
62 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
62 var stderr_out_stream = std.io.FileOutStream.init(stderr_file);
6363 stderr = &stderr_out_stream.stream;
6464
6565 const args = try os.argsAlloc(allocator);
......@@ -491,7 +491,7 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
491491 stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
492492 for (msgs) |msg| {
493493 defer msg.destroy();
494 msg.printToFile(&stderr_file, color) catch os.exit(1);
494 msg.printToFile(stderr_file, color) catch os.exit(1);
495495 }
496496 },
497497 }
......@@ -619,7 +619,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
619619 }
620620
621621 var stdin_file = try io.getStdIn();
622 var stdin = io.FileInStream.init(&stdin_file);
622 var stdin = io.FileInStream.init(stdin_file);
623623
624624 const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
625625 defer allocator.free(source_code);
......@@ -635,7 +635,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
635635 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
636636 defer msg.destroy();
637637
638 try msg.printToFile(&stderr_file, color);
638 try msg.printToFile(stderr_file, color);
639639 }
640640 if (tree.errors.len != 0) {
641641 os.exit(1);
......@@ -772,7 +772,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
772772 const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
773773 defer fmt.loop.allocator.destroy(msg);
774774
775 try msg.printToFile(&stderr_file, fmt.color);
775 try msg.printToFile(stderr_file, fmt.color);
776776 }
777777 if (tree.errors.len != 0) {
778778 fmt.any_error = true;
src-self-hosted/test.zig+2-2
......@@ -185,7 +185,7 @@ pub const TestContext = struct {
185185 try stderr.write("build incorrectly failed:\n");
186186 for (msgs) |msg| {
187187 defer msg.destroy();
188 try msg.printToFile(&stderr, errmsg.Color.Auto);
188 try msg.printToFile(stderr, errmsg.Color.Auto);
189189 }
190190 },
191191 }
......@@ -234,7 +234,7 @@ pub const TestContext = struct {
234234 var stderr = try std.io.getStdErr();
235235 for (msgs) |msg| {
236236 defer msg.destroy();
237 try msg.printToFile(&stderr, errmsg.Color.Auto);
237 try msg.printToFile(stderr, errmsg.Color.Auto);
238238 }
239239 std.debug.warn("============\n");
240240 return error.TestFailed;
src/all_types.hpp+6
......@@ -43,6 +43,7 @@ struct IrAnalyze;
4343struct IrExecutable {
4444 ZigList<IrBasicBlock *> basic_block_list;
4545 Buf *name;
46 FnTableEntry *name_fn;
4647 size_t mem_slot_count;
4748 size_t next_debug_id;
4849 size_t *backward_branch_count;
......@@ -1805,6 +1806,11 @@ struct VariableTableEntry {
18051806 VarLinkage linkage;
18061807 IrInstruction *decl_instruction;
18071808 uint32_t align_bytes;
1809
1810 // In an inline loop, multiple variables may be created,
1811 // In this case, a reference to a variable should follow
1812 // this pointer to the redefined variable.
1813 VariableTableEntry *next_var;
18081814};
18091815
18101816struct ErrorTableEntry {
src/analyze.cpp+13-11
......@@ -1575,7 +1575,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15751575
15761576 switch (type_entry->id) {
15771577 case TypeTableEntryIdInvalid:
1578 return g->builtin_types.entry_invalid;
1578 zig_unreachable();
15791579 case TypeTableEntryIdUnreachable:
15801580 case TypeTableEntryIdUndefined:
15811581 case TypeTableEntryIdNull:
......@@ -1680,14 +1680,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
16801680 case TypeTableEntryIdBlock:
16811681 case TypeTableEntryIdBoundFn:
16821682 case TypeTableEntryIdMetaType:
1683 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
1684 add_node_error(g, fn_proto->return_type,
1685 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
1686 buf_ptr(&fn_type_id.return_type->name),
1687 calling_convention_name(fn_type_id.cc)));
1688 return g->builtin_types.entry_invalid;
1689 }
1690 return get_generic_fn_type(g, &fn_type_id);
16911683 case TypeTableEntryIdUnreachable:
16921684 case TypeTableEntryIdVoid:
16931685 case TypeTableEntryIdBool:
......@@ -1703,6 +1695,11 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
17031695 case TypeTableEntryIdUnion:
17041696 case TypeTableEntryIdFn:
17051697 case TypeTableEntryIdPromise:
1698 if ((err = type_ensure_zero_bits_known(g, fn_type_id.return_type)))
1699 return g->builtin_types.entry_invalid;
1700 if (type_requires_comptime(fn_type_id.return_type)) {
1701 return get_generic_fn_type(g, &fn_type_id);
1702 }
17061703 break;
17071704 }
17081705
......@@ -3245,6 +3242,13 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
32453242 } else if (tld->id == TldIdFn) {
32463243 assert(tld->source_node->type == NodeTypeFnProto);
32473244 is_export = tld->source_node->data.fn_proto.is_export;
3245
3246 if (!is_export && !tld->source_node->data.fn_proto.is_extern &&
3247 tld->source_node->data.fn_proto.fn_def_node == nullptr)
3248 {
3249 add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body"));
3250 return;
3251 }
32483252 }
32493253 if (is_export) {
32503254 g->resolve_queue.append(tld);
......@@ -5620,8 +5624,6 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
56205624 if (type_entry->id == TypeTableEntryIdInt) {
56215625 const_val->special = ConstValSpecialStatic;
56225626 eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max);
5623 } else if (type_entry->id == TypeTableEntryIdFloat) {
5624 zig_panic("TODO analyze_min_max_value float");
56255627 } else if (type_entry->id == TypeTableEntryIdBool) {
56265628 const_val->special = ConstValSpecialStatic;
56275629 const_val->data.x_bool = is_max;
src/codegen.cpp+12-1
......@@ -2618,6 +2618,9 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
26182618 IrInstructionPtrCast *instruction)
26192619{
26202620 TypeTableEntry *wanted_type = instruction->base.value.type;
2621 if (!type_has_bits(wanted_type)) {
2622 return nullptr;
2623 }
26212624 LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
26222625 return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
26232626}
......@@ -3036,6 +3039,12 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) {
30363039 LLVMBuildCall(g->builder, write_register_fn_val, params, 2, "");
30373040}
30383041
3042static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) {
3043 unsigned attr_kind_id = LLVMGetEnumAttributeKindForName("sret", 4);
3044 LLVMAttributeRef sret_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), attr_kind_id, 1);
3045 LLVMAddCallSiteAttribute(call_instr, 1, sret_attr);
3046}
3047
30393048static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) {
30403049 LLVMValueRef fn_val;
30413050 TypeTableEntry *fn_type;
......@@ -3131,6 +3140,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
31313140 } else if (!ret_has_bits) {
31323141 return nullptr;
31333142 } else if (first_arg_ret) {
3143 set_call_instr_sret(g, result);
31343144 return instruction->tmp_ptr;
31353145 } else if (handle_is_ptr(src_return_type)) {
31363146 auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr);
......@@ -4573,8 +4583,9 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f
45734583 args.append(allocator_val);
45744584 args.append(coro_size);
45754585 args.append(alignment_val);
4576 ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length,
4586 LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length,
45774587 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
4588 set_call_instr_sret(g, call_instruction);
45784589 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, "");
45794590 LLVMValueRef err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
45804591 LLVMBuildStore(g->builder, err_val, err_code_ptr);
src/ir.cpp+173-135
......@@ -17,8 +17,7 @@
1717#include "util.hpp"
1818
1919struct IrExecContext {
20 ConstExprValue *mem_slot_list;
21 size_t mem_slot_count;
20 ZigList<ConstExprValue *> mem_slot_list;
2221};
2322
2423struct IrBuilder {
......@@ -60,7 +59,7 @@ enum ConstCastResultId {
6059 ConstCastResultIdType,
6160 ConstCastResultIdUnresolvedInferredErrSet,
6261 ConstCastResultIdAsyncAllocatorType,
63 ConstCastResultIdNullWrapPtr,
62 ConstCastResultIdNullWrapPtr
6463};
6564
6665struct ConstCastOnly;
......@@ -155,18 +154,22 @@ static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type
155154ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
156155 assert(get_codegen_ptr_type(const_val->type) != nullptr);
157156 assert(const_val->special == ConstValSpecialStatic);
157 ConstExprValue *result;
158158 switch (const_val->data.x_ptr.special) {
159159 case ConstPtrSpecialInvalid:
160160 zig_unreachable();
161161 case ConstPtrSpecialRef:
162 return const_val->data.x_ptr.data.ref.pointee;
162 result = const_val->data.x_ptr.data.ref.pointee;
163 break;
163164 case ConstPtrSpecialBaseArray:
164165 expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val);
165 return &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[
166 result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[
166167 const_val->data.x_ptr.data.base_array.elem_index];
168 break;
167169 case ConstPtrSpecialBaseStruct:
168 return &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[
170 result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[
169171 const_val->data.x_ptr.data.base_struct.field_index];
172 break;
170173 case ConstPtrSpecialHardCodedAddr:
171174 zig_unreachable();
172175 case ConstPtrSpecialDiscard:
......@@ -174,7 +177,8 @@ ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
174177 case ConstPtrSpecialFunction:
175178 zig_unreachable();
176179 }
177 zig_unreachable();
180 assert(result != nullptr);
181 return result;
178182}
179183
180184static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
......@@ -3181,7 +3185,11 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
31813185 {
31823186 IrInstruction *return_value;
31833187 if (expr_node) {
3188 // Temporarily set this so that if we return a type it gets the name of the function
3189 FnTableEntry *prev_name_fn = irb->exec->name_fn;
3190 irb->exec->name_fn = exec_fn_entry(irb->exec);
31843191 return_value = ir_gen_node(irb, expr_node, scope);
3192 irb->exec->name_fn = prev_name_fn;
31853193 if (return_value == irb->codegen->invalid_instruction)
31863194 return irb->codegen->invalid_instruction;
31873195 } else {
......@@ -3275,7 +3283,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
32753283}
32763284
32773285static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
3278 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
3286 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,
3287 bool skip_name_check)
32793288{
32803289 VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
32813290 variable_entry->parent_scope = parent_scope;
......@@ -3288,29 +3297,30 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco
32883297 if (name) {
32893298 buf_init_from_buf(&variable_entry->name, name);
32903299
3291 VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name);
3292 if (existing_var && !existing_var->shadowable) {
3293 ErrorMsg *msg = add_node_error(codegen, node,
3294 buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
3295 add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
3296 variable_entry->value->type = codegen->builtin_types.entry_invalid;
3297 } else {
3298 TypeTableEntry *type = get_primitive_type(codegen, name);
3299 if (type != nullptr) {
3300 add_node_error(codegen, node,
3301 buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
3300 if (!skip_name_check) {
3301 VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name);
3302 if (existing_var && !existing_var->shadowable) {
3303 ErrorMsg *msg = add_node_error(codegen, node,
3304 buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
3305 add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
33023306 variable_entry->value->type = codegen->builtin_types.entry_invalid;
33033307 } else {
3304 Tld *tld = find_decl(codegen, parent_scope, name);
3305 if (tld != nullptr) {
3306 ErrorMsg *msg = add_node_error(codegen, node,
3307 buf_sprintf("redefinition of '%s'", buf_ptr(name)));
3308 add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
3308 TypeTableEntry *type = get_primitive_type(codegen, name);
3309 if (type != nullptr) {
3310 add_node_error(codegen, node,
3311 buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
33093312 variable_entry->value->type = codegen->builtin_types.entry_invalid;
3313 } else {
3314 Tld *tld = find_decl(codegen, parent_scope, name);
3315 if (tld != nullptr) {
3316 ErrorMsg *msg = add_node_error(codegen, node,
3317 buf_sprintf("redefinition of '%s'", buf_ptr(name)));
3318 add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
3319 variable_entry->value->type = codegen->builtin_types.entry_invalid;
3320 }
33103321 }
33113322 }
33123323 }
3313
33143324 } else {
33153325 assert(is_shadowable);
33163326 // TODO make this name not actually be in scope. user should be able to make a variable called "_anon"
......@@ -3333,14 +3343,9 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s
33333343 bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
33343344{
33353345 bool is_underscored = name ? buf_eql_str(name, "_") : false;
3336 VariableTableEntry *var = create_local_var( irb->codegen
3337 , node
3338 , scope
3339 , (is_underscored ? nullptr : name)
3340 , src_is_const
3341 , gen_is_const
3342 , (is_underscored ? true : is_shadowable)
3343 , is_comptime );
3346 VariableTableEntry *var = create_local_var(irb->codegen, node, scope,
3347 (is_underscored ? nullptr : name), src_is_const, gen_is_const,
3348 (is_underscored ? true : is_shadowable), is_comptime, false);
33443349 if (is_comptime != nullptr || gen_is_const) {
33453350 var->mem_slot_index = exec_next_mem_slot(irb->exec);
33463351 var->owner_exec = irb->exec;
......@@ -6479,20 +6484,17 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o
64796484static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, AstNode *source_node) {
64806485 if (exec->name) {
64816486 return exec->name;
6487 } else if (exec->name_fn != nullptr) {
6488 Buf *name = buf_alloc();
6489 buf_append_buf(name, &exec->name_fn->symbol_name);
6490 buf_appendf(name, "(");
6491 render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
6492 buf_appendf(name, ")");
6493 return name;
64826494 } else {
6483 FnTableEntry *fn_entry = exec_fn_entry(exec);
6484 if (fn_entry) {
6485 Buf *name = buf_alloc();
6486 buf_append_buf(name, &fn_entry->symbol_name);
6487 buf_appendf(name, "(");
6488 render_instance_name_recursive(codegen, name, &fn_entry->fndef_scope->base, exec->begin_scope);
6489 buf_appendf(name, ")");
6490 return name;
6491 } else {
6492 //Note: C-imports do not have valid location information
6493 return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
6494 (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
6495 }
6495 //Note: C-imports do not have valid location information
6496 return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
6497 (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
64966498 }
64976499}
64986500
......@@ -6690,7 +6692,10 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
66906692 return irb->codegen->invalid_instruction;
66916693 }
66926694 } else {
6693 return_type = nullptr;
6695 add_node_error(irb->codegen, node,
6696 buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
6697 return irb->codegen->invalid_instruction;
6698 //return_type = nullptr;
66946699 }
66956700
66966701 IrInstruction *async_allocator_type_value = nullptr;
......@@ -8466,9 +8471,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
84668471 if (wanted_type == actual_type)
84678472 return result;
84688473
8469 // * and [*] can do a const-cast-only to ?* and ?[*], respectively
8470 // but not if there is a mutable parent pointer
8471 // and not if the pointer is zero bits
8474 // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively
8475 // but not if we want a mutable pointer
8476 // and not if the actual pointer has zero bits
84728477 if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional &&
84738478 wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
84748479 actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type))
......@@ -8483,6 +8488,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
84838488 return result;
84848489 }
84858490
8491 // *T and [*]T can always cast to *c_void
8492 if (wanted_type->id == TypeTableEntryIdPointer &&
8493 wanted_type->data.pointer.ptr_len == PtrLenSingle &&
8494 wanted_type->data.pointer.child_type == g->builtin_types.entry_c_void &&
8495 actual_type->id == TypeTableEntryIdPointer &&
8496 (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
8497 (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile))
8498 {
8499 assert(actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment);
8500 return result;
8501 }
8502
84868503 // pointer const
84878504 if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) {
84888505 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
......@@ -12478,6 +12495,24 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
1247812495 }
1247912496 }
1248012497
12498 if (var->value->type != nullptr && !is_comptime_var) {
12499 // This is at least the second time we've seen this variable declaration during analysis.
12500 // This means that this is actually a different variable due to, e.g. an inline while loop.
12501 // We make a new variable so that it can hold a different type, and so the debug info can
12502 // be distinct.
12503 VariableTableEntry *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope,
12504 &var->name, var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true);
12505 new_var->owner_exec = var->owner_exec;
12506 if (var->mem_slot_index != SIZE_MAX) {
12507 ConstExprValue *vals = create_const_vals(1);
12508 new_var->mem_slot_index = ira->exec_context.mem_slot_list.length;
12509 ira->exec_context.mem_slot_list.append(vals);
12510 }
12511
12512 var->next_var = new_var;
12513 var = new_var;
12514 }
12515
1248112516 var->value->type = result_type;
1248212517 assert(var->value->type);
1248312518
......@@ -12496,10 +12531,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
1249612531
1249712532 if (casted_init_value->value.special != ConstValSpecialRuntime) {
1249812533 if (var->mem_slot_index != SIZE_MAX) {
12499 assert(var->mem_slot_index < ira->exec_context.mem_slot_count);
12500 ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
12501 copy_const_val(mem_slot, &casted_init_value->value,
12502 !is_comptime_var || var->gen_is_const);
12534 assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
12535 ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
12536 copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const);
1250312537
1250412538 if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) {
1250512539 ir_build_const_from(ira, &decl_var_instruction->base);
......@@ -12960,6 +12994,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
1296012994 VariableTableEntry *var)
1296112995{
1296212996 Error err;
12997 while (var->next_var != nullptr) {
12998 var = var->next_var;
12999 }
13000
1296313001 if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {
1296413002 assert(ira->codegen->errors.length != 0);
1296513003 return ira->codegen->invalid_instruction;
......@@ -12979,8 +13017,8 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
1297913017 assert(var->owner_exec != nullptr);
1298013018 assert(var->owner_exec->analysis != nullptr);
1298113019 IrExecContext *exec_context = &var->owner_exec->analysis->exec_context;
12982 assert(var->mem_slot_index < exec_context->mem_slot_count);
12983 mem_slot = &exec_context->mem_slot_list[var->mem_slot_index];
13020 assert(var->mem_slot_index < exec_context->mem_slot_list.length);
13021 mem_slot = exec_context->mem_slot_list.at(var->mem_slot_index);
1298413022 }
1298513023 }
1298613024
......@@ -14439,8 +14477,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
1443914477 ConstExprValue *payload_val = union_val->data.x_union.payload;
1444014478
1444114479 TypeTableEntry *field_type = field->type_entry;
14442 if (field_type->id == TypeTableEntryIdVoid)
14443 {
14480 if (field_type->id == TypeTableEntryIdVoid) {
1444414481 assert(payload_val == nullptr);
1444514482 payload_val = create_const_vals(1);
1444614483 payload_val->special = ConstValSpecialStatic;
......@@ -16445,12 +16482,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
1644516482 eval_min_max_value(ira->codegen, target_type, out_val, is_max);
1644616483 return ira->codegen->builtin_types.entry_num_lit_int;
1644716484 }
16448 case TypeTableEntryIdFloat:
16449 {
16450 ConstExprValue *out_val = ir_build_const_from(ira, source_instruction);
16451 eval_min_max_value(ira->codegen, target_type, out_val, is_max);
16452 return ira->codegen->builtin_types.entry_num_lit_float;
16453 }
1645416485 case TypeTableEntryIdBool:
1645516486 case TypeTableEntryIdVoid:
1645616487 {
......@@ -16459,7 +16490,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
1645916490 return target_type;
1646016491 }
1646116492 case TypeTableEntryIdEnum:
16462 zig_panic("TODO min/max value for enum type");
16493 case TypeTableEntryIdFloat:
1646316494 case TypeTableEntryIdMetaType:
1646416495 case TypeTableEntryIdUnreachable:
1646516496 case TypeTableEntryIdPointer:
......@@ -16792,12 +16823,11 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na
1679216823 return var->value->data.x_type;
1679316824}
1679416825
16795static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope)
16796{
16826static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) {
1679716827 Error err;
1679816828 TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr);
1679916829 if ((err = ensure_complete_type(ira->codegen, type_info_definition_type)))
16800 return false;
16830 return err;
1680116831
1680216832 ensure_field_index(type_info_definition_type, "name", 0);
1680316833 ensure_field_index(type_info_definition_type, "is_pub", 1);
......@@ -16805,38 +16835,33 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1680516835
1680616836 TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);
1680716837 if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type)))
16808 return false;
16838 return err;
1680916839
1681016840 TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);
1681116841 if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type)))
16812 return false;
16842 return err;
1681316843
1681416844 TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);
1681516845 if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type)))
16816 return false;
16846 return err;
1681716847
1681816848 // Loop through our definitions once to figure out how many definitions we will generate info for.
1681916849 auto decl_it = decls_scope->decl_table.entry_iterator();
1682016850 decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
1682116851 int definition_count = 0;
1682216852
16823 while ((curr_entry = decl_it.next()) != nullptr)
16824 {
16853 while ((curr_entry = decl_it.next()) != nullptr) {
1682516854 // If the definition is unresolved, force it to be resolved again.
16826 if (curr_entry->value->resolution == TldResolutionUnresolved)
16827 {
16855 if (curr_entry->value->resolution == TldResolutionUnresolved) {
1682816856 resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node);
16829 if (curr_entry->value->resolution != TldResolutionOk)
16830 {
16831 return false;
16857 if (curr_entry->value->resolution != TldResolutionOk) {
16858 return ErrorSemanticAnalyzeFail;
1683216859 }
1683316860 }
1683416861
1683516862 // Skip comptime blocks and test functions.
16836 if (curr_entry->value->id != TldIdCompTime)
16837 {
16838 if (curr_entry->value->id == TldIdFn)
16839 {
16863 if (curr_entry->value->id != TldIdCompTime) {
16864 if (curr_entry->value->id == TldIdFn) {
1684016865 FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
1684116866 if (fn_entry->is_test)
1684216867 continue;
......@@ -16858,13 +16883,11 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1685816883 decl_it = decls_scope->decl_table.entry_iterator();
1685916884 curr_entry = nullptr;
1686016885 int definition_index = 0;
16861 while ((curr_entry = decl_it.next()) != nullptr)
16862 {
16886 while ((curr_entry = decl_it.next()) != nullptr) {
1686316887 // Skip comptime blocks and test functions.
16864 if (curr_entry->value->id == TldIdCompTime)
16888 if (curr_entry->value->id == TldIdCompTime) {
1686516889 continue;
16866 else if (curr_entry->value->id == TldIdFn)
16867 {
16890 } else if (curr_entry->value->id == TldIdFn) {
1686816891 FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
1686916892 if (fn_entry->is_test)
1687016893 continue;
......@@ -16887,13 +16910,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1688716910 inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val;
1688816911 inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1;
1688916912
16890 switch (curr_entry->value->id)
16891 {
16913 switch (curr_entry->value->id) {
1689216914 case TldIdVar:
1689316915 {
1689416916 VariableTableEntry *var = ((TldVar *)curr_entry->value)->var;
1689516917 if ((err = ensure_complete_type(ira->codegen, var->value->type)))
16896 return false;
16918 return ErrorSemanticAnalyzeFail;
1689716919
1689816920 if (var->value->type->id == TypeTableEntryIdMetaType)
1689916921 {
......@@ -17024,7 +17046,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1702417046 {
1702517047 TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
1702617048 if ((err = ensure_complete_type(ira->codegen, type_entry)))
17027 return false;
17049 return ErrorSemanticAnalyzeFail;
1702817050
1702917051 // This is a type.
1703017052 bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
......@@ -17046,7 +17068,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1704617068 }
1704717069
1704817070 assert(definition_index == definition_count);
17049 return true;
17071 return ErrorNone;
1705017072}
1705117073
1705217074static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry *ptr_type_entry) {
......@@ -17104,30 +17126,31 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry
1710417126 return result;
1710517127};
1710617128
17107static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) {
17108 Error err;
17109 assert(type_entry != nullptr);
17110 assert(!type_is_invalid(type_entry));
17129static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field,
17130 TypeTableEntry *type_info_enum_field_type)
17131{
17132 enum_field_val->special = ConstValSpecialStatic;
17133 enum_field_val->type = type_info_enum_field_type;
1711117134
17112 if ((err = ensure_complete_type(ira->codegen, type_entry)))
17113 return nullptr;
17135 ConstExprValue *inner_fields = create_const_vals(2);
17136 inner_fields[1].special = ConstValSpecialStatic;
17137 inner_fields[1].type = ira->codegen->builtin_types.entry_usize;
1711417138
17115 const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field,
17116 TypeTableEntry *type_info_enum_field_type) {
17117 enum_field_val->special = ConstValSpecialStatic;
17118 enum_field_val->type = type_info_enum_field_type;
17139 ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
17140 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
1711917141
17120 ConstExprValue *inner_fields = create_const_vals(2);
17121 inner_fields[1].special = ConstValSpecialStatic;
17122 inner_fields[1].type = ira->codegen->builtin_types.entry_usize;
17142 bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
1712317143
17124 ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
17125 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
17144 enum_field_val->data.x_struct.fields = inner_fields;
17145}
1712617146
17127 bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
17147static Error ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry, ConstExprValue **out) {
17148 Error err;
17149 assert(type_entry != nullptr);
17150 assert(!type_is_invalid(type_entry));
1712817151
17129 enum_field_val->data.x_struct.fields = inner_fields;
17130 };
17152 if ((err = ensure_complete_type(ira->codegen, type_entry)))
17153 return err;
1713117154
1713217155 if (type_entry == ira->codegen->builtin_types.entry_global_error_set) {
1713317156 zig_panic("TODO implement @typeInfo for global error set");
......@@ -17150,13 +17173,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1715017173 case TypeTableEntryIdBlock:
1715117174 case TypeTableEntryIdArgTuple:
1715217175 case TypeTableEntryIdOpaque:
17153 return nullptr;
17176 *out = nullptr;
17177 return ErrorNone;
1715417178 default:
1715517179 {
1715617180 // Lookup an available value in our cache.
1715717181 auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
17158 if (entry != nullptr)
17159 return entry->value;
17182 if (entry != nullptr) {
17183 *out = entry->value;
17184 return ErrorNone;
17185 }
1716017186
1716117187 // Fallthrough if we don't find one.
1716217188 }
......@@ -17307,15 +17333,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1730717333 {
1730817334 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index];
1730917335 ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index];
17310 make_enum_field_val(enum_field_val, enum_field, type_info_enum_field_type);
17336 make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type);
1731117337 enum_field_val->data.x_struct.parent.id = ConstParentIdArray;
1731217338 enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array;
1731317339 enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index;
1731417340 }
1731517341 // defs: []TypeInfo.Definition
1731617342 ensure_field_index(result->type, "defs", 3);
17317 if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope))
17318 return nullptr;
17343 if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope)))
17344 return err;
1731917345
1732017346 break;
1732117347 }
......@@ -17341,8 +17367,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1734117367 error_array->data.x_array.s_none.elements = create_const_vals(error_count);
1734217368
1734317369 init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false);
17344 for (uint32_t error_index = 0; error_index < error_count; error_index++)
17345 {
17370 for (uint32_t error_index = 0; error_index < error_count; error_index++) {
1734617371 ErrorTableEntry *error = type_entry->data.error_set.errors[error_index];
1734717372 ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index];
1734817373
......@@ -17420,9 +17445,9 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1742017445 tag_type->type = ira->codegen->builtin_types.entry_type;
1742117446 tag_type->data.x_type = type_entry->data.unionation.tag_type;
1742217447 fields[1].data.x_optional = tag_type;
17423 }
17424 else
17448 } else {
1742517449 fields[1].data.x_optional = nullptr;
17450 }
1742617451 // fields: []TypeInfo.UnionField
1742717452 ensure_field_index(result->type, "fields", 2);
1742817453
......@@ -17455,7 +17480,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1745517480 inner_fields[1].data.x_optional = nullptr;
1745617481 } else {
1745717482 inner_fields[1].data.x_optional = create_const_vals(1);
17458 make_enum_field_val(inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
17483 make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
1745917484 }
1746017485
1746117486 inner_fields[2].special = ConstValSpecialStatic;
......@@ -17472,8 +17497,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1747217497 }
1747317498 // defs: []TypeInfo.Definition
1747417499 ensure_field_index(result->type, "defs", 3);
17475 if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope))
17476 return nullptr;
17500 if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope)))
17501 return err;
1747717502
1747817503 break;
1747917504 }
......@@ -17546,8 +17571,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1754617571 }
1754717572 // defs: []TypeInfo.Definition
1754817573 ensure_field_index(result->type, "defs", 2);
17549 if (!ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope))
17550 return nullptr;
17574 if ((err = ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope)))
17575 return err;
1755117576
1755217577 break;
1755317578 }
......@@ -17660,7 +17685,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1766017685 {
1766117686 TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type;
1766217687 assert(fn_type->id == TypeTableEntryIdFn);
17663 result = ir_make_type_info_value(ira, fn_type);
17688 if ((err = ir_make_type_info_value(ira, fn_type, &result)))
17689 return err;
1766417690
1766517691 break;
1766617692 }
......@@ -17668,12 +17694,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1766817694
1766917695 assert(result != nullptr);
1767017696 ira->codegen->type_info_cache.put(type_entry, result);
17671 return result;
17697 *out = result;
17698 return ErrorNone;
1767217699}
1767317700
1767417701static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
1767517702 IrInstructionTypeInfo *instruction)
1767617703{
17704 Error err;
1767717705 IrInstruction *type_value = instruction->type_value->other;
1767817706 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
1767917707 if (type_is_invalid(type_entry))
......@@ -17681,15 +17709,16 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
1768117709
1768217710 TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr, nullptr);
1768317711
17712 ConstExprValue *payload;
17713 if ((err = ir_make_type_info_value(ira, type_entry, &payload)))
17714 return ira->codegen->builtin_types.entry_invalid;
17715
1768417716 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
1768517717 out_val->type = result_type;
1768617718 bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry));
17687
17688 ConstExprValue *payload = ir_make_type_info_value(ira, type_entry);
1768917719 out_val->data.x_union.payload = payload;
1769017720
17691 if (payload != nullptr)
17692 {
17721 if (payload != nullptr) {
1769317722 assert(payload->type->id == TypeTableEntryIdStruct);
1769417723 payload->data.x_struct.parent.id = ConstParentIdUnion;
1769517724 payload->data.x_struct.parent.data.p_union.union_val = out_val;
......@@ -19731,6 +19760,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
1973119760}
1973219761
1973319762static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) {
19763 Error err;
19764
1973419765 IrInstruction *dest_type_value = instruction->dest_type->other;
1973519766 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
1973619767 if (type_is_invalid(dest_type))
......@@ -19784,9 +19815,13 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc
1978419815 instruction->base.source_node, nullptr, ptr);
1978519816 casted_ptr->value.type = dest_type;
1978619817
19787 // keep the bigger alignment, it can only help
19818 // Keep the bigger alignment, it can only help-
19819 // unless the target is zero bits.
19820 if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
19821 return ira->codegen->builtin_types.entry_invalid;
19822
1978819823 IrInstruction *result;
19789 if (src_align_bytes > dest_align_bytes) {
19824 if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
1979019825 result = ir_align_cast(ira, casted_ptr, src_align_bytes, false);
1979119826 if (type_is_invalid(result->value.type))
1979219827 return ira->codegen->builtin_types.entry_invalid;
......@@ -21192,8 +21227,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
2119221227 ira->new_irb.codegen = codegen;
2119321228 ira->new_irb.exec = new_exec;
2119421229
21195 ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count;
21196 ira->exec_context.mem_slot_list = create_const_vals(ira->exec_context.mem_slot_count);
21230 ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count);
21231 ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count);
21232 for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) {
21233 ira->exec_context.mem_slot_list.items[i] = &vals[i];
21234 }
2119721235
2119821236 IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
2119921237 IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr);
src/translate_c.cpp+10-11
......@@ -3075,12 +3075,19 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
30753075 trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
30763076 case Stmt::DeclStmtClass:
30773077 return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
3078 case Stmt::DoStmtClass:
30783079 case Stmt::WhileStmtClass: {
3079 AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt);
3080 AstNode *while_node = sc == Stmt::DoStmtClass
3081 ? trans_do_loop(c, scope, (const DoStmt *)stmt)
3082 : trans_while_loop(c, scope, (const WhileStmt *)stmt);
3083
3084 if (while_node == nullptr)
3085 return ErrorUnexpected;
3086
30803087 assert(while_node->type == NodeTypeWhileExpr);
3081 if (while_node->data.while_expr.body == nullptr) {
3088 if (while_node->data.while_expr.body == nullptr)
30823089 while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
3083 }
3090
30843091 return wrap_stmt(out_node, out_child_scope, scope, while_node);
30853092 }
30863093 case Stmt::IfStmtClass:
......@@ -3105,14 +3112,6 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
31053112 case Stmt::UnaryExprOrTypeTraitExprClass:
31063113 return wrap_stmt(out_node, out_child_scope, scope,
31073114 trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
3108 case Stmt::DoStmtClass: {
3109 AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt);
3110 assert(while_node->type == NodeTypeWhileExpr);
3111 if (while_node->data.while_expr.body == nullptr) {
3112 while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
3113 }
3114 return wrap_stmt(out_node, out_child_scope, scope, while_node);
3115 }
31163115 case Stmt::ForStmtClass: {
31173116 AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt);
31183117 return wrap_stmt(out_node, out_child_scope, scope, node);
src/windows_sdk.cpp+7-2
......@@ -204,7 +204,11 @@ static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
204204 // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
205205 c2 = 26624;
206206 }
207 if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
207
208 if ( (c0 > v0)
209 || (c0 == v0 && c1 > v1)
210 || (c0 == v0 && c1 == v1 && c2 > v2)
211 || (c0 == v0 && c1 == v1 && c2 == v2 && c3 > v3) ) {
208212 v0 = c0, v1 = c1, v2 = c2, v3 = c3;
209213 free((void*)priv->base.version10_ptr);
210214 priv->base.version10_ptr = strdup(ffd.cFileName);
......@@ -244,7 +248,8 @@ static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
244248 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
245249 int c0 = 0, c1 = 0;
246250 sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
247 if ((c0 > v0) || (c1 > v1)) {
251
252 if ( (c0 > v0) || (c0 == v0 && c1 > v1) ) {
248253 v0 = c0, v1 = c1;
249254 free((void*)priv->base.version81_ptr);
250255 priv->base.version81_ptr = strdup(ffd.cFileName);
std/coff.zig created+230
......@@ -0,0 +1,230 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const mem = std.mem;
5const os = std.os;
6
7const ArrayList = std.ArrayList;
8
9// CoffHeader.machine values
10// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
11const IMAGE_FILE_MACHINE_I386 = 0x014c;
12const IMAGE_FILE_MACHINE_IA64 = 0x0200;
13const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
14
15// OptionalHeader.magic values
16// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
17const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
18const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
19
20const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
21const DEBUG_DIRECTORY = 6;
22
23pub const CoffError = error {
24 InvalidPEMagic,
25 InvalidPEHeader,
26 InvalidMachine,
27 MissingCoffSection,
28};
29
30pub const Coff = struct {
31 in_file: os.File,
32 allocator: *mem.Allocator,
33
34 coff_header: CoffHeader,
35 pe_header: OptionalHeader,
36 sections: ArrayList(Section),
37
38 guid: [16]u8,
39 age: u32,
40
41 pub fn loadHeader(self: *Coff) !void {
42 const pe_pointer_offset = 0x3C;
43
44 var file_stream = io.FileInStream.init(self.in_file);
45 const in = &file_stream.stream;
46
47 var magic: [2]u8 = undefined;
48 try in.readNoEof(magic[0..]);
49 if (!mem.eql(u8, magic, "MZ"))
50 return error.InvalidPEMagic;
51
52 // Seek to PE File Header (coff header)
53 try self.in_file.seekTo(pe_pointer_offset);
54 const pe_magic_offset = try in.readIntLe(u32);
55 try self.in_file.seekTo(pe_magic_offset);
56
57 var pe_header_magic: [4]u8 = undefined;
58 try in.readNoEof(pe_header_magic[0..]);
59 if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0}))
60 return error.InvalidPEHeader;
61
62 self.coff_header = CoffHeader {
63 .machine = try in.readIntLe(u16),
64 .number_of_sections = try in.readIntLe(u16),
65 .timedate_stamp = try in.readIntLe(u32),
66 .pointer_to_symbol_table = try in.readIntLe(u32),
67 .number_of_symbols = try in.readIntLe(u32),
68 .size_of_optional_header = try in.readIntLe(u16),
69 .characteristics = try in.readIntLe(u16),
70 };
71
72 switch (self.coff_header.machine) {
73 IMAGE_FILE_MACHINE_I386,
74 IMAGE_FILE_MACHINE_AMD64,
75 IMAGE_FILE_MACHINE_IA64
76 => {},
77 else => return error.InvalidMachine,
78 }
79
80 try self.loadOptionalHeader(&file_stream);
81 }
82
83 fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void {
84 const in = &file_stream.stream;
85 self.pe_header.magic = try in.readIntLe(u16);
86 // For now we're only interested in finding the reference to the .pdb,
87 // so we'll skip most of this header, which size is different in 32
88 // 64 bits by the way.
89 var skip_size: u16 = undefined;
90 if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
91 skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
92 }
93 else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
94 skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
95 }
96 else
97 return error.InvalidPEMagic;
98
99 try self.in_file.seekForward(skip_size);
100
101 const number_of_rva_and_sizes = try in.readIntLe(u32);
102 if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
103 return error.InvalidPEHeader;
104
105 for (self.pe_header.data_directory) |*data_dir| {
106 data_dir.* = OptionalHeader.DataDirectory {
107 .virtual_address = try in.readIntLe(u32),
108 .size = try in.readIntLe(u32),
109 };
110 }
111 }
112
113 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
114 try self.loadSections();
115 const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header;
116
117 // The linker puts a chunk that contains the .pdb path right after the
118 // debug_directory.
119 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
120 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
121 try self.in_file.seekTo(file_offset + debug_dir.size);
122
123 var file_stream = io.FileInStream.init(self.in_file);
124 const in = &file_stream.stream;
125
126 var cv_signature: [4]u8 = undefined; // CodeView signature
127 try in.readNoEof(cv_signature[0..]);
128 // 'RSDS' indicates PDB70 format, used by lld.
129 if (!mem.eql(u8, cv_signature, "RSDS"))
130 return error.InvalidPEMagic;
131 try in.readNoEof(self.guid[0..]);
132 self.age = try in.readIntLe(u32);
133
134 // Finally read the null-terminated string.
135 var byte = try in.readByte();
136 var i: usize = 0;
137 while (byte != 0 and i < buffer.len) : (i += 1) {
138 buffer[i] = byte;
139 byte = try in.readByte();
140 }
141
142 if (byte != 0 and i == buffer.len)
143 return error.NameTooLong;
144
145 return i;
146 }
147
148 pub fn loadSections(self: *Coff) !void {
149 if (self.sections.len != 0)
150 return;
151
152 self.sections = ArrayList(Section).init(self.allocator);
153
154 var file_stream = io.FileInStream.init(self.in_file);
155 const in = &file_stream.stream;
156
157 var name: [8]u8 = undefined;
158
159 var i: u16 = 0;
160 while (i < self.coff_header.number_of_sections) : (i += 1) {
161 try in.readNoEof(name[0..]);
162 try self.sections.append(Section {
163 .header = SectionHeader {
164 .name = name,
165 .misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) },
166 .virtual_address = try in.readIntLe(u32),
167 .size_of_raw_data = try in.readIntLe(u32),
168 .pointer_to_raw_data = try in.readIntLe(u32),
169 .pointer_to_relocations = try in.readIntLe(u32),
170 .pointer_to_line_numbers = try in.readIntLe(u32),
171 .number_of_relocations = try in.readIntLe(u16),
172 .number_of_line_numbers = try in.readIntLe(u16),
173 .characteristics = try in.readIntLe(u32),
174 },
175 });
176 }
177 }
178
179 pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
180 for (self.sections.toSlice()) |*sec| {
181 if (mem.eql(u8, sec.header.name[0..name.len], name)) {
182 return sec;
183 }
184 }
185 return null;
186 }
187
188};
189
190const CoffHeader = struct {
191 machine: u16,
192 number_of_sections: u16,
193 timedate_stamp: u32,
194 pointer_to_symbol_table: u32,
195 number_of_symbols: u32,
196 size_of_optional_header: u16,
197 characteristics: u16
198};
199
200const OptionalHeader = struct {
201 const DataDirectory = struct {
202 virtual_address: u32,
203 size: u32
204 };
205
206 magic: u16,
207 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
208};
209
210pub const Section = struct {
211 header: SectionHeader,
212};
213
214const SectionHeader = struct {
215 const Misc = union {
216 physical_address: u32,
217 virtual_size: u32
218 };
219
220 name: [8]u8,
221 misc: Misc,
222 virtual_address: u32,
223 size_of_raw_data: u32,
224 pointer_to_raw_data: u32,
225 pointer_to_relocations: u32,
226 pointer_to_line_numbers: u32,
227 number_of_relocations: u16,
228 number_of_line_numbers: u16,
229 characteristics: u32,
230};
std/crypto/blake2.zig+8-8
......@@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256);
3434fn Blake2s(comptime out_len: usize) type {
3535 return struct {
3636 const Self = this;
37 const block_size = 64;
38 const digest_size = out_len / 8;
37 const block_length = 64;
38 const digest_length = out_len / 8;
3939
4040 const iv = [8]u32{
4141 0x6A09E667,
......@@ -250,8 +250,8 @@ test "blake2s256 streaming" {
250250}
251251
252252test "blake2s256 aligned final" {
253 var block = []u8{0} ** Blake2s256.block_size;
254 var out: [Blake2s256.digest_size]u8 = undefined;
253 var block = []u8{0} ** Blake2s256.block_length;
254 var out: [Blake2s256.digest_length]u8 = undefined;
255255
256256 var h = Blake2s256.init();
257257 h.update(block);
......@@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512);
267267fn Blake2b(comptime out_len: usize) type {
268268 return struct {
269269 const Self = this;
270 const block_size = 128;
271 const digest_size = out_len / 8;
270 const block_length = 128;
271 const digest_length = out_len / 8;
272272
273273 const iv = [8]u64{
274274 0x6a09e667f3bcc908,
......@@ -483,8 +483,8 @@ test "blake2b512 streaming" {
483483}
484484
485485test "blake2b512 aligned final" {
486 var block = []u8{0} ** Blake2b512.block_size;
487 var out: [Blake2b512.digest_size]u8 = undefined;
486 var block = []u8{0} ** Blake2b512.block_length;
487 var out: [Blake2b512.digest_length]u8 = undefined;
488488
489489 var h = Blake2b512.init();
490490 h.update(block);
std/crypto/chacha20.zig created+432
......@@ -0,0 +1,432 @@
1// Based on public domain Supercop by Daniel J. Bernstein
2
3const std = @import("../index.zig");
4const mem = std.mem;
5const endian = std.endian;
6const assert = std.debug.assert;
7const builtin = @import("builtin");
8
9const QuarterRound = struct {
10 a: usize,
11 b: usize,
12 c: usize,
13 d: usize,
14};
15
16fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
17 return QuarterRound{
18 .a = a,
19 .b = b,
20 .c = c,
21 .d = d,
22 };
23}
24
25// The chacha family of ciphers are based on the salsa family.
26fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
27 assert(out.len >= 64);
28
29 var x: [16]u32 = undefined;
30
31 for (x) |_, i|
32 x[i] = input[i];
33
34 const rounds = comptime []QuarterRound{
35 Rp(0, 4, 8, 12),
36 Rp(1, 5, 9, 13),
37 Rp(2, 6, 10, 14),
38 Rp(3, 7, 11, 15),
39 Rp(0, 5, 10, 15),
40 Rp(1, 6, 11, 12),
41 Rp(2, 7, 8, 13),
42 Rp(3, 4, 9, 14),
43 };
44
45 comptime var j: usize = 0;
46 inline while (j < 20) : (j += 2) {
47 // two-round cycles
48 inline for (rounds) |r| {
49 x[r.a] +%= x[r.b];
50 x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16));
51 x[r.c] +%= x[r.d];
52 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12));
53 x[r.a] +%= x[r.b];
54 x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8));
55 x[r.c] +%= x[r.d];
56 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7));
57 }
58 }
59
60 for (x) |_, i| {
61 mem.writeInt(out[4 * i .. 4 * i + 4], x[i] +% input[i], builtin.Endian.Little);
62 }
63}
64
65fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
66 var ctx: [16]u32 = undefined;
67 var remaining: usize = if (in.len > out.len) in.len else out.len;
68 var cursor: usize = 0;
69
70 const c = "expand 32-byte k";
71 const constant_le = []u32{
72 mem.readIntLE(u32, c[0..4]),
73 mem.readIntLE(u32, c[4..8]),
74 mem.readIntLE(u32, c[8..12]),
75 mem.readIntLE(u32, c[12..16]),
76 };
77
78 mem.copy(u32, ctx[0..], constant_le[0..4]);
79 mem.copy(u32, ctx[4..12], key[0..8]);
80 mem.copy(u32, ctx[12..16], counter[0..4]);
81
82 while (true) {
83 var buf: [64]u8 = undefined;
84 salsa20_wordtobyte(buf[0..], ctx);
85
86 if (remaining < 64) {
87 var i: usize = 0;
88 while (i < remaining) : (i += 1)
89 out[cursor + i] = in[cursor + i] ^ buf[i];
90 return;
91 }
92
93 var i: usize = 0;
94 while (i < 64) : (i += 1)
95 out[cursor + i] = in[cursor + i] ^ buf[i];
96
97 cursor += 64;
98 remaining -= 64;
99
100 ctx[12] += 1;
101 }
102}
103
104/// ChaCha20 avoids the possibility of timing attacks, as there are no branches
105/// on secret key data.
106///
107/// in and out should be the same length.
108/// counter should generally be 0 or 1
109///
110/// ChaCha20 is self-reversing. To decrypt just run the cipher with the same
111/// counter, nonce, and key.
112pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {
113 assert(in.len >= out.len);
114 assert((in.len >> 6) + counter <= @maxValue(u32));
115
116 var k: [8]u32 = undefined;
117 var c: [4]u32 = undefined;
118
119 k[0] = mem.readIntLE(u32, key[0..4]);
120 k[1] = mem.readIntLE(u32, key[4..8]);
121 k[2] = mem.readIntLE(u32, key[8..12]);
122 k[3] = mem.readIntLE(u32, key[12..16]);
123 k[4] = mem.readIntLE(u32, key[16..20]);
124 k[5] = mem.readIntLE(u32, key[20..24]);
125 k[6] = mem.readIntLE(u32, key[24..28]);
126 k[7] = mem.readIntLE(u32, key[28..32]);
127
128 c[0] = counter;
129 c[1] = mem.readIntLE(u32, nonce[0..4]);
130 c[2] = mem.readIntLE(u32, nonce[4..8]);
131 c[3] = mem.readIntLE(u32, nonce[8..12]);
132 chaCha20_internal(out, in, k, c);
133}
134
135/// This is the original ChaCha20 before RFC 7539, which recommends using the
136/// orgininal version on applications such as disk or file encryption that might
137/// exceed the 256 GiB limit of the 96-bit nonce version.
138pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {
139 assert(in.len >= out.len);
140 assert(counter +% (in.len >> 6) >= counter);
141
142 var cursor: u64 = 0;
143 var k: [8]u32 = undefined;
144 var c: [4]u32 = undefined;
145
146 k[0] = mem.readIntLE(u32, key[0..4]);
147 k[1] = mem.readIntLE(u32, key[4..8]);
148 k[2] = mem.readIntLE(u32, key[8..12]);
149 k[3] = mem.readIntLE(u32, key[12..16]);
150 k[4] = mem.readIntLE(u32, key[16..20]);
151 k[5] = mem.readIntLE(u32, key[20..24]);
152 k[6] = mem.readIntLE(u32, key[24..28]);
153 k[7] = mem.readIntLE(u32, key[28..32]);
154
155 c[0] = @truncate(u32, counter);
156 c[1] = @truncate(u32, counter >> 32);
157 c[2] = mem.readIntLE(u32, nonce[0..4]);
158 c[3] = mem.readIntLE(u32, nonce[4..8]);
159
160 const block_size = (1 << 6);
161 const big_block = (block_size << 32);
162
163 // first partial big block
164 if (((@intCast(u64, @maxValue(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
165 chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c);
166 cursor = big_block - cursor;
167 c[1] += 1;
168 if (comptime @sizeOf(usize) > 4) {
169 // A big block is giant: 256 GiB, but we can avoid this limitation
170 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
171 var i: u32 = 0;
172 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
173 chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
174 c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this.
175 cursor += big_block;
176 }
177 }
178 }
179
180 chaCha20_internal(out[cursor..], in[cursor..], k, c);
181}
182
183// https://tools.ietf.org/html/rfc7539#section-2.4.2
184test "crypto.chacha20 test vector sunscreen" {
185 const expected_result = []u8{
186 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80,
187 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
188 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2,
189 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
190 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab,
191 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
192 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab,
193 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
194 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61,
195 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
196 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06,
197 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
198 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6,
199 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
200 0x87, 0x4d,
201 };
202 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
203 var result: [114]u8 = undefined;
204 const key = []u8{
205 0, 1, 2, 3, 4, 5, 6, 7,
206 8, 9, 10, 11, 12, 13, 14, 15,
207 16, 17, 18, 19, 20, 21, 22, 23,
208 24, 25, 26, 27, 28, 29, 30, 31,
209 };
210 const nonce = []u8{
211 0, 0, 0, 0,
212 0, 0, 0, 0x4a,
213 0, 0, 0, 0,
214 };
215
216 chaCha20IETF(result[0..], input[0..], 1, key, nonce);
217 assert(mem.eql(u8, expected_result, result));
218
219 // Chacha20 is self-reversing.
220 var plaintext: [114]u8 = undefined;
221 chaCha20IETF(plaintext[0..], result[0..], 1, key, nonce);
222 assert(mem.compare(u8, input, plaintext) == mem.Compare.Equal);
223}
224
225// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
226test "crypto.chacha20 test vector 1" {
227 const expected_result = []u8{
228 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
229 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
230 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
231 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
232 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
233 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
234 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
235 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
236 };
237 const input = []u8{
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 };
247 var result: [64]u8 = undefined;
248 const key = []u8{
249 0, 0, 0, 0, 0, 0, 0, 0,
250 0, 0, 0, 0, 0, 0, 0, 0,
251 0, 0, 0, 0, 0, 0, 0, 0,
252 0, 0, 0, 0, 0, 0, 0, 0,
253 };
254 const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
255
256 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
257 assert(mem.eql(u8, expected_result, result));
258}
259
260test "crypto.chacha20 test vector 2" {
261 const expected_result = []u8{
262 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96,
263 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96,
264 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60,
265 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41,
266 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2,
267 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c,
268 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
269 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
270 };
271 const input = []u8{
272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
278 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280 };
281 var result: [64]u8 = undefined;
282 const key = []u8{
283 0, 0, 0, 0, 0, 0, 0, 0,
284 0, 0, 0, 0, 0, 0, 0, 0,
285 0, 0, 0, 0, 0, 0, 0, 0,
286 0, 0, 0, 0, 0, 0, 0, 1,
287 };
288 const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
289
290 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
291 assert(mem.eql(u8, expected_result, result));
292}
293
294test "crypto.chacha20 test vector 3" {
295 const expected_result = []u8{
296 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5,
297 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a,
298 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13,
299 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31,
300 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45,
301 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b,
302 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
303 0x44, 0x5f, 0x41, 0xe3,
304 };
305 const input = []u8{
306 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313 0x00, 0x00, 0x00, 0x00,
314 };
315 var result: [60]u8 = undefined;
316 const key = []u8{
317 0, 0, 0, 0, 0, 0, 0, 0,
318 0, 0, 0, 0, 0, 0, 0, 0,
319 0, 0, 0, 0, 0, 0, 0, 0,
320 0, 0, 0, 0, 0, 0, 0, 0,
321 };
322 const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 1 };
323
324 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
325 assert(mem.eql(u8, expected_result, result));
326}
327
328test "crypto.chacha20 test vector 4" {
329 const expected_result = []u8{
330 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
331 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
332 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
333 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
334 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
335 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
336 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
337 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
338 };
339 const input = []u8{
340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 };
349 var result: [64]u8 = undefined;
350 const key = []u8{
351 0, 0, 0, 0, 0, 0, 0, 0,
352 0, 0, 0, 0, 0, 0, 0, 0,
353 0, 0, 0, 0, 0, 0, 0, 0,
354 0, 0, 0, 0, 0, 0, 0, 0,
355 };
356 const nonce = []u8{ 1, 0, 0, 0, 0, 0, 0, 0 };
357
358 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
359 assert(mem.eql(u8, expected_result, result));
360}
361
362test "crypto.chacha20 test vector 5" {
363 const expected_result = []u8{
364 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69,
365 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75,
366 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93,
367 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1,
368 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41,
369 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69,
370 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1,
371 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a,
372
373 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94,
374 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66,
375 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58,
376 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd,
377 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56,
378 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e,
379 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e,
380 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7,
381
382 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15,
383 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3,
384 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a,
385 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25,
386 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5,
387 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69,
388 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4,
389 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7,
390
391 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79,
392 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a,
393 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a,
394 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2,
395 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a,
396 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09,
397 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
398 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
399 };
400 const input = []u8{
401 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
402 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
403 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
404 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
405 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
406 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
407 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
408 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
409
410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
411 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
412 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
413 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
414 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
415 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
416 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
417 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
418 };
419 var result: [256]u8 = undefined;
420 const key = []u8{
421 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
422 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
423 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
424 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
425 };
426 const nonce = []u8{
427 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
428 };
429
430 chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
431 assert(mem.eql(u8, expected_result, result));
432}
std/crypto/hmac.zig+54-37
......@@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5);
77pub const HmacSha1 = Hmac(crypto.Sha1);
88pub const HmacSha256 = Hmac(crypto.Sha256);
99
10pub fn Hmac(comptime H: type) type {
10pub fn Hmac(comptime Hash: type) type {
1111 return struct {
12 const digest_size = H.digest_size;
12 const Self = this;
13 pub const mac_length = Hash.digest_length;
14 pub const minimum_key_length = 0;
1315
14 pub fn hash(output: []u8, key: []const u8, message: []const u8) void {
15 debug.assert(output.len >= H.digest_size);
16 debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
17 var scratch: [H.block_size]u8 = undefined;
16 o_key_pad: [Hash.block_length]u8,
17 i_key_pad: [Hash.block_length]u8,
18 scratch: [Hash.block_length]u8,
19 hash: Hash,
20
21 // HMAC(k, m) = H(o_key_pad | H(i_key_pad | msg)) where | is concatenation
22 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
23 var ctx = Self.init(key);
24 ctx.update(msg);
25 ctx.final(out[0..]);
26 }
27
28 pub fn init(key: []const u8) Self {
29 var ctx: Self = undefined;
1830
1931 // Normalize key length to block size of hash
20 if (key.len > H.block_size) {
21 H.hash(key, scratch[0..H.digest_size]);
22 mem.set(u8, scratch[H.digest_size..H.block_size], 0);
23 } else if (key.len < H.block_size) {
24 mem.copy(u8, scratch[0..key.len], key);
25 mem.set(u8, scratch[key.len..H.block_size], 0);
32 if (key.len > Hash.block_length) {
33 Hash.hash(key, ctx.scratch[0..mac_length]);
34 mem.set(u8, ctx.scratch[mac_length..Hash.block_length], 0);
35 } else if (key.len < Hash.block_length) {
36 mem.copy(u8, ctx.scratch[0..key.len], key);
37 mem.set(u8, ctx.scratch[key.len..Hash.block_length], 0);
2638 } else {
27 mem.copy(u8, scratch[0..], key);
39 mem.copy(u8, ctx.scratch[0..], key);
2840 }
2941
30 var o_key_pad: [H.block_size]u8 = undefined;
31 for (o_key_pad) |*b, i| {
32 b.* = scratch[i] ^ 0x5c;
42 for (ctx.o_key_pad) |*b, i| {
43 b.* = ctx.scratch[i] ^ 0x5c;
3344 }
3445
35 var i_key_pad: [H.block_size]u8 = undefined;
36 for (i_key_pad) |*b, i| {
37 b.* = scratch[i] ^ 0x36;
46 for (ctx.i_key_pad) |*b, i| {
47 b.* = ctx.scratch[i] ^ 0x36;
3848 }
3949
40 // HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation
41 var hmac = H.init();
42 hmac.update(i_key_pad[0..]);
43 hmac.update(message);
44 hmac.final(scratch[0..H.digest_size]);
50 ctx.hash = Hash.init();
51 ctx.hash.update(ctx.i_key_pad[0..]);
52 return ctx;
53 }
54
55 pub fn update(ctx: *Self, msg: []const u8) void {
56 ctx.hash.update(msg);
57 }
58
59 pub fn final(ctx: *Self, out: []u8) void {
60 debug.assert(Hash.block_length >= out.len and out.len >= mac_length);
4561
46 hmac.reset();
47 hmac.update(o_key_pad[0..]);
48 hmac.update(scratch[0..H.digest_size]);
49 hmac.final(output[0..H.digest_size]);
62 ctx.hash.final(ctx.scratch[0..mac_length]);
63 ctx.hash.reset();
64 ctx.hash.update(ctx.o_key_pad[0..]);
65 ctx.hash.update(ctx.scratch[0..mac_length]);
66 ctx.hash.final(out[0..mac_length]);
5067 }
5168 };
5269}
......@@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type {
5471const htest = @import("test.zig");
5572
5673test "hmac md5" {
57 var out: [crypto.Md5.digest_size]u8 = undefined;
58 HmacMd5.hash(out[0..], "", "");
74 var out: [HmacMd5.mac_length]u8 = undefined;
75 HmacMd5.create(out[0..], "", "");
5976 htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
6077
61 HmacMd5.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
78 HmacMd5.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
6279 htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
6380}
6481
6582test "hmac sha1" {
66 var out: [crypto.Sha1.digest_size]u8 = undefined;
67 HmacSha1.hash(out[0..], "", "");
83 var out: [HmacSha1.mac_length]u8 = undefined;
84 HmacSha1.create(out[0..], "", "");
6885 htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
6986
70 HmacSha1.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
87 HmacSha1.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
7188 htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
7289}
7390
7491test "hmac sha256" {
75 var out: [crypto.Sha256.digest_size]u8 = undefined;
76 HmacSha256.hash(out[0..], "", "");
92 var out: [HmacSha256.mac_length]u8 = undefined;
93 HmacSha256.create(out[0..], "", "");
7794 htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
7895
79 HmacSha256.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
96 HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
8097 htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
8198}
std/crypto/index.zig+14-4
......@@ -21,14 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512;
2121
2222const hmac = @import("hmac.zig");
2323pub const HmacMd5 = hmac.HmacMd5;
24pub const HmacSha1 = hmac.Sha1;
25pub const HmacSha256 = hmac.Sha256;
24pub const HmacSha1 = hmac.HmacSha1;
25pub const HmacSha256 = hmac.HmacSha256;
26
27const import_chaCha20 = @import("chacha20.zig");
28pub const chaCha20IETF = import_chaCha20.chaCha20IETF;
29pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
30
31pub const Poly1305 = @import("poly1305.zig").Poly1305;
32pub const X25519 = @import("x25519.zig").X25519;
2633
2734test "crypto" {
35 _ = @import("blake2.zig");
36 _ = @import("chacha20.zig");
37 _ = @import("hmac.zig");
2838 _ = @import("md5.zig");
39 _ = @import("poly1305.zig");
2940 _ = @import("sha1.zig");
3041 _ = @import("sha2.zig");
3142 _ = @import("sha3.zig");
32 _ = @import("blake2.zig");
33 _ = @import("hmac.zig");
43 _ = @import("x25519.zig");
3444}
std/crypto/md5.zig+4-4
......@@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar
2929
3030pub const Md5 = struct {
3131 const Self = this;
32 const block_size = 64;
33 const digest_size = 16;
32 const block_length = 64;
33 const digest_length = 16;
3434
3535 s: [4]u32,
3636 // Streaming Cache
......@@ -271,8 +271,8 @@ test "md5 streaming" {
271271}
272272
273273test "md5 aligned final" {
274 var block = []u8{0} ** Md5.block_size;
275 var out: [Md5.digest_size]u8 = undefined;
274 var block = []u8{0} ** Md5.block_length;
275 var out: [Md5.digest_length]u8 = undefined;
276276
277277 var h = Md5.init();
278278 h.update(block);
std/crypto/poly1305.zig created+233
......@@ -0,0 +1,233 @@
1// Translated from monocypher which is licensed under CC-0/BSD-3.
2//
3// https://monocypher.org/
4
5const std = @import("../index.zig");
6const builtin = @import("builtin");
7
8const Endian = builtin.Endian;
9const readInt = std.mem.readInt;
10const writeInt = std.mem.writeInt;
11
12pub const Poly1305 = struct {
13 const Self = this;
14
15 pub const mac_length = 16;
16 pub const minimum_key_length = 32;
17
18 // constant multiplier (from the secret key)
19 r: [4]u32,
20 // accumulated hash
21 h: [5]u32,
22 // chunk of the message
23 c: [5]u32,
24 // random number added at the end (from the secret key)
25 pad: [4]u32,
26 // How many bytes are there in the chunk.
27 c_idx: usize,
28
29 fn secureZero(self: *Self) void {
30 std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Poly1305)]);
31 }
32
33 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
34 std.debug.assert(out.len >= mac_length);
35 std.debug.assert(key.len >= minimum_key_length);
36
37 var ctx = Poly1305.init(key);
38 ctx.update(msg);
39 ctx.final(out);
40 }
41
42 // Initialize the MAC context.
43 // - key.len is sufficient size.
44 pub fn init(key: []const u8) Self {
45 var ctx: Poly1305 = undefined;
46
47 // Initial hash is zero
48 {
49 var i: usize = 0;
50 while (i < 5) : (i += 1) {
51 ctx.h[i] = 0;
52 }
53 }
54 // add 2^130 to every input block
55 ctx.c[4] = 1;
56 polyClearC(&ctx);
57
58 // load r and pad (r has some of its bits cleared)
59 {
60 var i: usize = 0;
61 while (i < 1) : (i += 1) {
62 ctx.r[0] = readInt(key[0..4], u32, Endian.Little) & 0x0fffffff;
63 }
64 }
65 {
66 var i: usize = 1;
67 while (i < 4) : (i += 1) {
68 ctx.r[i] = readInt(key[i * 4 .. i * 4 + 4], u32, Endian.Little) & 0x0ffffffc;
69 }
70 }
71 {
72 var i: usize = 0;
73 while (i < 4) : (i += 1) {
74 ctx.pad[i] = readInt(key[i * 4 + 16 .. i * 4 + 16 + 4], u32, Endian.Little);
75 }
76 }
77
78 return ctx;
79 }
80
81 // h = (h + c) * r
82 // preconditions:
83 // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
84 // ctx->c <= 1_ffffffff_ffffffff_ffffffff_ffffffff
85 // ctx->r <= 0ffffffc_0ffffffc_0ffffffc_0fffffff
86 // Postcondition:
87 // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
88 fn polyBlock(ctx: *Self) void {
89 // s = h + c, without carry propagation
90 const s0 = u64(ctx.h[0]) + ctx.c[0]; // s0 <= 1_fffffffe
91 const s1 = u64(ctx.h[1]) + ctx.c[1]; // s1 <= 1_fffffffe
92 const s2 = u64(ctx.h[2]) + ctx.c[2]; // s2 <= 1_fffffffe
93 const s3 = u64(ctx.h[3]) + ctx.c[3]; // s3 <= 1_fffffffe
94 const s4 = u64(ctx.h[4]) + ctx.c[4]; // s4 <= 5
95
96 // Local all the things!
97 const r0 = ctx.r[0]; // r0 <= 0fffffff
98 const r1 = ctx.r[1]; // r1 <= 0ffffffc
99 const r2 = ctx.r[2]; // r2 <= 0ffffffc
100 const r3 = ctx.r[3]; // r3 <= 0ffffffc
101 const rr0 = (r0 >> 2) * 5; // rr0 <= 13fffffb // lose 2 bits...
102 const rr1 = (r1 >> 2) + r1; // rr1 <= 13fffffb // rr1 == (r1 >> 2) * 5
103 const rr2 = (r2 >> 2) + r2; // rr2 <= 13fffffb // rr1 == (r2 >> 2) * 5
104 const rr3 = (r3 >> 2) + r3; // rr3 <= 13fffffb // rr1 == (r3 >> 2) * 5
105
106 // (h + c) * r, without carry propagation
107 const x0 = s0 * r0 + s1 * rr3 + s2 * rr2 + s3 * rr1 + s4 * rr0; //<=97ffffe007fffff8
108 const x1 = s0 * r1 + s1 * r0 + s2 * rr3 + s3 * rr2 + s4 * rr1; //<=8fffffe20ffffff6
109 const x2 = s0 * r2 + s1 * r1 + s2 * r0 + s3 * rr3 + s4 * rr2; //<=87ffffe417fffff4
110 const x3 = s0 * r3 + s1 * r2 + s2 * r1 + s3 * r0 + s4 * rr3; //<=7fffffe61ffffff2
111 const x4 = s4 * (r0 & 3); // ...recover 2 bits //<= f
112
113 // partial reduction modulo 2^130 - 5
114 const _u5 = @truncate(u32, x4 + (x3 >> 32)); // u5 <= 7ffffff5
115 const _u0 = (_u5 >> 2) * 5 + (x0 & 0xffffffff);
116 const _u1 = (_u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32);
117 const _u2 = (_u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32);
118 const _u3 = (_u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32);
119 const _u4 = (_u3 >> 32) + (_u5 & 3);
120
121 // Update the hash
122 ctx.h[0] = @truncate(u32, _u0); // u0 <= 1_9ffffff0
123 ctx.h[1] = @truncate(u32, _u1); // u1 <= 1_97ffffe0
124 ctx.h[2] = @truncate(u32, _u2); // u2 <= 1_8fffffe2
125 ctx.h[3] = @truncate(u32, _u3); // u3 <= 1_87ffffe4
126 ctx.h[4] = @truncate(u32, _u4); // u4 <= 4
127 }
128
129 // (re-)initializes the input counter and input buffer
130 fn polyClearC(ctx: *Self) void {
131 ctx.c[0] = 0;
132 ctx.c[1] = 0;
133 ctx.c[2] = 0;
134 ctx.c[3] = 0;
135 ctx.c_idx = 0;
136 }
137
138 fn polyTakeInput(ctx: *Self, input: u8) void {
139 const word = ctx.c_idx >> 2;
140 const byte = ctx.c_idx & 3;
141 ctx.c[word] |= std.math.shl(u32, input, byte * 8);
142 ctx.c_idx += 1;
143 }
144
145 fn polyUpdate(ctx: *Self, msg: []const u8) void {
146 for (msg) |b| {
147 polyTakeInput(ctx, b);
148 if (ctx.c_idx == 16) {
149 polyBlock(ctx);
150 polyClearC(ctx);
151 }
152 }
153 }
154
155 fn alignTo(x: usize, block_size: usize) usize {
156 return ((~x) +% 1) & (block_size - 1);
157 }
158
159 // Feed data into the MAC context.
160 pub fn update(ctx: *Self, msg: []const u8) void {
161 // Align ourselves with block boundaries
162 const alignm = std.math.min(alignTo(ctx.c_idx, 16), msg.len);
163 polyUpdate(ctx, msg[0..alignm]);
164
165 var nmsg = msg[alignm..];
166
167 // Process the msg block by block
168 const nb_blocks = nmsg.len >> 4;
169 var i: usize = 0;
170 while (i < nb_blocks) : (i += 1) {
171 ctx.c[0] = readInt(nmsg[0..4], u32, Endian.Little);
172 ctx.c[1] = readInt(nmsg[4..8], u32, Endian.Little);
173 ctx.c[2] = readInt(nmsg[8..12], u32, Endian.Little);
174 ctx.c[3] = readInt(nmsg[12..16], u32, Endian.Little);
175 polyBlock(ctx);
176 nmsg = nmsg[16..];
177 }
178 if (nb_blocks > 0) {
179 polyClearC(ctx);
180 }
181
182 // remaining bytes
183 polyUpdate(ctx, nmsg[0..]);
184 }
185
186 // Finalize the MAC and output into buffer provided by caller.
187 pub fn final(ctx: *Self, out: []u8) void {
188 // Process the last block (if any)
189 if (ctx.c_idx != 0) {
190 // move the final 1 according to remaining input length
191 // (We may add less than 2^130 to the last input block)
192 ctx.c[4] = 0;
193 polyTakeInput(ctx, 1);
194 // one last hash update
195 polyBlock(ctx);
196 }
197
198 // check if we should subtract 2^130-5 by performing the
199 // corresponding carry propagation.
200 const _u0 = u64(5) + ctx.h[0]; // <= 1_00000004
201 const _u1 = (_u0 >> 32) + ctx.h[1]; // <= 1_00000000
202 const _u2 = (_u1 >> 32) + ctx.h[2]; // <= 1_00000000
203 const _u3 = (_u2 >> 32) + ctx.h[3]; // <= 1_00000000
204 const _u4 = (_u3 >> 32) + ctx.h[4]; // <= 5
205 // u4 indicates how many times we should subtract 2^130-5 (0 or 1)
206
207 // h + pad, minus 2^130-5 if u4 exceeds 3
208 const uu0 = (_u4 >> 2) * 5 + ctx.h[0] + ctx.pad[0]; // <= 2_00000003
209 const uu1 = (uu0 >> 32) + ctx.h[1] + ctx.pad[1]; // <= 2_00000000
210 const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000
211 const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000
212
213 writeInt(out[0..], @truncate(u32, uu0), Endian.Little);
214 writeInt(out[4..], @truncate(u32, uu1), Endian.Little);
215 writeInt(out[8..], @truncate(u32, uu2), Endian.Little);
216 writeInt(out[12..], @truncate(u32, uu3), Endian.Little);
217
218 ctx.secureZero();
219 }
220};
221
222test "poly1305 rfc7439 vector1" {
223 const expected_mac = "\xa8\x06\x1d\xc1\x30\x51\x36\xc6\xc2\x2b\x8b\xaf\x0c\x01\x27\xa9";
224
225 const msg = "Cryptographic Forum Research Group";
226 const key = "\x85\xd6\xbe\x78\x57\x55\x6d\x33\x7f\x44\x52\xfe\x42\xd5\x06\xa8" ++
227 "\x01\x03\x80\x8a\xfb\x0d\xb2\xfd\x4a\xbf\xf6\xaf\x41\x49\xf5\x1b";
228
229 var mac: [16]u8 = undefined;
230 Poly1305.create(mac[0..], msg, key);
231
232 std.debug.assert(std.mem.eql(u8, mac, expected_mac));
233}
std/crypto/sha1.zig+4-4
......@@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
2626
2727pub const Sha1 = struct {
2828 const Self = this;
29 const block_size = 64;
30 const digest_size = 20;
29 const block_length = 64;
30 const digest_length = 20;
3131
3232 s: [5]u32,
3333 // Streaming Cache
......@@ -292,8 +292,8 @@ test "sha1 streaming" {
292292}
293293
294294test "sha1 aligned final" {
295 var block = []u8{0} ** Sha1.block_size;
296 var out: [Sha1.digest_size]u8 = undefined;
295 var block = []u8{0} ** Sha1.block_length;
296 var out: [Sha1.digest_length]u8 = undefined;
297297
298298 var h = Sha1.init();
299299 h.update(block);
std/crypto/sha2.zig+8-8
......@@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params);
7878fn Sha2_32(comptime params: Sha2Params32) type {
7979 return struct {
8080 const Self = this;
81 const block_size = 64;
82 const digest_size = params.out_len / 8;
81 const block_length = 64;
82 const digest_length = params.out_len / 8;
8383
8484 s: [8]u32,
8585 // Streaming Cache
......@@ -338,8 +338,8 @@ test "sha256 streaming" {
338338}
339339
340340test "sha256 aligned final" {
341 var block = []u8{0} ** Sha256.block_size;
342 var out: [Sha256.digest_size]u8 = undefined;
341 var block = []u8{0} ** Sha256.block_length;
342 var out: [Sha256.digest_length]u8 = undefined;
343343
344344 var h = Sha256.init();
345345 h.update(block);
......@@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params);
419419fn Sha2_64(comptime params: Sha2Params64) type {
420420 return struct {
421421 const Self = this;
422 const block_size = 128;
423 const digest_size = params.out_len / 8;
422 const block_length = 128;
423 const digest_length = params.out_len / 8;
424424
425425 s: [8]u64,
426426 // Streaming Cache
......@@ -715,8 +715,8 @@ test "sha512 streaming" {
715715}
716716
717717test "sha512 aligned final" {
718 var block = []u8{0} ** Sha512.block_size;
719 var out: [Sha512.digest_size]u8 = undefined;
718 var block = []u8{0} ** Sha512.block_length;
719 var out: [Sha512.digest_length]u8 = undefined;
720720
721721 var h = Sha512.init();
722722 h.update(block);
std/crypto/sha3.zig+15-88
......@@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06);
1313fn Keccak(comptime bits: usize, comptime delim: u8) type {
1414 return struct {
1515 const Self = this;
16 const block_size = 200;
17 const digest_size = bits / 8;
16 const block_length = 200;
17 const digest_length = bits / 8;
1818
1919 s: [200]u8,
2020 offset: usize,
......@@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
8787}
8888
8989const RC = []const u64{
90 0x0000000000000001,
91 0x0000000000008082,
92 0x800000000000808a,
93 0x8000000080008000,
94 0x000000000000808b,
95 0x0000000080000001,
96 0x8000000080008081,
97 0x8000000000008009,
98 0x000000000000008a,
99 0x0000000000000088,
100 0x0000000080008009,
101 0x000000008000000a,
102 0x000000008000808b,
103 0x800000000000008b,
104 0x8000000000008089,
105 0x8000000000008003,
106 0x8000000000008002,
107 0x8000000000000080,
108 0x000000000000800a,
109 0x800000008000000a,
110 0x8000000080008081,
111 0x8000000000008080,
112 0x0000000080000001,
113 0x8000000080008008,
90 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
91 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
92 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
93 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
94 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
95 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
11496};
11597
11698const ROTC = []const usize{
117 1,
118 3,
119 6,
120 10,
121 15,
122 21,
123 28,
124 36,
125 45,
126 55,
127 2,
128 14,
129 27,
130 41,
131 56,
132 8,
133 25,
134 43,
135 62,
136 18,
137 39,
138 61,
139 20,
140 44,
99 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
141100};
142101
143102const PIL = []const usize{
144 10,
145 7,
146 11,
147 17,
148 18,
149 3,
150 5,
151 16,
152 8,
153 21,
154 24,
155 4,
156 15,
157 23,
158 19,
159 13,
160 12,
161 2,
162 20,
163 14,
164 22,
165 9,
166 6,
167 1,
103 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
168104};
169105
170106const M5 = []const usize{
171 0,
172 1,
173 2,
174 3,
175 4,
176 0,
177 1,
178 2,
179 3,
180 4,
107 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
181108};
182109
183110fn keccak_f(comptime F: usize, d: []u8) void {
......@@ -297,8 +224,8 @@ test "sha3-256 streaming" {
297224}
298225
299226test "sha3-256 aligned final" {
300 var block = []u8{0} ** Sha3_256.block_size;
301 var out: [Sha3_256.digest_size]u8 = undefined;
227 var block = []u8{0} ** Sha3_256.block_length;
228 var out: [Sha3_256.digest_length]u8 = undefined;
302229
303230 var h = Sha3_256.init();
304231 h.update(block);
......@@ -368,8 +295,8 @@ test "sha3-512 streaming" {
368295}
369296
370297test "sha3-512 aligned final" {
371 var block = []u8{0} ** Sha3_512.block_size;
372 var out: [Sha3_512.digest_size]u8 = undefined;
298 var block = []u8{0} ** Sha3_512.block_length;
299 var out: [Sha3_512.digest_length]u8 = undefined;
373300
374301 var h = Sha3_512.init();
375302 h.update(block);
std/crypto/throughput_test.zig+176-21
......@@ -1,38 +1,193 @@
1// Modify the HashFunction variable to the one wanted to test.
2//
3// ```
4// zig build-exe --release-fast throughput_test.zig
5// ./throughput_test
6// ```
7
1const builtin = @import("builtin");
82const std = @import("std");
93const time = std.os.time;
104const Timer = time.Timer;
11const HashFunction = @import("md5.zig").Md5;
5const crypto = @import("index.zig");
126
13const MiB = 1024 * 1024;
14const BytesToHash = 1024 * MiB;
7const KiB = 1024;
8const MiB = 1024 * KiB;
159
16pub fn main() !void {
17 var stdout_file = try std.io.getStdOut();
18 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
19 const stdout = &stdout_out_stream.stream;
10var prng = std.rand.DefaultPrng.init(0);
2011
21 var block: [HashFunction.block_size]u8 = undefined;
22 std.mem.set(u8, block[0..], 0);
12const Crypto = struct {
13 ty: type,
14 name: []const u8,
15};
2316
24 var h = HashFunction.init();
25 var offset: usize = 0;
17const hashes = []Crypto{
18 Crypto{ .ty = crypto.Md5, .name = "md5" },
19 Crypto{ .ty = crypto.Sha1, .name = "sha1" },
20 Crypto{ .ty = crypto.Sha256, .name = "sha256" },
21 Crypto{ .ty = crypto.Sha512, .name = "sha512" },
22 Crypto{ .ty = crypto.Sha3_256, .name = "sha3-256" },
23 Crypto{ .ty = crypto.Sha3_512, .name = "sha3-512" },
24 Crypto{ .ty = crypto.Blake2s256, .name = "blake2s" },
25 Crypto{ .ty = crypto.Blake2b512, .name = "blake2b" },
26};
27
28pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 {
29 var h = Hash.init();
30
31 var block: [Hash.digest_length]u8 = undefined;
32 prng.random.bytes(block[0..]);
2633
34 var offset: usize = 0;
2735 var timer = try Timer.start();
2836 const start = timer.lap();
29 while (offset < BytesToHash) : (offset += block.len) {
37 while (offset < bytes) : (offset += block.len) {
3038 h.update(block[0..]);
3139 }
3240 const end = timer.read();
3341
3442 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
35 const throughput = @floatToInt(u64, BytesToHash / elapsed_s);
43 const throughput = @floatToInt(u64, bytes / elapsed_s);
44
45 return throughput;
46}
47
48const macs = []Crypto{
49 Crypto{ .ty = crypto.Poly1305, .name = "poly1305" },
50 Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" },
51 Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" },
52 Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },
53};
54
55pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
56 std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
57
58 var in: [1 * MiB]u8 = undefined;
59 prng.random.bytes(in[0..]);
60
61 var key: [32]u8 = undefined;
62 prng.random.bytes(key[0..]);
63
64 var offset: usize = 0;
65 var timer = try Timer.start();
66 const start = timer.lap();
67 while (offset < bytes) : (offset += in.len) {
68 Mac.create(key[0..], in[0..], key);
69 }
70 const end = timer.read();
71
72 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
73 const throughput = @floatToInt(u64, bytes / elapsed_s);
74
75 return throughput;
76}
77
78const exchanges = []Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
79
80pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 {
81 std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
82
83 var in: [DhKeyExchange.minimum_key_length]u8 = undefined;
84 prng.random.bytes(in[0..]);
85
86 var out: [DhKeyExchange.minimum_key_length]u8 = undefined;
87 prng.random.bytes(out[0..]);
88
89 var offset: usize = 0;
90 var timer = try Timer.start();
91 const start = timer.lap();
92 {
93 var i: usize = 0;
94 while (i < exchange_count) : (i += 1) {
95 _ = DhKeyExchange.create(out[0..], out, in);
96 }
97 }
98 const end = timer.read();
99
100 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
101 const throughput = @floatToInt(u64, exchange_count / elapsed_s);
102
103 return throughput;
104}
36105
37 try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
106fn usage() void {
107 std.debug.warn(
108 \\throughput_test [options]
109 \\
110 \\Options:
111 \\ --filter [test-name]
112 \\ --seed [int]
113 \\ --help
114 \\
115 );
116}
117
118fn mode(comptime x: comptime_int) comptime_int {
119 return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
120}
121
122// TODO(#1358): Replace with builtin formatted padding when available.
123fn printPad(stdout: var, s: []const u8) !void {
124 var i: usize = 0;
125 while (i < 12 - s.len) : (i += 1) {
126 try stdout.print(" ");
127 }
128 try stdout.print("{}", s);
129}
130
131pub fn main() !void {
132 var stdout_file = try std.io.getStdOut();
133 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
134 const stdout = &stdout_out_stream.stream;
135
136 var buffer: [1024]u8 = undefined;
137 var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
138 const args = try std.os.argsAlloc(&fixed.allocator);
139
140 var filter: ?[]u8 = "";
141
142 var i: usize = 1;
143 while (i < args.len) : (i += 1) {
144 if (std.mem.eql(u8, args[i], "--seed")) {
145 i += 1;
146 if (i == args.len) {
147 usage();
148 std.os.exit(1);
149 }
150
151 const seed = try std.fmt.parseUnsigned(u32, args[i], 10);
152 prng.seed(seed);
153 } else if (std.mem.eql(u8, args[i], "--filter")) {
154 i += 1;
155 if (i == args.len) {
156 usage();
157 std.os.exit(1);
158 }
159
160 filter = args[i];
161 } else if (std.mem.eql(u8, args[i], "--help")) {
162 usage();
163 return;
164 } else {
165 usage();
166 std.os.exit(1);
167 }
168 }
169
170 inline for (hashes) |H| {
171 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
172 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
173 try printPad(stdout, H.name);
174 try stdout.print(": {} MiB/s\n", throughput / (1 * MiB));
175 }
176 }
177
178 inline for (macs) |M| {
179 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
180 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
181 try printPad(stdout, M.name);
182 try stdout.print(": {} MiB/s\n", throughput / (1 * MiB));
183 }
184 }
185
186 inline for (exchanges) |E| {
187 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
188 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
189 try printPad(stdout, E.name);
190 try stdout.print(": {} exchanges/s\n", throughput);
191 }
192 }
38193}
std/crypto/x25519.zig created+664
......@@ -0,0 +1,664 @@
1// Translated from monocypher which is licensed under CC-0/BSD-3.
2//
3// https://monocypher.org/
4
5const std = @import("../index.zig");
6const builtin = @import("builtin");
7
8const Endian = builtin.Endian;
9const readInt = std.mem.readInt;
10const writeInt = std.mem.writeInt;
11
12// Based on Supercop's ref10 implementation.
13pub const X25519 = struct {
14 pub const secret_length = 32;
15 pub const minimum_key_length = 32;
16
17 fn trimScalar(s: []u8) void {
18 s[0] &= 248;
19 s[31] &= 127;
20 s[31] |= 64;
21 }
22
23 fn scalarBit(s: []const u8, i: usize) i32 {
24 return (s[i >> 3] >> @intCast(u3, i & 7)) & 1;
25 }
26
27 pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
28 std.debug.assert(out.len >= secret_length);
29 std.debug.assert(private_key.len >= minimum_key_length);
30 std.debug.assert(public_key.len >= minimum_key_length);
31
32 var storage: [7]Fe = undefined;
33 var x1 = &storage[0];
34 var x2 = &storage[1];
35 var z2 = &storage[2];
36 var x3 = &storage[3];
37 var z3 = &storage[4];
38 var t0 = &storage[5];
39 var t1 = &storage[6];
40
41 // computes the scalar product
42 Fe.fromBytes(x1, public_key);
43
44 // restrict the possible scalar values
45 var e: [32]u8 = undefined;
46 for (e[0..]) |_, i| {
47 e[i] = private_key[i];
48 }
49 trimScalar(e[0..]);
50
51 // computes the actual scalar product (the result is in x2 and z2)
52
53 // Montgomery ladder
54 // In projective coordinates, to avoid divisons: x = X / Z
55 // We don't care about the y coordinate, it's only 1 bit of information
56 Fe.init1(x2);
57 Fe.init0(z2); // "zero" point
58 Fe.copy(x3, x1);
59 Fe.init1(z3);
60
61 var swap: i32 = 0;
62 var pos: isize = 254;
63 while (pos >= 0) : (pos -= 1) {
64 // constant time conditional swap before ladder step
65 const b = scalarBit(e, @intCast(usize, pos));
66 swap ^= b; // xor trick avoids swapping at the end of the loop
67 Fe.cswap(x2, x3, swap);
68 Fe.cswap(z2, z3, swap);
69 swap = b; // anticipates one last swap after the loop
70
71 // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
72 // with differential addition
73 Fe.sub(t0, x3, z3);
74 Fe.sub(t1, x2, z2);
75 Fe.add(x2, x2, z2);
76 Fe.add(z2, x3, z3);
77 Fe.mul(z3, t0, x2);
78 Fe.mul(z2, z2, t1);
79 Fe.sq(t0, t1);
80 Fe.sq(t1, x2);
81 Fe.add(x3, z3, z2);
82 Fe.sub(z2, z3, z2);
83 Fe.mul(x2, t1, t0);
84 Fe.sub(t1, t1, t0);
85 Fe.sq(z2, z2);
86 Fe.mulSmall(z3, t1, 121666);
87 Fe.sq(x3, x3);
88 Fe.add(t0, t0, z3);
89 Fe.mul(z3, x1, z2);
90 Fe.mul(z2, t1, t0);
91 }
92
93 // last swap is necessary to compensate for the xor trick
94 // Note: after this swap, P3 == P2 + P1.
95 Fe.cswap(x2, x3, swap);
96 Fe.cswap(z2, z3, swap);
97
98 // normalises the coordinates: x == X / Z
99 Fe.invert(z2, z2);
100 Fe.mul(x2, x2, z2);
101 Fe.toBytes(out, x2);
102
103 x1.secureZero();
104 x2.secureZero();
105 x3.secureZero();
106 t0.secureZero();
107 t1.secureZero();
108 z2.secureZero();
109 z3.secureZero();
110 std.mem.secureZero(u8, e[0..]);
111
112 // Returns false if the output is all zero
113 // (happens with some malicious public keys)
114 return !zerocmp(u8, out);
115 }
116
117 pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool {
118 var base_point = []u8{9} ++ []u8{0} ** 31;
119 return create(public_key, private_key, base_point);
120 }
121};
122
123// Constant time compare to zero.
124fn zerocmp(comptime T: type, a: []const T) bool {
125 var s: T = 0;
126 for (a) |b| {
127 s |= b;
128 }
129 return s == 0;
130}
131
132////////////////////////////////////
133/// Arithmetic modulo 2^255 - 19 ///
134////////////////////////////////////
135// Taken from Supercop's ref10 implementation.
136// A bit bigger than TweetNaCl, over 4 times faster.
137
138// field element
139const Fe = struct {
140 b: [10]i32,
141
142 fn secureZero(self: *Fe) void {
143 std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]);
144 }
145
146 fn init0(h: *Fe) void {
147 for (h.b) |*e| {
148 e.* = 0;
149 }
150 }
151
152 fn init1(h: *Fe) void {
153 for (h.b[1..]) |*e| {
154 e.* = 0;
155 }
156 h.b[0] = 1;
157 }
158
159 fn copy(h: *Fe, f: *const Fe) void {
160 for (h.b) |_, i| {
161 h.b[i] = f.b[i];
162 }
163 }
164
165 fn neg(h: *Fe, f: *const Fe) void {
166 for (h.b) |_, i| {
167 h.b[i] = -f.b[i];
168 }
169 }
170
171 fn add(h: *Fe, f: *const Fe, g: *const Fe) void {
172 for (h.b) |_, i| {
173 h.b[i] = f.b[i] + g.b[i];
174 }
175 }
176
177 fn sub(h: *Fe, f: *const Fe, g: *const Fe) void {
178 for (h.b) |_, i| {
179 h.b[i] = f.b[i] - g.b[i];
180 }
181 }
182
183 fn cswap(f: *Fe, g: *Fe, b: i32) void {
184 for (f.b) |_, i| {
185 const x = (f.b[i] ^ g.b[i]) & -b;
186 f.b[i] ^= x;
187 g.b[i] ^= x;
188 }
189 }
190
191 fn ccopy(f: *Fe, g: *const Fe, b: i32) void {
192 for (f.b) |_, i| {
193 const x = (f.b[i] ^ g.b[i]) & -b;
194 f.b[i] ^= x;
195 }
196 }
197
198 inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void {
199 const j = (i + 1) % 10;
200
201 c[i] = (t[i] + (i64(1) << shift)) >> (shift + 1);
202 t[j] += c[i] * mult;
203 t[i] -= c[i] * (i64(1) << (shift + 1));
204 }
205
206 fn carry1(h: *Fe, t: []i64) void {
207 var c: [10]i64 = undefined;
208
209 var sc = c[0..];
210 var st = t[0..];
211
212 carryRound(sc, st, 9, 24, 19);
213 carryRound(sc, st, 1, 24, 1);
214 carryRound(sc, st, 3, 24, 1);
215 carryRound(sc, st, 5, 24, 1);
216 carryRound(sc, st, 7, 24, 1);
217 carryRound(sc, st, 0, 25, 1);
218 carryRound(sc, st, 2, 25, 1);
219 carryRound(sc, st, 4, 25, 1);
220 carryRound(sc, st, 6, 25, 1);
221 carryRound(sc, st, 8, 25, 1);
222
223 for (h.b) |_, i| {
224 h.b[i] = @intCast(i32, t[i]);
225 }
226 }
227
228 fn carry2(h: *Fe, t: []i64) void {
229 var c: [10]i64 = undefined;
230
231 var sc = c[0..];
232 var st = t[0..];
233
234 carryRound(sc, st, 0, 25, 1);
235 carryRound(sc, st, 4, 25, 1);
236 carryRound(sc, st, 1, 24, 1);
237 carryRound(sc, st, 5, 24, 1);
238 carryRound(sc, st, 2, 25, 1);
239 carryRound(sc, st, 6, 25, 1);
240 carryRound(sc, st, 3, 24, 1);
241 carryRound(sc, st, 7, 24, 1);
242 carryRound(sc, st, 4, 25, 1);
243 carryRound(sc, st, 8, 25, 1);
244 carryRound(sc, st, 9, 24, 19);
245 carryRound(sc, st, 0, 25, 1);
246
247 for (h.b) |_, i| {
248 h.b[i] = @intCast(i32, t[i]);
249 }
250 }
251
252 fn fromBytes(h: *Fe, s: []const u8) void {
253 std.debug.assert(s.len >= 32);
254
255 var t: [10]i64 = undefined;
256
257 t[0] = readInt(s[0..4], u32, Endian.Little);
258 t[1] = readInt(s[4..7], u32, Endian.Little) << 6;
259 t[2] = readInt(s[7..10], u32, Endian.Little) << 5;
260 t[3] = readInt(s[10..13], u32, Endian.Little) << 3;
261 t[4] = readInt(s[13..16], u32, Endian.Little) << 2;
262 t[5] = readInt(s[16..20], u32, Endian.Little);
263 t[6] = readInt(s[20..23], u32, Endian.Little) << 7;
264 t[7] = readInt(s[23..26], u32, Endian.Little) << 5;
265 t[8] = readInt(s[26..29], u32, Endian.Little) << 4;
266 t[9] = (readInt(s[29..32], u32, Endian.Little) & 0x7fffff) << 2;
267
268 carry1(h, t[0..]);
269 }
270
271 fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void {
272 var t: [10]i64 = undefined;
273
274 for (t[0..]) |_, i| {
275 t[i] = i64(f.b[i]) * g;
276 }
277
278 carry1(h, t[0..]);
279 }
280
281 fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void {
282 const f = f1.b;
283 const g = g1.b;
284
285 var F: [10]i32 = undefined;
286 var G: [10]i32 = undefined;
287
288 F[1] = f[1] * 2;
289 F[3] = f[3] * 2;
290 F[5] = f[5] * 2;
291 F[7] = f[7] * 2;
292 F[9] = f[9] * 2;
293
294 G[1] = g[1] * 19;
295 G[2] = g[2] * 19;
296 G[3] = g[3] * 19;
297 G[4] = g[4] * 19;
298 G[5] = g[5] * 19;
299 G[6] = g[6] * 19;
300 G[7] = g[7] * 19;
301 G[8] = g[8] * 19;
302 G[9] = g[9] * 19;
303
304 // t's become h
305 var t: [10]i64 = undefined;
306
307 t[0] = f[0] * i64(g[0]) + F[1] * i64(G[9]) + f[2] * i64(G[8]) + F[3] * i64(G[7]) + f[4] * i64(G[6]) + F[5] * i64(G[5]) + f[6] * i64(G[4]) + F[7] * i64(G[3]) + f[8] * i64(G[2]) + F[9] * i64(G[1]);
308 t[1] = f[0] * i64(g[1]) + f[1] * i64(g[0]) + f[2] * i64(G[9]) + f[3] * i64(G[8]) + f[4] * i64(G[7]) + f[5] * i64(G[6]) + f[6] * i64(G[5]) + f[7] * i64(G[4]) + f[8] * i64(G[3]) + f[9] * i64(G[2]);
309 t[2] = f[0] * i64(g[2]) + F[1] * i64(g[1]) + f[2] * i64(g[0]) + F[3] * i64(G[9]) + f[4] * i64(G[8]) + F[5] * i64(G[7]) + f[6] * i64(G[6]) + F[7] * i64(G[5]) + f[8] * i64(G[4]) + F[9] * i64(G[3]);
310 t[3] = f[0] * i64(g[3]) + f[1] * i64(g[2]) + f[2] * i64(g[1]) + f[3] * i64(g[0]) + f[4] * i64(G[9]) + f[5] * i64(G[8]) + f[6] * i64(G[7]) + f[7] * i64(G[6]) + f[8] * i64(G[5]) + f[9] * i64(G[4]);
311 t[4] = f[0] * i64(g[4]) + F[1] * i64(g[3]) + f[2] * i64(g[2]) + F[3] * i64(g[1]) + f[4] * i64(g[0]) + F[5] * i64(G[9]) + f[6] * i64(G[8]) + F[7] * i64(G[7]) + f[8] * i64(G[6]) + F[9] * i64(G[5]);
312 t[5] = f[0] * i64(g[5]) + f[1] * i64(g[4]) + f[2] * i64(g[3]) + f[3] * i64(g[2]) + f[4] * i64(g[1]) + f[5] * i64(g[0]) + f[6] * i64(G[9]) + f[7] * i64(G[8]) + f[8] * i64(G[7]) + f[9] * i64(G[6]);
313 t[6] = f[0] * i64(g[6]) + F[1] * i64(g[5]) + f[2] * i64(g[4]) + F[3] * i64(g[3]) + f[4] * i64(g[2]) + F[5] * i64(g[1]) + f[6] * i64(g[0]) + F[7] * i64(G[9]) + f[8] * i64(G[8]) + F[9] * i64(G[7]);
314 t[7] = f[0] * i64(g[7]) + f[1] * i64(g[6]) + f[2] * i64(g[5]) + f[3] * i64(g[4]) + f[4] * i64(g[3]) + f[5] * i64(g[2]) + f[6] * i64(g[1]) + f[7] * i64(g[0]) + f[8] * i64(G[9]) + f[9] * i64(G[8]);
315 t[8] = f[0] * i64(g[8]) + F[1] * i64(g[7]) + f[2] * i64(g[6]) + F[3] * i64(g[5]) + f[4] * i64(g[4]) + F[5] * i64(g[3]) + f[6] * i64(g[2]) + F[7] * i64(g[1]) + f[8] * i64(g[0]) + F[9] * i64(G[9]);
316 t[9] = f[0] * i64(g[9]) + f[1] * i64(g[8]) + f[2] * i64(g[7]) + f[3] * i64(g[6]) + f[4] * i64(g[5]) + f[5] * i64(g[4]) + f[6] * i64(g[3]) + f[7] * i64(g[2]) + f[8] * i64(g[1]) + f[9] * i64(g[0]);
317
318 carry2(h, t[0..]);
319 }
320
321 // we could use Fe.mul() for this, but this is significantly faster
322 fn sq(h: *Fe, fz: *const Fe) void {
323 const f0 = fz.b[0];
324 const f1 = fz.b[1];
325 const f2 = fz.b[2];
326 const f3 = fz.b[3];
327 const f4 = fz.b[4];
328 const f5 = fz.b[5];
329 const f6 = fz.b[6];
330 const f7 = fz.b[7];
331 const f8 = fz.b[8];
332 const f9 = fz.b[9];
333
334 const f0_2 = f0 * 2;
335 const f1_2 = f1 * 2;
336 const f2_2 = f2 * 2;
337 const f3_2 = f3 * 2;
338 const f4_2 = f4 * 2;
339 const f5_2 = f5 * 2;
340 const f6_2 = f6 * 2;
341 const f7_2 = f7 * 2;
342 const f5_38 = f5 * 38;
343 const f6_19 = f6 * 19;
344 const f7_38 = f7 * 38;
345 const f8_19 = f8 * 19;
346 const f9_38 = f9 * 38;
347
348 var t: [10]i64 = undefined;
349
350 t[0] = f0 * i64(f0) + f1_2 * i64(f9_38) + f2_2 * i64(f8_19) + f3_2 * i64(f7_38) + f4_2 * i64(f6_19) + f5 * i64(f5_38);
351 t[1] = f0_2 * i64(f1) + f2 * i64(f9_38) + f3_2 * i64(f8_19) + f4 * i64(f7_38) + f5_2 * i64(f6_19);
352 t[2] = f0_2 * i64(f2) + f1_2 * i64(f1) + f3_2 * i64(f9_38) + f4_2 * i64(f8_19) + f5_2 * i64(f7_38) + f6 * i64(f6_19);
353 t[3] = f0_2 * i64(f3) + f1_2 * i64(f2) + f4 * i64(f9_38) + f5_2 * i64(f8_19) + f6 * i64(f7_38);
354 t[4] = f0_2 * i64(f4) + f1_2 * i64(f3_2) + f2 * i64(f2) + f5_2 * i64(f9_38) + f6_2 * i64(f8_19) + f7 * i64(f7_38);
355 t[5] = f0_2 * i64(f5) + f1_2 * i64(f4) + f2_2 * i64(f3) + f6 * i64(f9_38) + f7_2 * i64(f8_19);
356 t[6] = f0_2 * i64(f6) + f1_2 * i64(f5_2) + f2_2 * i64(f4) + f3_2 * i64(f3) + f7_2 * i64(f9_38) + f8 * i64(f8_19);
357 t[7] = f0_2 * i64(f7) + f1_2 * i64(f6) + f2_2 * i64(f5) + f3_2 * i64(f4) + f8 * i64(f9_38);
358 t[8] = f0_2 * i64(f8) + f1_2 * i64(f7_2) + f2_2 * i64(f6) + f3_2 * i64(f5_2) + f4 * i64(f4) + f9 * i64(f9_38);
359 t[9] = f0_2 * i64(f9) + f1_2 * i64(f8) + f2_2 * i64(f7) + f3_2 * i64(f6) + f4 * i64(f5_2);
360
361 carry2(h, t[0..]);
362 }
363
364 fn sq2(h: *Fe, f: *const Fe) void {
365 Fe.sq(h, f);
366 Fe.mul_small(h, h, 2);
367 }
368
369 // This could be simplified, but it would be slower
370 fn invert(out: *Fe, z: *const Fe) void {
371 var i: usize = undefined;
372
373 var t: [4]Fe = undefined;
374 var t0 = &t[0];
375 var t1 = &t[1];
376 var t2 = &t[2];
377 var t3 = &t[3];
378
379 Fe.sq(t0, z);
380 Fe.sq(t1, t0);
381 Fe.sq(t1, t1);
382 Fe.mul(t1, z, t1);
383 Fe.mul(t0, t0, t1);
384
385 Fe.sq(t2, t0);
386 Fe.mul(t1, t1, t2);
387
388 Fe.sq(t2, t1);
389 i = 1;
390 while (i < 5) : (i += 1) Fe.sq(t2, t2);
391 Fe.mul(t1, t2, t1);
392
393 Fe.sq(t2, t1);
394 i = 1;
395 while (i < 10) : (i += 1) Fe.sq(t2, t2);
396 Fe.mul(t2, t2, t1);
397
398 Fe.sq(t3, t2);
399 i = 1;
400 while (i < 20) : (i += 1) Fe.sq(t3, t3);
401 Fe.mul(t2, t3, t2);
402
403 Fe.sq(t2, t2);
404 i = 1;
405 while (i < 10) : (i += 1) Fe.sq(t2, t2);
406 Fe.mul(t1, t2, t1);
407
408 Fe.sq(t2, t1);
409 i = 1;
410 while (i < 50) : (i += 1) Fe.sq(t2, t2);
411 Fe.mul(t2, t2, t1);
412
413 Fe.sq(t3, t2);
414 i = 1;
415 while (i < 100) : (i += 1) Fe.sq(t3, t3);
416 Fe.mul(t2, t3, t2);
417
418 Fe.sq(t2, t2);
419 i = 1;
420 while (i < 50) : (i += 1) Fe.sq(t2, t2);
421 Fe.mul(t1, t2, t1);
422
423 Fe.sq(t1, t1);
424 i = 1;
425 while (i < 5) : (i += 1) Fe.sq(t1, t1);
426 Fe.mul(out, t1, t0);
427
428 t0.secureZero();
429 t1.secureZero();
430 t2.secureZero();
431 t3.secureZero();
432 }
433
434 // This could be simplified, but it would be slower
435 fn pow22523(out: *Fe, z: *const Fe) void {
436 var i: usize = undefined;
437
438 var t: [3]Fe = undefined;
439 var t0 = &t[0];
440 var t1 = &t[1];
441 var t2 = &t[2];
442
443 Fe.sq(t0, z);
444 Fe.sq(t1, t0);
445 Fe.sq(t1, t1);
446 Fe.mul(t1, z, t1);
447 Fe.mul(t0, t0, t1);
448
449 Fe.sq(t0, t0);
450 Fe.mul(t0, t1, t0);
451
452 Fe.sq(t1, t0);
453 i = 1;
454 while (i < 5) : (i += 1) Fe.sq(t1, t1);
455 Fe.mul(t0, t1, t0);
456
457 Fe.sq(t1, t0);
458 i = 1;
459 while (i < 10) : (i += 1) Fe.sq(t1, t1);
460 Fe.mul(t1, t1, t0);
461
462 Fe.sq(t2, t1);
463 i = 1;
464 while (i < 20) : (i += 1) Fe.sq(t2, t2);
465 Fe.mul(t1, t2, t1);
466
467 Fe.sq(t1, t1);
468 i = 1;
469 while (i < 10) : (i += 1) Fe.sq(t1, t1);
470 Fe.mul(t0, t1, t0);
471
472 Fe.sq(t1, t0);
473 i = 1;
474 while (i < 50) : (i += 1) Fe.sq(t1, t1);
475 Fe.mul(t1, t1, t0);
476
477 Fe.sq(t2, t1);
478 i = 1;
479 while (i < 100) : (i += 1) Fe.sq(t2, t2);
480 Fe.mul(t1, t2, t1);
481
482 Fe.sq(t1, t1);
483 i = 1;
484 while (i < 50) : (i += 1) Fe.sq(t1, t1);
485 Fe.mul(t0, t1, t0);
486
487 Fe.sq(t0, t0);
488 i = 1;
489 while (i < 2) : (i += 1) Fe.sq(t0, t0);
490 Fe.mul(out, t0, z);
491
492 t0.secureZero();
493 t1.secureZero();
494 t2.secureZero();
495 }
496
497 inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void {
498 c[i] = t[i] >> shift;
499 if (i + 1 < 10) {
500 t[i + 1] += c[i];
501 }
502 t[i] -= c[i] * (i32(1) << shift);
503 }
504
505 fn toBytes(s: []u8, h: *const Fe) void {
506 std.debug.assert(s.len >= 32);
507
508 var t: [10]i64 = undefined;
509 for (h.b[0..]) |_, i| {
510 t[i] = h.b[i];
511 }
512
513 var q = (19 * t[9] + ((i32(1) << 24))) >> 25;
514 {
515 var i: usize = 0;
516 while (i < 5) : (i += 1) {
517 q += t[2 * i];
518 q >>= 26;
519 q += t[2 * i + 1];
520 q >>= 25;
521 }
522 }
523 t[0] += 19 * q;
524
525 var c: [10]i64 = undefined;
526
527 var st = t[0..];
528 var sc = c[0..];
529
530 toBytesRound(sc, st, 0, 26);
531 toBytesRound(sc, st, 1, 25);
532 toBytesRound(sc, st, 2, 26);
533 toBytesRound(sc, st, 3, 25);
534 toBytesRound(sc, st, 4, 26);
535 toBytesRound(sc, st, 5, 25);
536 toBytesRound(sc, st, 6, 26);
537 toBytesRound(sc, st, 7, 25);
538 toBytesRound(sc, st, 8, 26);
539 toBytesRound(sc, st, 9, 25);
540
541 var ut: [10]u32 = undefined;
542 for (ut[0..]) |_, i| {
543 ut[i] = @bitCast(u32, @intCast(i32, t[i]));
544 }
545
546 writeInt(s[0..], (ut[0] >> 0) | (ut[1] << 26), Endian.Little);
547 writeInt(s[4..], (ut[1] >> 6) | (ut[2] << 19), Endian.Little);
548 writeInt(s[8..], (ut[2] >> 13) | (ut[3] << 13), Endian.Little);
549 writeInt(s[12..], (ut[3] >> 19) | (ut[4] << 6), Endian.Little);
550 writeInt(s[16..], (ut[5] >> 0) | (ut[6] << 25), Endian.Little);
551 writeInt(s[20..], (ut[6] >> 7) | (ut[7] << 19), Endian.Little);
552 writeInt(s[24..], (ut[7] >> 13) | (ut[8] << 12), Endian.Little);
553 writeInt(s[28..], (ut[8] >> 20) | (ut[9] << 6), Endian.Little);
554
555 std.mem.secureZero(i64, t[0..]);
556 }
557
558 // Parity check. Returns 0 if even, 1 if odd
559 fn isNegative(f: *const Fe) bool {
560 var s: [32]u8 = undefined;
561 Fe.toBytes(s[0..], f);
562 const isneg = s[0] & 1;
563 s.secureZero();
564 return isneg;
565 }
566
567 fn isNonZero(f: *const Fe) bool {
568 var s: [32]u8 = undefined;
569 Fe.toBytes(s[0..], f);
570 const isnonzero = zerocmp(u8, s[0..]);
571 s.secureZero();
572 return isneg;
573 }
574};
575
576test "x25519 rfc7748 vector1" {
577 const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4";
578 const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c";
579
580 const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52";
581
582 var output: [32]u8 = undefined;
583
584 std.debug.assert(X25519.create(output[0..], secret_key, public_key));
585 std.debug.assert(std.mem.eql(u8, output, expected_output));
586}
587
588test "x25519 rfc7748 vector2" {
589 const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d";
590 const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93";
591
592 const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57";
593
594 var output: [32]u8 = undefined;
595
596 std.debug.assert(X25519.create(output[0..], secret_key, public_key));
597 std.debug.assert(std.mem.eql(u8, output, expected_output));
598}
599
600test "x25519 rfc7748 one iteration" {
601 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
602 const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79";
603
604 var k: [32]u8 = initial_value;
605 var u: [32]u8 = initial_value;
606
607 var i: usize = 0;
608 while (i < 1) : (i += 1) {
609 var output: [32]u8 = undefined;
610 std.debug.assert(X25519.create(output[0..], k, u));
611
612 std.mem.copy(u8, u[0..], k[0..]);
613 std.mem.copy(u8, k[0..], output[0..]);
614 }
615
616 std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
617}
618
619test "x25519 rfc7748 1,000 iterations" {
620 // These iteration tests are slow so we always skip them. Results have been verified.
621 if (true) {
622 return error.SkipZigTest;
623 }
624
625 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
626 const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51";
627
628 var k: [32]u8 = initial_value;
629 var u: [32]u8 = initial_value;
630
631 var i: usize = 0;
632 while (i < 1000) : (i += 1) {
633 var output: [32]u8 = undefined;
634 std.debug.assert(X25519.create(output[0..], k, u));
635
636 std.mem.copy(u8, u[0..], k[0..]);
637 std.mem.copy(u8, k[0..], output[0..]);
638 }
639
640 std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
641}
642
643test "x25519 rfc7748 1,000,000 iterations" {
644 if (true) {
645 return error.SkipZigTest;
646 }
647
648 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
649 const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24";
650
651 var k: [32]u8 = initial_value;
652 var u: [32]u8 = initial_value;
653
654 var i: usize = 0;
655 while (i < 1000000) : (i += 1) {
656 var output: [32]u8 = undefined;
657 std.debug.assert(X25519.create(output[0..], k, u));
658
659 std.mem.copy(u8, u[0..], k[0..]);
660 std.mem.copy(u8, k[0..], output[0..]);
661 }
662
663 std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
664}
std/debug/index.zig+494-20
......@@ -4,8 +4,11 @@ const mem = std.mem;
44const io = std.io;
55const os = std.os;
66const elf = std.elf;
7const macho = std.macho;
87const DW = std.dwarf;
8const macho = std.macho;
9const coff = std.coff;
10const pdb = std.pdb;
11const windows = os.windows;
912const ArrayList = std.ArrayList;
1013const builtin = @import("builtin");
1114
......@@ -17,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) {
1720 builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
1821};
1922
23const Module = struct {
24 mod_info: pdb.ModInfo,
25 module_name: []u8,
26 obj_file_name: []u8,
27
28 populated: bool,
29 symbols: []u8,
30 subsect_info: []u8,
31 checksum_offset: ?usize,
32};
33
2034/// Tries to write to stderr, unbuffered, and ignores any error returned.
2135/// Does not append a newline.
2236var stderr_file: os.File = undefined;
......@@ -37,7 +51,7 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
3751 return st;
3852 } else {
3953 stderr_file = try io.getStdErr();
40 stderr_file_out_stream = io.FileOutStream.init(&stderr_file);
54 stderr_file_out_stream = io.FileOutStream.init(stderr_file);
4155 const st = &stderr_file_out_stream.stream;
4256 stderr_stream = st;
4357 return st;
......@@ -70,7 +84,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
7084 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
7185 return;
7286 };
73 writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| {
87 writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| {
7488 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
7589 return;
7690 };
......@@ -191,7 +205,11 @@ pub inline fn getReturnAddress(frame_count: usize) usize {
191205 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
192206}
193207
194pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
208pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
209 switch (builtin.os) {
210 builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr),
211 else => {},
212 }
195213 const AddressState = union(enum) {
196214 NotLookingForStartAddress,
197215 LookingForStartAddress: usize,
......@@ -224,18 +242,296 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
224242 }
225243}
226244
245pub fn writeCurrentStackTraceWindows(out_stream: var, debug_info: *DebugInfo,
246 tty_color: bool, start_addr: ?usize) !void
247{
248 var addr_buf: [1024]usize = undefined;
249 const casted_len = @intCast(u32, addr_buf.len); // TODO shouldn't need this cast
250 const n = windows.RtlCaptureStackBackTrace(0, casted_len, @ptrCast(**c_void, &addr_buf), null);
251 const addrs = addr_buf[0..n];
252 var start_i: usize = if (start_addr) |saddr| blk: {
253 for (addrs) |addr, i| {
254 if (addr == saddr) break :blk i;
255 }
256 return;
257 } else 0;
258 for (addrs[start_i..]) |addr| {
259 try printSourceAtAddress(debug_info, out_stream, addr, tty_color);
260 }
261}
262
227263pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
228264 switch (builtin.os) {
229265 builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color),
230266 builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color),
231 builtin.Os.windows => {
232 // TODO https://github.com/ziglang/zig/issues/721
233 return error.UnsupportedOperatingSystem;
234 },
267 builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color),
235268 else => return error.UnsupportedOperatingSystem,
236269 }
237270}
238271
272fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void {
273 const allocator = getDebugInfoAllocator();
274 const base_address = os.getBaseAddress();
275 const relative_address = relocated_address - base_address;
276
277 var coff_section: *coff.Section = undefined;
278 const mod_index = for (di.sect_contribs) |sect_contrib| {
279 if (sect_contrib.Section >= di.coff.sections.len) continue;
280 coff_section = &di.coff.sections.toSlice()[sect_contrib.Section];
281
282 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
283 const vaddr_end = vaddr_start + sect_contrib.Size;
284 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
285 break sect_contrib.ModuleIndex;
286 }
287 } else {
288 // we have no information to add to the address
289 if (tty_color) {
290 try out_stream.print("???:?:?: ");
291 setTtyColor(TtyColor.Dim);
292 try out_stream.print("0x{x} in ??? (???)", relocated_address);
293 setTtyColor(TtyColor.Reset);
294 try out_stream.print("\n\n\n");
295 } else {
296 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address);
297 }
298 return;
299 };
300
301 const mod = &di.modules[mod_index];
302 try populateModule(di, mod);
303 const obj_basename = os.path.basename(mod.obj_file_name);
304
305 var symbol_i: usize = 0;
306 const symbol_name = while (symbol_i != mod.symbols.len) {
307 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
308 if (prefix.RecordLen < 2)
309 return error.InvalidDebugInfo;
310 switch (prefix.RecordKind) {
311 pdb.SymbolKind.S_LPROC32 => {
312 const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
313 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
314 const vaddr_end = vaddr_start + proc_sym.CodeSize;
315 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
316 break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym));
317 }
318 },
319 else => {},
320 }
321 symbol_i += prefix.RecordLen + @sizeOf(u16);
322 if (symbol_i > mod.symbols.len)
323 return error.InvalidDebugInfo;
324 } else "???";
325
326 const subsect_info = mod.subsect_info;
327
328 var sect_offset: usize = 0;
329 var skip_len: usize = undefined;
330 const opt_line_info = subsections: {
331 const checksum_offset = mod.checksum_offset orelse break :subsections null;
332 while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
333 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
334 skip_len = subsect_hdr.Length;
335 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
336
337 switch (subsect_hdr.Kind) {
338 pdb.DebugSubsectionKind.Lines => {
339 var line_index: usize = sect_offset;
340
341 const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
342 if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
343 line_index += @sizeOf(pdb.LineFragmentHeader);
344
345 const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
346 line_index += @sizeOf(pdb.LineBlockFragmentHeader);
347
348 const has_column = line_hdr.Flags.LF_HaveColumns;
349
350 const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
351 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
352 if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) {
353 var line_i: usize = 0;
354 const start_line_index = line_index;
355 while (line_i < block_hdr.NumLines) : (line_i += 1) {
356 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
357 line_index += @sizeOf(pdb.LineNumberEntry);
358 const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
359 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
360 const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End;
361 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
362 const subsect_index = checksum_offset + block_hdr.NameIndex;
363 const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
364 const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
365 try di.pdb.string_table.seekTo(strtab_offset);
366 const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
367 const line = flags.Start;
368 const column = if (has_column) blk: {
369 line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
370 line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i;
371 const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]);
372 break :blk col_num_entry.StartColumn;
373 } else 0;
374 break :subsections LineInfo{
375 .allocator = allocator,
376 .file_name = source_file_name,
377 .line = line,
378 .column = column,
379 };
380 }
381 }
382 break :subsections null;
383 }
384 },
385 else => {},
386 }
387
388 if (sect_offset > subsect_info.len)
389 return error.InvalidDebugInfo;
390 } else {
391 break :subsections null;
392 }
393 };
394
395 if (tty_color) {
396 setTtyColor(TtyColor.White);
397 if (opt_line_info) |li| {
398 try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column);
399 } else {
400 try out_stream.print("???:?:?");
401 }
402 setTtyColor(TtyColor.Reset);
403 try out_stream.print(": ");
404 setTtyColor(TtyColor.Dim);
405 try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename);
406 setTtyColor(TtyColor.Reset);
407
408 if (opt_line_info) |line_info| {
409 try out_stream.print("\n");
410 if (printLineFromFile(out_stream, line_info)) {
411 if (line_info.column == 0) {
412 try out_stream.write("\n");
413 } else {
414 {
415 var col_i: usize = 1;
416 while (col_i < line_info.column) : (col_i += 1) {
417 try out_stream.writeByte(' ');
418 }
419 }
420 setTtyColor(TtyColor.Green);
421 try out_stream.write("^");
422 setTtyColor(TtyColor.Reset);
423 try out_stream.write("\n");
424 }
425 } else |err| switch (err) {
426 error.EndOfFile => {},
427 else => return err,
428 }
429 } else {
430 try out_stream.print("\n\n\n");
431 }
432 } else {
433 if (opt_line_info) |li| {
434 try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename);
435 } else {
436 try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename);
437 }
438 }
439}
440
441const TtyColor = enum{
442 Red,
443 Green,
444 Cyan,
445 White,
446 Dim,
447 Bold,
448 Reset,
449};
450
451/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt
452fn setTtyColor(tty_color: TtyColor) void {
453 const S = struct {
454 var attrs: windows.WORD = undefined;
455 var init_attrs = false;
456 };
457 if (!S.init_attrs) {
458 S.init_attrs = true;
459 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
460 // TODO handle error
461 _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info);
462 S.attrs = info.wAttributes;
463 }
464
465 // TODO handle errors
466 switch (tty_color) {
467 TtyColor.Red => {
468 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY);
469 },
470 TtyColor.Green => {
471 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY);
472 },
473 TtyColor.Cyan => {
474 _ = windows.SetConsoleTextAttribute(stderr_file.handle,
475 windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
476 },
477 TtyColor.White, TtyColor.Bold => {
478 _ = windows.SetConsoleTextAttribute(stderr_file.handle,
479 windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
480 },
481 TtyColor.Dim => {
482 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY);
483 },
484 TtyColor.Reset => {
485 _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs);
486 },
487 }
488}
489
490fn populateModule(di: *DebugInfo, mod: *Module) !void {
491 if (mod.populated)
492 return;
493 const allocator = getDebugInfoAllocator();
494
495 if (mod.mod_info.C11ByteSize != 0)
496 return error.InvalidDebugInfo;
497
498 if (mod.mod_info.C13ByteSize == 0)
499 return error.MissingDebugInfo;
500
501 const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
502
503 const signature = try modi.stream.readIntLe(u32);
504 if (signature != 4)
505 return error.InvalidDebugInfo;
506
507 mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
508 try modi.stream.readNoEof(mod.symbols);
509
510 mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
511 try modi.stream.readNoEof(mod.subsect_info);
512
513 var sect_offset: usize = 0;
514 var skip_len: usize = undefined;
515 while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
516 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
517 skip_len = subsect_hdr.Length;
518 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
519
520 switch (subsect_hdr.Kind) {
521 pdb.DebugSubsectionKind.FileChecksums => {
522 mod.checksum_offset = sect_offset;
523 break;
524 },
525 else => {},
526 }
527
528 if (sect_offset > mod.subsect_info.len)
529 return error.InvalidDebugInfo;
530 }
531
532 mod.populated = true;
533}
534
239535fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
240536 var min: usize = 0;
241537 var max: usize = symbols.len - 1; // Exclude sentinel.
......@@ -372,14 +668,185 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {
372668 switch (builtin.os) {
373669 builtin.Os.linux => return openSelfDebugInfoLinux(allocator),
374670 builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator),
375 builtin.Os.windows => {
376 // TODO: https://github.com/ziglang/zig/issues/721
377 return error.UnsupportedOperatingSystem;
378 },
671 builtin.Os.windows => return openSelfDebugInfoWindows(allocator),
379672 else => return error.UnsupportedOperatingSystem,
380673 }
381674}
382675
676fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
677 const self_file = try os.openSelfExe();
678 defer self_file.close();
679
680 const coff_obj = try allocator.createOne(coff.Coff);
681 coff_obj.* = coff.Coff{
682 .in_file = self_file,
683 .allocator = allocator,
684 .coff_header = undefined,
685 .pe_header = undefined,
686 .sections = undefined,
687 .guid = undefined,
688 .age = undefined,
689 };
690
691 var di = DebugInfo{
692 .coff = coff_obj,
693 .pdb = undefined,
694 .sect_contribs = undefined,
695 .modules = undefined,
696 };
697
698 try di.coff.loadHeader();
699
700 var path_buf: [windows.MAX_PATH]u8 = undefined;
701 const len = try di.coff.getPdbPath(path_buf[0..]);
702 const raw_path = path_buf[0..len];
703
704 const path = try os.path.resolve(allocator, raw_path);
705
706 try di.pdb.openFile(di.coff, path);
707
708 var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
709 const version = try pdb_stream.stream.readIntLe(u32);
710 const signature = try pdb_stream.stream.readIntLe(u32);
711 const age = try pdb_stream.stream.readIntLe(u32);
712 var guid: [16]u8 = undefined;
713 try pdb_stream.stream.readNoEof(guid[0..]);
714 if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)
715 return error.InvalidDebugInfo;
716 // We validated the executable and pdb match.
717
718 const string_table_index = str_tab_index: {
719 const name_bytes_len = try pdb_stream.stream.readIntLe(u32);
720 const name_bytes = try allocator.alloc(u8, name_bytes_len);
721 try pdb_stream.stream.readNoEof(name_bytes);
722
723 const HashTableHeader = packed struct {
724 Size: u32,
725 Capacity: u32,
726
727 fn maxLoad(cap: u32) u32 {
728 return cap * 2 / 3 + 1;
729 }
730 };
731 var hash_tbl_hdr: HashTableHeader = undefined;
732 try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr);
733 if (hash_tbl_hdr.Capacity == 0)
734 return error.InvalidDebugInfo;
735
736 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
737 return error.InvalidDebugInfo;
738
739 const present = try readSparseBitVector(&pdb_stream.stream, allocator);
740 if (present.len != hash_tbl_hdr.Size)
741 return error.InvalidDebugInfo;
742 const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
743
744 const Bucket = struct {
745 first: u32,
746 second: u32,
747 };
748 const bucket_list = try allocator.alloc(Bucket, present.len);
749 for (present) |_| {
750 const name_offset = try pdb_stream.stream.readIntLe(u32);
751 const name_index = try pdb_stream.stream.readIntLe(u32);
752 const name = mem.toSlice(u8, name_bytes.ptr + name_offset);
753 if (mem.eql(u8, name, "/names")) {
754 break :str_tab_index name_index;
755 }
756 }
757 return error.MissingDebugInfo;
758 };
759
760 di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
761 di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
762
763 const dbi = di.pdb.dbi;
764
765 // Dbi Header
766 var dbi_stream_header: pdb.DbiStreamHeader = undefined;
767 try dbi.stream.readStruct(pdb.DbiStreamHeader, &dbi_stream_header);
768 const mod_info_size = dbi_stream_header.ModInfoSize;
769 const section_contrib_size = dbi_stream_header.SectionContributionSize;
770
771 var modules = ArrayList(Module).init(allocator);
772
773 // Module Info Substream
774 var mod_info_offset: usize = 0;
775 while (mod_info_offset != mod_info_size) {
776 var mod_info: pdb.ModInfo = undefined;
777 try dbi.stream.readStruct(pdb.ModInfo, &mod_info);
778 var this_record_len: usize = @sizeOf(pdb.ModInfo);
779
780 const module_name = try dbi.readNullTermString(allocator);
781 this_record_len += module_name.len + 1;
782
783 const obj_file_name = try dbi.readNullTermString(allocator);
784 this_record_len += obj_file_name.len + 1;
785
786 const march_forward_bytes = this_record_len % 4;
787 if (march_forward_bytes != 0) {
788 try dbi.seekForward(march_forward_bytes);
789 this_record_len += march_forward_bytes;
790 }
791
792 try modules.append(Module{
793 .mod_info = mod_info,
794 .module_name = module_name,
795 .obj_file_name = obj_file_name,
796
797 .populated = false,
798 .symbols = undefined,
799 .subsect_info = undefined,
800 .checksum_offset = null,
801 });
802
803 mod_info_offset += this_record_len;
804 if (mod_info_offset > mod_info_size)
805 return error.InvalidDebugInfo;
806 }
807
808 di.modules = modules.toOwnedSlice();
809
810 // Section Contribution Substream
811 var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
812 var sect_cont_offset: usize = 0;
813 if (section_contrib_size != 0) {
814 const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32));
815 if (ver != pdb.SectionContrSubstreamVersion.Ver60)
816 return error.InvalidDebugInfo;
817 sect_cont_offset += @sizeOf(u32);
818 }
819 while (sect_cont_offset != section_contrib_size) {
820 const entry = try sect_contribs.addOne();
821 try dbi.stream.readStruct(pdb.SectionContribEntry, entry);
822 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
823
824 if (sect_cont_offset > section_contrib_size)
825 return error.InvalidDebugInfo;
826 }
827
828 di.sect_contribs = sect_contribs.toOwnedSlice();
829
830 return di;
831}
832
833fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
834 const num_words = try stream.readIntLe(u32);
835 var word_i: usize = 0;
836 var list = ArrayList(usize).init(allocator);
837 while (word_i != num_words) : (word_i += 1) {
838 const word = try stream.readIntLe(u32);
839 var bit_i: u5 = 0;
840 while (true) : (bit_i += 1) {
841 if (word & (u32(1) << bit_i) != 0) {
842 try list.append(word_i * 32 + bit_i);
843 }
844 if (bit_i == @maxValue(u5)) break;
845 }
846 }
847 return list.toOwnedSlice();
848}
849
383850fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
384851 var di = DebugInfo{
385852 .self_exe_file = undefined,
......@@ -395,7 +862,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
395862 di.self_exe_file = try os.openSelfExe();
396863 errdefer di.self_exe_file.close();
397864
398 try di.elf.openFile(allocator, &di.self_exe_file);
865 try di.elf.openFile(allocator, di.self_exe_file);
399866 errdefer di.elf.close();
400867
401868 di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
......@@ -578,7 +1045,13 @@ pub const DebugInfo = switch (builtin.os) {
5781045 return self.ofiles.allocator;
5791046 }
5801047 },
581 else => struct {
1048 builtin.Os.windows => struct {
1049 pdb: pdb.Pdb,
1050 coff: *coff.Coff,
1051 sect_contribs: []pdb.SectionContribEntry,
1052 modules: []Module,
1053 },
1054 builtin.Os.linux => struct {
5821055 self_exe_file: os.File,
5831056 elf: elf.Elf,
5841057 debug_info: *elf.SectionHeader,
......@@ -594,7 +1067,7 @@ pub const DebugInfo = switch (builtin.os) {
5941067 }
5951068
5961069 pub fn readString(self: *DebugInfo) ![]u8 {
597 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
1070 var in_file_stream = io.FileInStream.init(self.self_exe_file);
5981071 const in_stream = &in_file_stream.stream;
5991072 return readStringRaw(self.allocator(), in_stream);
6001073 }
......@@ -604,6 +1077,7 @@ pub const DebugInfo = switch (builtin.os) {
6041077 self.elf.close();
6051078 }
6061079 },
1080 else => @compileError("Unsupported OS"),
6071081};
6081082
6091083const PcRange = struct {
......@@ -929,7 +1403,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
9291403}
9301404
9311405fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {
932 const in_file = &st.self_exe_file;
1406 const in_file = st.self_exe_file;
9331407 var in_file_stream = io.FileInStream.init(in_file);
9341408 const in_stream = &in_file_stream.stream;
9351409 var result = AbbrevTable.init(st.allocator());
......@@ -980,7 +1454,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
9801454}
9811455
9821456fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
983 const in_file = &st.self_exe_file;
1457 const in_file = st.self_exe_file;
9841458 var in_file_stream = io.FileInStream.init(in_file);
9851459 const in_stream = &in_file_stream.stream;
9861460 const abbrev_code = try readULeb128(in_stream);
......@@ -1202,7 +1676,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
12021676fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
12031677 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
12041678
1205 const in_file = &di.self_exe_file;
1679 const in_file = di.self_exe_file;
12061680 const debug_line_end = di.debug_line.offset + di.debug_line.size;
12071681 var this_offset = di.debug_line.offset;
12081682 var this_index: usize = 0;
......@@ -1382,7 +1856,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
13821856 var this_unit_offset = st.debug_info.offset;
13831857 var cu_index: usize = 0;
13841858
1385 var in_file_stream = io.FileInStream.init(&st.self_exe_file);
1859 var in_file_stream = io.FileInStream.init(st.self_exe_file);
13861860 const in_stream = &in_file_stream.stream;
13871861
13881862 while (this_unit_offset < debug_info_end) {
......@@ -1448,7 +1922,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
14481922}
14491923
14501924fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {
1451 var in_file_stream = io.FileInStream.init(&st.self_exe_file);
1925 var in_file_stream = io.FileInStream.init(st.self_exe_file);
14521926 const in_stream = &in_file_stream.stream;
14531927 for (st.compile_unit_list.toSlice()) |*compile_unit| {
14541928 if (compile_unit.pc_range) |range| {
std/elf.zig+2-2
......@@ -353,7 +353,7 @@ pub const SectionHeader = struct {
353353};
354354
355355pub const Elf = struct {
356 in_file: *os.File,
356 in_file: os.File,
357357 auto_close_stream: bool,
358358 is_64: bool,
359359 endian: builtin.Endian,
......@@ -376,7 +376,7 @@ pub const Elf = struct {
376376 }
377377
378378 /// Call close when done.
379 pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void {
379 pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void {
380380 elf.allocator = allocator;
381381 elf.in_file = file;
382382 elf.auto_close_stream = false;
std/event/lock.zig+1-1
......@@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder;
77const Loop = std.event.Loop;
88
99/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
10/// coroutines which are waiting for the lock are suspended, and
1111/// are resumed when the lock is released, in order.
1212/// Allows only one actor to hold the lock.
1313pub const Lock = struct {
std/event/locked.zig+1-1
......@@ -3,7 +3,7 @@ const Lock = std.event.Lock;
33const Loop = std.event.Loop;
44
55/// Thread-safe async/await lock that protects one piece of data.
6/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
6/// coroutines which are waiting for the lock are suspended, and
77/// are resumed when the lock is released, in order.
88pub fn Locked(comptime T: type) type {
99 return struct {
std/event/rwlock.zig+1-1
......@@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder;
77const Loop = std.event.Loop;
88
99/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
10/// coroutines which are waiting for the lock are suspended, and
1111/// are resumed when the lock is released, in order.
1212/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
1313/// When a read lock is held, it will not be released until the reader queue is empty.
std/event/rwlocked.zig+1-1
......@@ -3,7 +3,7 @@ const RwLock = std.event.RwLock;
33const Loop = std.event.Loop;
44
55/// Thread-safe async/await RW lock that protects one piece of data.
6/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
6/// coroutines which are waiting for the lock are suspended, and
77/// are resumed when the lock is released, in order.
88pub fn RwLocked(comptime T: type) type {
99 return struct {
std/event/tcp.zig+4-4
......@@ -89,7 +89,7 @@ pub const Server = struct {
8989 error.ProcessFdQuotaExceeded => {
9090 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
9191 suspend {
92 self.waiting_for_emfile_node = PromiseNode.init( @handle() );
92 self.waiting_for_emfile_node = PromiseNode.init(@handle());
9393 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
9494 }
9595 continue;
......@@ -145,11 +145,11 @@ test "listen on a port, send bytes, receive bytes" {
145145 cancel @handle();
146146 }
147147 }
148 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
148 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: std.os.File) !void {
149149 const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
150 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
150 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/733
151151
152 var adapter = std.io.FileOutStream.init(&socket);
152 var adapter = std.io.FileOutStream.init(socket);
153153 var stream = &adapter.stream;
154154 try stream.print("hello from server\n");
155155 }
std/fmt/index.zig+126-17
......@@ -163,26 +163,47 @@ pub fn formatType(
163163 }
164164 break :cf false;
165165 };
166
167166 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
167
168168 try output(context, @typeName(T));
169 if (comptime @typeId(T) == builtin.TypeId.Enum) {
170 try output(context, ".");
171 try formatType(@tagName(value), "", context, Errors, output);
172 return;
173 }
174 comptime var field_i = 0;
175 inline while (field_i < @memberCount(T)) : (field_i += 1) {
176 if (field_i == 0) {
177 try output(context, "{ .");
178 } else {
179 try output(context, ", .");
180 }
181 try output(context, @memberName(T, field_i));
182 try output(context, " = ");
183 try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
169 switch (comptime @typeId(T)) {
170 builtin.TypeId.Enum => {
171 try output(context, ".");
172 try formatType(@tagName(value), "", context, Errors, output);
173 return;
174 },
175 builtin.TypeId.Struct => {
176 comptime var field_i = 0;
177 inline while (field_i < @memberCount(T)) : (field_i += 1) {
178 if (field_i == 0) {
179 try output(context, "{ .");
180 } else {
181 try output(context, ", .");
182 }
183 try output(context, @memberName(T, field_i));
184 try output(context, " = ");
185 try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
186 }
187 try output(context, " }");
188 },
189 builtin.TypeId.Union => {
190 const info = @typeInfo(T).Union;
191 if (info.tag_type) |UnionTagType| {
192 try output(context, "{ .");
193 try output(context, @tagName(UnionTagType(value)));
194 try output(context, " = ");
195 inline for (info.fields) |u_field| {
196 if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
197 try formatType(@field(value, u_field.name), "", context, Errors, output);
198 }
199 }
200 try output(context, " }");
201 } else {
202 try format(context, Errors, output, "@{x}", @ptrToInt(&value));
203 }
204 },
205 else => unreachable,
184206 }
185 try output(context, " }");
186207 return;
187208 },
188209 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
......@@ -329,6 +350,11 @@ pub fn formatText(
329350 comptime var width = 0;
330351 if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
331352 return formatBuf(bytes, width, context, Errors, output);
353 } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) {
354 for (bytes) |c| {
355 try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);
356 }
357 return;
332358 } else @compileError("Unknown format character: " ++ []u8{fmt[0]});
333359 }
334360 return output(context, bytes);
......@@ -1194,6 +1220,70 @@ test "fmt.format" {
11941220 try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
11951221 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
11961222 }
1223 //struct format
1224 {
1225 const S = struct {
1226 a: u32,
1227 b: error,
1228 };
1229
1230 const inst = S{
1231 .a = 456,
1232 .b = error.Unused,
1233 };
1234
1235 try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
1236 }
1237 //union format
1238 {
1239 const TU = union(enum) {
1240 float: f32,
1241 int: u32,
1242 };
1243
1244 const UU = union {
1245 float: f32,
1246 int: u32,
1247 };
1248
1249 const EU = extern union {
1250 float: f32,
1251 int: u32,
1252 };
1253
1254 const tu_inst = TU{ .int = 123 };
1255 const uu_inst = UU{ .int = 456 };
1256 const eu_inst = EU{ .float = 321.123 };
1257
1258 try testFmt("TU{ .int = 123 }", "{}", tu_inst);
1259
1260 var buf: [100]u8 = undefined;
1261 const uu_result = try bufPrint(buf[0..], "{}", uu_inst);
1262 debug.assert(mem.eql(u8, uu_result[0..3], "UU@"));
1263
1264 const eu_result = try bufPrint(buf[0..], "{}", eu_inst);
1265 debug.assert(mem.eql(u8, uu_result[0..3], "EU@"));
1266 }
1267 //enum format
1268 {
1269 const E = enum {
1270 One,
1271 Two,
1272 Three,
1273 };
1274
1275 const inst = E.Two;
1276
1277 try testFmt("E.Two", "{}", inst);
1278 }
1279 //print bytes as hex
1280 {
1281 const some_bytes = "\xCA\xFE\xBA\xBE";
1282 try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
1283 try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
1284 const bytes_with_zeros = "\x00\x0E\xBA\xBE";
1285 try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros);
1286 }
11971287}
11981288
11991289fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {
......@@ -1241,3 +1331,22 @@ pub fn isWhiteSpace(byte: u8) bool {
12411331 else => false,
12421332 };
12431333}
1334
1335pub fn hexToBytes(out: []u8, input: []const u8) !void {
1336 if (out.len * 2 < input.len)
1337 return error.InvalidLength;
1338
1339 var in_i: usize = 0;
1340 while (in_i != input.len) : (in_i += 2) {
1341 const hi = try charToDigit(input[in_i], 16);
1342 const lo = try charToDigit(input[in_i + 1], 16);
1343 out[in_i / 2] = (hi << 4) | lo;
1344 }
1345}
1346
1347test "fmt.hexToBytes" {
1348 const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706";
1349 var pb: [32]u8 = undefined;
1350 try hexToBytes(pb[0..], test_hex_str);
1351 try testFmt(test_hex_str, "{X}", pb);
1352}
std/index.zig+4
......@@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig");
1515pub const base64 = @import("base64.zig");
1616pub const build = @import("build.zig");
1717pub const c = @import("c/index.zig");
18pub const coff = @import("coff.zig");
1819pub const crypto = @import("crypto/index.zig");
1920pub const cstr = @import("cstr.zig");
2021pub const debug = @import("debug/index.zig");
......@@ -33,6 +34,7 @@ pub const math = @import("math/index.zig");
3334pub const mem = @import("mem.zig");
3435pub const net = @import("net.zig");
3536pub const os = @import("os/index.zig");
37pub const pdb = @import("pdb.zig");
3638pub const rand = @import("rand/index.zig");
3739pub const rb = @import("rb.zig");
3840pub const sort = @import("sort.zig");
......@@ -56,6 +58,7 @@ test "std" {
5658 _ = @import("base64.zig");
5759 _ = @import("build.zig");
5860 _ = @import("c/index.zig");
61 _ = @import("coff.zig");
5962 _ = @import("crypto/index.zig");
6063 _ = @import("cstr.zig");
6164 _ = @import("debug/index.zig");
......@@ -74,6 +77,7 @@ test "std" {
7477 _ = @import("heap.zig");
7578 _ = @import("os/index.zig");
7679 _ = @import("rand/index.zig");
80 _ = @import("pdb.zig");
7781 _ = @import("sort.zig");
7882 _ = @import("unicode.zig");
7983 _ = @import("zig/index.zig");
std/io.zig+8-8
......@@ -34,13 +34,13 @@ pub fn getStdIn() GetStdIoErrs!File {
3434
3535/// Implementation of InStream trait for File
3636pub const FileInStream = struct {
37 file: *File,
37 file: File,
3838 stream: Stream,
3939
4040 pub const Error = @typeOf(File.read).ReturnType.ErrorSet;
4141 pub const Stream = InStream(Error);
4242
43 pub fn init(file: *File) FileInStream {
43 pub fn init(file: File) FileInStream {
4444 return FileInStream{
4545 .file = file,
4646 .stream = Stream{ .readFn = readFn },
......@@ -55,13 +55,13 @@ pub const FileInStream = struct {
5555
5656/// Implementation of OutStream trait for File
5757pub const FileOutStream = struct {
58 file: *File,
58 file: File,
5959 stream: Stream,
6060
6161 pub const Error = File.WriteError;
6262 pub const Stream = OutStream(Error);
6363
64 pub fn init(file: *File) FileOutStream {
64 pub fn init(file: File) FileOutStream {
6565 return FileOutStream{
6666 .file = file,
6767 .stream = Stream{ .writeFn = writeFn },
......@@ -210,7 +210,7 @@ pub fn InStream(comptime ReadError: type) type {
210210
211211 pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void {
212212 // Only extern and packed structs have defined in-memory layout.
213 assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
213 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
214214 return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..]));
215215 }
216216 };
......@@ -280,7 +280,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim
280280 const buf = try allocator.alignedAlloc(u8, A, size);
281281 errdefer allocator.free(buf);
282282
283 var adapter = FileInStream.init(&file);
283 var adapter = FileInStream.init(file);
284284 try adapter.stream.readNoEof(buf[0..size]);
285285 return buf;
286286}
......@@ -592,7 +592,7 @@ pub const BufferedAtomicFile = struct {
592592 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
593593 errdefer self.atomic_file.deinit();
594594
595 self.file_stream = FileOutStream.init(&self.atomic_file.file);
595 self.file_stream = FileOutStream.init(self.atomic_file.file);
596596 self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream);
597597 return self;
598598 }
......@@ -622,7 +622,7 @@ test "import io tests" {
622622
623623pub fn readLine(buf: []u8) !usize {
624624 var stdin = getStdIn() catch return error.StdInUnavailable;
625 var adapter = FileInStream.init(&stdin);
625 var adapter = FileInStream.init(stdin);
626626 var stream = &adapter.stream;
627627 var index: usize = 0;
628628 while (true) {
std/io_test.zig+2-2
......@@ -19,7 +19,7 @@ test "write a file, read it, then delete it" {
1919 var file = try os.File.openWrite(tmp_file_name);
2020 defer file.close();
2121
22 var file_out_stream = io.FileOutStream.init(&file);
22 var file_out_stream = io.FileOutStream.init(file);
2323 var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
2424 const st = &buf_stream.stream;
2525 try st.print("begin");
......@@ -35,7 +35,7 @@ test "write a file, read it, then delete it" {
3535 const expected_file_size = "begin".len + data.len + "end".len;
3636 assert(file_size == expected_file_size);
3737
38 var file_in_stream = io.FileInStream.init(&file);
38 var file_in_stream = io.FileInStream.init(file);
3939 var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream);
4040 const st = &buf_stream.stream;
4141 const contents = try st.readAllAlloc(allocator, 2 * 1024);
std/macho.zig+552-206
......@@ -1,4 +1,3 @@
1
21pub const mach_header = extern struct {
32 magic: u32,
43 cputype: cpu_type_t,
......@@ -25,26 +24,43 @@ pub const load_command = extern struct {
2524 cmdsize: u32,
2625};
2726
28
2927/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
3028/// "stab" style symbol table information as described in the header files
3129/// <nlist.h> and <stab.h>.
3230pub const symtab_command = extern struct {
33 cmd: u32, /// LC_SYMTAB
34 cmdsize: u32, /// sizeof(struct symtab_command)
35 symoff: u32, /// symbol table offset
36 nsyms: u32, /// number of symbol table entries
37 stroff: u32, /// string table offset
38 strsize: u32, /// string table size in bytes
31 /// LC_SYMTAB
32 cmd: u32,
33
34 /// sizeof(struct symtab_command)
35 cmdsize: u32,
36
37 /// symbol table offset
38 symoff: u32,
39
40 /// number of symbol table entries
41 nsyms: u32,
42
43 /// string table offset
44 stroff: u32,
45
46 /// string table size in bytes
47 strsize: u32,
3948};
4049
4150/// The linkedit_data_command contains the offsets and sizes of a blob
42/// of data in the __LINKEDIT segment.
51/// of data in the __LINKEDIT segment.
4352const linkedit_data_command = extern struct {
44 cmd: u32,/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
45 cmdsize: u32, /// sizeof(struct linkedit_data_command)
46 dataoff: u32 , /// file offset of data in __LINKEDIT segment
47 datasize: u32 , /// file size of data in __LINKEDIT segment
53 /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
54 cmd: u32,
55
56 /// sizeof(struct linkedit_data_command)
57 cmdsize: u32,
58
59 /// file offset of data in __LINKEDIT segment
60 dataoff: u32,
61
62 /// file size of data in __LINKEDIT segment
63 datasize: u32,
4864};
4965
5066/// The segment load command indicates that a part of this file is to be
......@@ -58,16 +74,35 @@ const linkedit_data_command = extern struct {
5874/// section structures directly follow the segment command and their size is
5975/// reflected in cmdsize.
6076pub const segment_command = extern struct {
61 cmd: u32,/// LC_SEGMENT
62 cmdsize: u32,/// includes sizeof section structs
63 segname: [16]u8,/// segment name
64 vmaddr: u32,/// memory address of this segment
65 vmsize: u32,/// memory size of this segment
66 fileoff: u32,/// file offset of this segment
67 filesize: u32,/// amount to map from the file
68 maxprot: vm_prot_t,/// maximum VM protection
69 initprot: vm_prot_t,/// initial VM protection
70 nsects: u32,/// number of sections in segment
77 /// LC_SEGMENT
78 cmd: u32,
79
80 /// includes sizeof section structs
81 cmdsize: u32,
82
83 /// segment name
84 segname: [16]u8,
85
86 /// memory address of this segment
87 vmaddr: u32,
88
89 /// memory size of this segment
90 vmsize: u32,
91
92 /// file offset of this segment
93 fileoff: u32,
94
95 /// amount to map from the file
96 filesize: u32,
97
98 /// maximum VM protection
99 maxprot: vm_prot_t,
100
101 /// initial VM protection
102 initprot: vm_prot_t,
103
104 /// number of sections in segment
105 nsects: u32,
71106 flags: u32,
72107};
73108
......@@ -76,17 +111,36 @@ pub const segment_command = extern struct {
76111/// sections then section_64 structures directly follow the 64-bit segment
77112/// command and their size is reflected in cmdsize.
78113pub const segment_command_64 = extern struct {
79 cmd: u32, /// LC_SEGMENT_64
80 cmdsize: u32, /// includes sizeof section_64 structs
81 segname: [16]u8, /// segment name
82 vmaddr: u64, /// memory address of this segment
83 vmsize: u64, /// memory size of this segment
84 fileoff: u64, /// file offset of this segment
85 filesize: u64, /// amount to map from the file
86 maxprot: vm_prot_t, /// maximum VM protection
87 initprot: vm_prot_t, /// initial VM protection
88 nsects: u32, /// number of sections in segment
89 flags: u32,
114 /// LC_SEGMENT_64
115 cmd: u32,
116
117 /// includes sizeof section_64 structs
118 cmdsize: u32,
119
120 /// segment name
121 segname: [16]u8,
122
123 /// memory address of this segment
124 vmaddr: u64,
125
126 /// memory size of this segment
127 vmsize: u64,
128
129 /// file offset of this segment
130 fileoff: u64,
131
132 /// amount to map from the file
133 filesize: u64,
134
135 /// maximum VM protection
136 maxprot: vm_prot_t,
137
138 /// initial VM protection
139 initprot: vm_prot_t,
140
141 /// number of sections in segment
142 nsects: u32,
143 flags: u32,
90144};
91145
92146/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
......@@ -115,32 +169,76 @@ pub const segment_command_64 = extern struct {
115169/// fields of the section structure for mach object files is described in the
116170/// header file <reloc.h>.
117171pub const @"section" = extern struct {
118 sectname: [16]u8, /// name of this section
119 segname: [16]u8, /// segment this section goes in
120 addr: u32, /// memory address of this section
121 size: u32, /// size in bytes of this section
122 offset: u32, /// file offset of this section
123 @"align": u32, /// section alignment (power of 2)
124 reloff: u32, /// file offset of relocation entries
125 nreloc: u32, /// number of relocation entries
126 flags: u32, /// flags (section type and attributes
127 reserved1: u32, /// reserved (for offset or index)
128 reserved2: u32, /// reserved (for count or sizeof)
172 /// name of this section
173 sectname: [16]u8,
174
175 /// segment this section goes in
176 segname: [16]u8,
177
178 /// memory address of this section
179 addr: u32,
180
181 /// size in bytes of this section
182 size: u32,
183
184 /// file offset of this section
185 offset: u32,
186
187 /// section alignment (power of 2)
188 @"align": u32,
189
190 /// file offset of relocation entries
191 reloff: u32,
192
193 /// number of relocation entries
194 nreloc: u32,
195
196 /// flags (section type and attributes
197 flags: u32,
198
199 /// reserved (for offset or index)
200 reserved1: u32,
201
202 /// reserved (for count or sizeof)
203 reserved2: u32,
129204};
130205
131206pub const section_64 = extern struct {
132 sectname: [16]u8, /// name of this section
133 segname: [16]u8, /// segment this section goes in
134 addr: u64, /// memory address of this section
135 size: u64, /// size in bytes of this section
136 offset: u32, /// file offset of this section
137 @"align": u32, /// section alignment (power of 2)
138 reloff: u32, /// file offset of relocation entries
139 nreloc: u32, /// number of relocation entries
140 flags: u32, /// flags (section type and attributes
141 reserved1: u32, /// reserved (for offset or index)
142 reserved2: u32, /// reserved (for count or sizeof)
143 reserved3: u32, /// reserved
207 /// name of this section
208 sectname: [16]u8,
209
210 /// segment this section goes in
211 segname: [16]u8,
212
213 /// memory address of this section
214 addr: u64,
215
216 /// size in bytes of this section
217 size: u64,
218
219 /// file offset of this section
220 offset: u32,
221
222 /// section alignment (power of 2)
223 @"align": u32,
224
225 /// file offset of relocation entries
226 reloff: u32,
227
228 /// number of relocation entries
229 nreloc: u32,
230
231 /// flags (section type and attributes
232 flags: u32,
233
234 /// reserved (for offset or index)
235 reserved1: u32,
236
237 /// reserved (for count or sizeof)
238 reserved2: u32,
239
240 /// reserved
241 reserved3: u32,
144242};
145243
146244pub const nlist = extern struct {
......@@ -168,116 +266,287 @@ pub const nlist_64 = extern struct {
168266/// simply be ignored.
169267pub const LC_REQ_DYLD = 0x80000000;
170268
171pub const LC_SEGMENT = 0x1; /// segment of this file to be mapped
172pub const LC_SYMTAB = 0x2; /// link-edit stab symbol table info
173pub const LC_SYMSEG = 0x3; /// link-edit gdb symbol table info (obsolete)
174pub const LC_THREAD = 0x4; /// thread
175pub const LC_UNIXTHREAD = 0x5; /// unix thread (includes a stack)
176pub const LC_LOADFVMLIB = 0x6; /// load a specified fixed VM shared library
177pub const LC_IDFVMLIB = 0x7; /// fixed VM shared library identification
178pub const LC_IDENT = 0x8; /// object identification info (obsolete)
179pub const LC_FVMFILE = 0x9; /// fixed VM file inclusion (internal use)
180pub const LC_PREPAGE = 0xa; /// prepage command (internal use)
181pub const LC_DYSYMTAB = 0xb; /// dynamic link-edit symbol table info
182pub const LC_LOAD_DYLIB = 0xc; /// load a dynamically linked shared library
183pub const LC_ID_DYLIB = 0xd; /// dynamically linked shared lib ident
184pub const LC_LOAD_DYLINKER = 0xe; /// load a dynamic linker
185pub const LC_ID_DYLINKER = 0xf; /// dynamic linker identification
186pub const LC_PREBOUND_DYLIB = 0x10; /// modules prebound for a dynamically
187pub const LC_ROUTINES = 0x11; /// image routines
188pub const LC_SUB_FRAMEWORK = 0x12; /// sub framework
189pub const LC_SUB_UMBRELLA = 0x13; /// sub umbrella
190pub const LC_SUB_CLIENT = 0x14; /// sub client
191pub const LC_SUB_LIBRARY = 0x15; /// sub library
192pub const LC_TWOLEVEL_HINTS = 0x16; /// two-level namespace lookup hints
193pub const LC_PREBIND_CKSUM = 0x17; /// prebind checksum
269/// segment of this file to be mapped
270pub const LC_SEGMENT = 0x1;
271
272/// link-edit stab symbol table info
273pub const LC_SYMTAB = 0x2;
274
275/// link-edit gdb symbol table info (obsolete)
276pub const LC_SYMSEG = 0x3;
277
278/// thread
279pub const LC_THREAD = 0x4;
280
281/// unix thread (includes a stack)
282pub const LC_UNIXTHREAD = 0x5;
283
284/// load a specified fixed VM shared library
285pub const LC_LOADFVMLIB = 0x6;
286
287/// fixed VM shared library identification
288pub const LC_IDFVMLIB = 0x7;
289
290/// object identification info (obsolete)
291pub const LC_IDENT = 0x8;
292
293/// fixed VM file inclusion (internal use)
294pub const LC_FVMFILE = 0x9;
295
296/// prepage command (internal use)
297pub const LC_PREPAGE = 0xa;
298
299/// dynamic link-edit symbol table info
300pub const LC_DYSYMTAB = 0xb;
301
302/// load a dynamically linked shared library
303pub const LC_LOAD_DYLIB = 0xc;
304
305/// dynamically linked shared lib ident
306pub const LC_ID_DYLIB = 0xd;
307
308/// load a dynamic linker
309pub const LC_LOAD_DYLINKER = 0xe;
310
311/// dynamic linker identification
312pub const LC_ID_DYLINKER = 0xf;
313
314/// modules prebound for a dynamically
315pub const LC_PREBOUND_DYLIB = 0x10;
316
317/// image routines
318pub const LC_ROUTINES = 0x11;
319
320/// sub framework
321pub const LC_SUB_FRAMEWORK = 0x12;
322
323/// sub umbrella
324pub const LC_SUB_UMBRELLA = 0x13;
325
326/// sub client
327pub const LC_SUB_CLIENT = 0x14;
328
329/// sub library
330pub const LC_SUB_LIBRARY = 0x15;
331
332/// two-level namespace lookup hints
333pub const LC_TWOLEVEL_HINTS = 0x16;
334
335/// prebind checksum
336pub const LC_PREBIND_CKSUM = 0x17;
194337
195338/// load a dynamically linked shared library that is allowed to be missing
196339/// (all symbols are weak imported).
197340pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
198341
199pub const LC_SEGMENT_64 = 0x19; /// 64-bit segment of this file to be mapped
200pub const LC_ROUTINES_64 = 0x1a; /// 64-bit image routines
201pub const LC_UUID = 0x1b; /// the uuid
202pub const LC_RPATH = (0x1c | LC_REQ_DYLD); /// runpath additions
203pub const LC_CODE_SIGNATURE = 0x1d; /// local of code signature
204pub const LC_SEGMENT_SPLIT_INFO = 0x1e; /// local of info to split segments
205pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD); /// load and re-export dylib
206pub const LC_LAZY_LOAD_DYLIB = 0x20; /// delay load of dylib until first use
207pub const LC_ENCRYPTION_INFO = 0x21; /// encrypted segment information
208pub const LC_DYLD_INFO = 0x22; /// compressed dyld information
209pub const LC_DYLD_INFO_ONLY = (0x22|LC_REQ_DYLD); /// compressed dyld information only
210pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD); /// load upward dylib
211pub const LC_VERSION_MIN_MACOSX = 0x24; /// build for MacOSX min OS version
212pub const LC_VERSION_MIN_IPHONEOS = 0x25; /// build for iPhoneOS min OS version
213pub const LC_FUNCTION_STARTS = 0x26; /// compressed table of function start addresses
214pub const LC_DYLD_ENVIRONMENT = 0x27; /// string for dyld to treat like environment variable
215pub const LC_MAIN = (0x28|LC_REQ_DYLD); /// replacement for LC_UNIXTHREAD
216pub const LC_DATA_IN_CODE = 0x29; /// table of non-instructions in __text
217pub const LC_SOURCE_VERSION = 0x2A; /// source version used to build binary
218pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B; /// Code signing DRs copied from linked dylibs
219pub const LC_ENCRYPTION_INFO_64 = 0x2C; /// 64-bit encrypted segment information
220pub const LC_LINKER_OPTION = 0x2D; /// linker options in MH_OBJECT files
221pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E; /// optimization hints in MH_OBJECT files
222pub const LC_VERSION_MIN_TVOS = 0x2F; /// build for AppleTV min OS version
223pub const LC_VERSION_MIN_WATCHOS = 0x30; /// build for Watch min OS version
224pub const LC_NOTE = 0x31; /// arbitrary data included within a Mach-O file
225pub const LC_BUILD_VERSION = 0x32; /// build for platform min OS version
226
227pub const MH_MAGIC = 0xfeedface; /// the mach magic number
228pub const MH_CIGAM = 0xcefaedfe; /// NXSwapInt(MH_MAGIC)
229
230pub const MH_MAGIC_64 = 0xfeedfacf; /// the 64-bit mach magic number
231pub const MH_CIGAM_64 = 0xcffaedfe; /// NXSwapInt(MH_MAGIC_64)
232
233pub const MH_OBJECT = 0x1; /// relocatable object file
234pub const MH_EXECUTE = 0x2; /// demand paged executable file
235pub const MH_FVMLIB = 0x3; /// fixed VM shared library file
236pub const MH_CORE = 0x4; /// core file
237pub const MH_PRELOAD = 0x5; /// preloaded executable file
238pub const MH_DYLIB = 0x6; /// dynamically bound shared library
239pub const MH_DYLINKER = 0x7; /// dynamic link editor
240pub const MH_BUNDLE = 0x8; /// dynamically bound bundle file
241pub const MH_DYLIB_STUB = 0x9; /// shared library stub for static linking only, no section contents
242pub const MH_DSYM = 0xa; /// companion file with only debug sections
243pub const MH_KEXT_BUNDLE = 0xb; /// x86_64 kexts
342/// 64-bit segment of this file to be mapped
343pub const LC_SEGMENT_64 = 0x19;
344
345/// 64-bit image routines
346pub const LC_ROUTINES_64 = 0x1a;
347
348/// the uuid
349pub const LC_UUID = 0x1b;
350
351/// runpath additions
352pub const LC_RPATH = (0x1c | LC_REQ_DYLD);
353
354/// local of code signature
355pub const LC_CODE_SIGNATURE = 0x1d;
356
357/// local of info to split segments
358pub const LC_SEGMENT_SPLIT_INFO = 0x1e;
359
360/// load and re-export dylib
361pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD);
362
363/// delay load of dylib until first use
364pub const LC_LAZY_LOAD_DYLIB = 0x20;
365
366/// encrypted segment information
367pub const LC_ENCRYPTION_INFO = 0x21;
368
369/// compressed dyld information
370pub const LC_DYLD_INFO = 0x22;
371
372/// compressed dyld information only
373pub const LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD);
374
375/// load upward dylib
376pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD);
377
378/// build for MacOSX min OS version
379pub const LC_VERSION_MIN_MACOSX = 0x24;
380
381/// build for iPhoneOS min OS version
382pub const LC_VERSION_MIN_IPHONEOS = 0x25;
383
384/// compressed table of function start addresses
385pub const LC_FUNCTION_STARTS = 0x26;
386
387/// string for dyld to treat like environment variable
388pub const LC_DYLD_ENVIRONMENT = 0x27;
389
390/// replacement for LC_UNIXTHREAD
391pub const LC_MAIN = (0x28 | LC_REQ_DYLD);
392
393/// table of non-instructions in __text
394pub const LC_DATA_IN_CODE = 0x29;
395
396/// source version used to build binary
397pub const LC_SOURCE_VERSION = 0x2A;
398
399/// Code signing DRs copied from linked dylibs
400pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B;
401
402/// 64-bit encrypted segment information
403pub const LC_ENCRYPTION_INFO_64 = 0x2C;
404
405/// linker options in MH_OBJECT files
406pub const LC_LINKER_OPTION = 0x2D;
407
408/// optimization hints in MH_OBJECT files
409pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E;
410
411/// build for AppleTV min OS version
412pub const LC_VERSION_MIN_TVOS = 0x2F;
413
414/// build for Watch min OS version
415pub const LC_VERSION_MIN_WATCHOS = 0x30;
416
417/// arbitrary data included within a Mach-O file
418pub const LC_NOTE = 0x31;
419
420/// build for platform min OS version
421pub const LC_BUILD_VERSION = 0x32;
422
423/// the mach magic number
424pub const MH_MAGIC = 0xfeedface;
425
426/// NXSwapInt(MH_MAGIC)
427pub const MH_CIGAM = 0xcefaedfe;
428
429/// the 64-bit mach magic number
430pub const MH_MAGIC_64 = 0xfeedfacf;
431
432/// NXSwapInt(MH_MAGIC_64)
433pub const MH_CIGAM_64 = 0xcffaedfe;
434
435/// relocatable object file
436pub const MH_OBJECT = 0x1;
437
438/// demand paged executable file
439pub const MH_EXECUTE = 0x2;
440
441/// fixed VM shared library file
442pub const MH_FVMLIB = 0x3;
443
444/// core file
445pub const MH_CORE = 0x4;
446
447/// preloaded executable file
448pub const MH_PRELOAD = 0x5;
449
450/// dynamically bound shared library
451pub const MH_DYLIB = 0x6;
452
453/// dynamic link editor
454pub const MH_DYLINKER = 0x7;
455
456/// dynamically bound bundle file
457pub const MH_BUNDLE = 0x8;
458
459/// shared library stub for static linking only, no section contents
460pub const MH_DYLIB_STUB = 0x9;
461
462/// companion file with only debug sections
463pub const MH_DSYM = 0xa;
464
465/// x86_64 kexts
466pub const MH_KEXT_BUNDLE = 0xb;
244467
245468// Constants for the flags field of the mach_header
246469
247pub const MH_NOUNDEFS = 0x1; /// the object file has no undefined references
248pub const MH_INCRLINK = 0x2; /// the object file is the output of an incremental link against a base file and can't be link edited again
249pub const MH_DYLDLINK = 0x4; /// the object file is input for the dynamic linker and can't be staticly link edited again
250pub const MH_BINDATLOAD = 0x8; /// the object file's undefined references are bound by the dynamic linker when loaded.
251pub const MH_PREBOUND = 0x10; /// the file has its dynamic undefined references prebound.
252pub const MH_SPLIT_SEGS = 0x20; /// the file has its read-only and read-write segments split
253pub const MH_LAZY_INIT = 0x40; /// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)
254pub const MH_TWOLEVEL = 0x80; /// the image is using two-level name space bindings
255pub const MH_FORCE_FLAT = 0x100; /// the executable is forcing all images to use flat name space bindings
256pub const MH_NOMULTIDEFS = 0x200; /// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used.
257pub const MH_NOFIXPREBINDING = 0x400; /// do not have dyld notify the prebinding agent about this executable
258pub const MH_PREBINDABLE = 0x800; /// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
259pub const MH_ALLMODSBOUND = 0x1000; /// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
260pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;/// safe to divide up the sections into sub-sections via symbols for dead code stripping
261pub const MH_CANONICAL = 0x4000; /// the binary has been canonicalized via the unprebind operation
262pub const MH_WEAK_DEFINES = 0x8000; /// the final linked image contains external weak symbols
263pub const MH_BINDS_TO_WEAK = 0x10000; /// the final linked image uses weak symbols
264
265pub const MH_ALLOW_STACK_EXECUTION = 0x20000;/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes.
266pub const MH_ROOT_SAFE = 0x40000; /// When this bit is set, the binary declares it is safe for use in processes with uid zero
267
268pub const MH_SETUID_SAFE = 0x80000; /// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
269
270pub const MH_NO_REEXPORTED_DYLIBS = 0x100000; /// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported
271pub const MH_PIE = 0x200000; /// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes.
272pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000; /// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib.
273pub const MH_HAS_TLV_DESCRIPTORS = 0x800000; /// Contains a section of type S_THREAD_LOCAL_VARIABLES
274
275pub const MH_NO_HEAP_EXECUTION = 0x1000000; /// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
276
277pub const MH_APP_EXTENSION_SAFE = 0x02000000; /// The code was linked for use in an application extension.
278
279pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info.
470/// the object file has no undefined references
471pub const MH_NOUNDEFS = 0x1;
472
473/// the object file is the output of an incremental link against a base file and can't be link edited again
474pub const MH_INCRLINK = 0x2;
475
476/// the object file is input for the dynamic linker and can't be staticly link edited again
477pub const MH_DYLDLINK = 0x4;
478
479/// the object file's undefined references are bound by the dynamic linker when loaded.
480pub const MH_BINDATLOAD = 0x8;
481
482/// the file has its dynamic undefined references prebound.
483pub const MH_PREBOUND = 0x10;
484
485/// the file has its read-only and read-write segments split
486pub const MH_SPLIT_SEGS = 0x20;
487
488/// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)
489pub const MH_LAZY_INIT = 0x40;
490
491/// the image is using two-level name space bindings
492pub const MH_TWOLEVEL = 0x80;
493
494/// the executable is forcing all images to use flat name space bindings
495pub const MH_FORCE_FLAT = 0x100;
496
497/// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used.
498pub const MH_NOMULTIDEFS = 0x200;
499
500/// do not have dyld notify the prebinding agent about this executable
501pub const MH_NOFIXPREBINDING = 0x400;
502
503/// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
504pub const MH_PREBINDABLE = 0x800;
505
506/// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
507pub const MH_ALLMODSBOUND = 0x1000;
508
509/// safe to divide up the sections into sub-sections via symbols for dead code stripping
510pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;
511
512/// the binary has been canonicalized via the unprebind operation
513pub const MH_CANONICAL = 0x4000;
514
515/// the final linked image contains external weak symbols
516pub const MH_WEAK_DEFINES = 0x8000;
517
518/// the final linked image uses weak symbols
519pub const MH_BINDS_TO_WEAK = 0x10000;
520
521/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes.
522pub const MH_ALLOW_STACK_EXECUTION = 0x20000;
523
524/// When this bit is set, the binary declares it is safe for use in processes with uid zero
525pub const MH_ROOT_SAFE = 0x40000;
526
527/// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
528pub const MH_SETUID_SAFE = 0x80000;
529
530/// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported
531pub const MH_NO_REEXPORTED_DYLIBS = 0x100000;
280532
533/// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes.
534pub const MH_PIE = 0x200000;
535
536/// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib.
537pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000;
538
539/// Contains a section of type S_THREAD_LOCAL_VARIABLES
540pub const MH_HAS_TLV_DESCRIPTORS = 0x800000;
541
542/// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
543pub const MH_NO_HEAP_EXECUTION = 0x1000000;
544
545/// The code was linked for use in an application extension.
546pub const MH_APP_EXTENSION_SAFE = 0x02000000;
547
548/// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info.
549pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000;
281550
282551/// The flags field of a section structure is separated into two parts a section
283552/// type and section attributes. The section types are mutually exclusive (it
......@@ -285,52 +554,129 @@ pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbol
285554/// than one attribute).
286555/// 256 section types
287556pub const SECTION_TYPE = 0x000000ff;
288pub const SECTION_ATTRIBUTES = 0xffffff00; /// 24 section attributes
289
290pub const S_REGULAR = 0x0; /// regular section
291pub const S_ZEROFILL = 0x1; /// zero fill on demand section
292pub const S_CSTRING_LITERALS = 0x2; /// section with only literal C string
293pub const S_4BYTE_LITERALS = 0x3; /// section with only 4 byte literals
294pub const S_8BYTE_LITERALS = 0x4; /// section with only 8 byte literals
295pub const S_LITERAL_POINTERS = 0x5; /// section with only pointers to
296
297
298pub const N_STAB = 0xe0; /// if any of these bits set, a symbolic debugging entry
299pub const N_PEXT = 0x10; /// private external symbol bit
300pub const N_TYPE = 0x0e; /// mask for the type bits
301pub const N_EXT = 0x01; /// external symbol bit, set for external symbols
302
303
304pub const N_GSYM = 0x20; /// global symbol: name,,NO_SECT,type,0
305pub const N_FNAME = 0x22; /// procedure name (f77 kludge): name,,NO_SECT,0,0
306pub const N_FUN = 0x24; /// procedure: name,,n_sect,linenumber,address
307pub const N_STSYM = 0x26; /// static symbol: name,,n_sect,type,address
308pub const N_LCSYM = 0x28; /// .lcomm symbol: name,,n_sect,type,address
309pub const N_BNSYM = 0x2e; /// begin nsect sym: 0,,n_sect,0,address
310pub const N_AST = 0x32; /// AST file path: name,,NO_SECT,0,0
311pub const N_OPT = 0x3c; /// emitted with gcc2_compiled and in gcc source
312pub const N_RSYM = 0x40; /// register sym: name,,NO_SECT,type,register
313pub const N_SLINE = 0x44; /// src line: 0,,n_sect,linenumber,address
314pub const N_ENSYM = 0x4e; /// end nsect sym: 0,,n_sect,0,address
315pub const N_SSYM = 0x60; /// structure elt: name,,NO_SECT,type,struct_offset
316pub const N_SO = 0x64; /// source file name: name,,n_sect,0,address
317pub const N_OSO = 0x66; /// object file name: name,,0,0,st_mtime
318pub const N_LSYM = 0x80; /// local sym: name,,NO_SECT,type,offset
319pub const N_BINCL = 0x82; /// include file beginning: name,,NO_SECT,0,sum
320pub const N_SOL = 0x84; /// #included file name: name,,n_sect,0,address
321pub const N_PARAMS = 0x86; /// compiler parameters: name,,NO_SECT,0,0
322pub const N_VERSION = 0x88; /// compiler version: name,,NO_SECT,0,0
323pub const N_OLEVEL = 0x8A; /// compiler -O level: name,,NO_SECT,0,0
324pub const N_PSYM = 0xa0; /// parameter: name,,NO_SECT,type,offset
325pub const N_EINCL = 0xa2; /// include file end: name,,NO_SECT,0,0
326pub const N_ENTRY = 0xa4; /// alternate entry: name,,n_sect,linenumber,address
327pub const N_LBRAC = 0xc0; /// left bracket: 0,,NO_SECT,nesting level,address
328pub const N_EXCL = 0xc2; /// deleted include file: name,,NO_SECT,0,sum
329pub const N_RBRAC = 0xe0; /// right bracket: 0,,NO_SECT,nesting level,address
330pub const N_BCOMM = 0xe2; /// begin common: name,,NO_SECT,0,0
331pub const N_ECOMM = 0xe4; /// end common: name,,n_sect,0,0
332pub const N_ECOML = 0xe8; /// end common (local name): 0,,n_sect,0,address
333pub const N_LENG = 0xfe; /// second stab entry with length information
557
558/// 24 section attributes
559pub const SECTION_ATTRIBUTES = 0xffffff00;
560
561/// regular section
562pub const S_REGULAR = 0x0;
563
564/// zero fill on demand section
565pub const S_ZEROFILL = 0x1;
566
567/// section with only literal C string
568pub const S_CSTRING_LITERALS = 0x2;
569
570/// section with only 4 byte literals
571pub const S_4BYTE_LITERALS = 0x3;
572
573/// section with only 8 byte literals
574pub const S_8BYTE_LITERALS = 0x4;
575
576/// section with only pointers to
577pub const S_LITERAL_POINTERS = 0x5;
578
579/// if any of these bits set, a symbolic debugging entry
580pub const N_STAB = 0xe0;
581
582/// private external symbol bit
583pub const N_PEXT = 0x10;
584
585/// mask for the type bits
586pub const N_TYPE = 0x0e;
587
588/// external symbol bit, set for external symbols
589pub const N_EXT = 0x01;
590
591/// global symbol: name,,NO_SECT,type,0
592pub const N_GSYM = 0x20;
593
594/// procedure name (f77 kludge): name,,NO_SECT,0,0
595pub const N_FNAME = 0x22;
596
597/// procedure: name,,n_sect,linenumber,address
598pub const N_FUN = 0x24;
599
600/// static symbol: name,,n_sect,type,address
601pub const N_STSYM = 0x26;
602
603/// .lcomm symbol: name,,n_sect,type,address
604pub const N_LCSYM = 0x28;
605
606/// begin nsect sym: 0,,n_sect,0,address
607pub const N_BNSYM = 0x2e;
608
609/// AST file path: name,,NO_SECT,0,0
610pub const N_AST = 0x32;
611
612/// emitted with gcc2_compiled and in gcc source
613pub const N_OPT = 0x3c;
614
615/// register sym: name,,NO_SECT,type,register
616pub const N_RSYM = 0x40;
617
618/// src line: 0,,n_sect,linenumber,address
619pub const N_SLINE = 0x44;
620
621/// end nsect sym: 0,,n_sect,0,address
622pub const N_ENSYM = 0x4e;
623
624/// structure elt: name,,NO_SECT,type,struct_offset
625pub const N_SSYM = 0x60;
626
627/// source file name: name,,n_sect,0,address
628pub const N_SO = 0x64;
629
630/// object file name: name,,0,0,st_mtime
631pub const N_OSO = 0x66;
632
633/// local sym: name,,NO_SECT,type,offset
634pub const N_LSYM = 0x80;
635
636/// include file beginning: name,,NO_SECT,0,sum
637pub const N_BINCL = 0x82;
638
639/// #included file name: name,,n_sect,0,address
640pub const N_SOL = 0x84;
641
642/// compiler parameters: name,,NO_SECT,0,0
643pub const N_PARAMS = 0x86;
644
645/// compiler version: name,,NO_SECT,0,0
646pub const N_VERSION = 0x88;
647
648/// compiler -O level: name,,NO_SECT,0,0
649pub const N_OLEVEL = 0x8A;
650
651/// parameter: name,,NO_SECT,type,offset
652pub const N_PSYM = 0xa0;
653
654/// include file end: name,,NO_SECT,0,0
655pub const N_EINCL = 0xa2;
656
657/// alternate entry: name,,n_sect,linenumber,address
658pub const N_ENTRY = 0xa4;
659
660/// left bracket: 0,,NO_SECT,nesting level,address
661pub const N_LBRAC = 0xc0;
662
663/// deleted include file: name,,NO_SECT,0,sum
664pub const N_EXCL = 0xc2;
665
666/// right bracket: 0,,NO_SECT,nesting level,address
667pub const N_RBRAC = 0xe0;
668
669/// begin common: name,,NO_SECT,0,0
670pub const N_BCOMM = 0xe2;
671
672/// end common: name,,n_sect,0,0
673pub const N_ECOMM = 0xe4;
674
675/// end common (local name): 0,,n_sect,0,address
676pub const N_ECOML = 0xe8;
677
678/// second stab entry with length information
679pub const N_LENG = 0xfe;
334680
335681/// If a segment contains any sections marked with S_ATTR_DEBUG then all
336682/// sections in that segment must have this attribute. No section other than
......@@ -339,10 +685,10 @@ pub const N_LENG = 0xfe; /// second stab entry with length information
339685/// a section type S_REGULAR. The static linker will not copy section contents
340686/// from sections with this attribute into its output file. These sections
341687/// generally contain DWARF debugging info.
342pub const S_ATTR_DEBUG = 0x02000000; /// a debug section
688/// a debug section
689pub const S_ATTR_DEBUG = 0x02000000;
343690
344691pub const cpu_type_t = integer_t;
345692pub const cpu_subtype_t = integer_t;
346693pub const integer_t = c_int;
347694pub const vm_prot_t = c_int;
348
std/os/child_process.zig+48-14
......@@ -1,5 +1,6 @@
11const std = @import("../index.zig");
22const cstr = std.cstr;
3const unicode = std.unicode;
34const io = std.io;
45const os = std.os;
56const posix = os.posix;
......@@ -12,6 +13,7 @@ const Buffer = std.Buffer;
1213const builtin = @import("builtin");
1314const Os = builtin.Os;
1415const LinkedList = std.LinkedList;
16const windows_util = @import("windows/util.zig");
1517
1618const is_windows = builtin.os == Os.windows;
1719
......@@ -209,8 +211,8 @@ pub const ChildProcess = struct {
209211 defer Buffer.deinit(&stdout);
210212 defer Buffer.deinit(&stderr);
211213
212 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
213 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
214 var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
215 var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
214216
215217 try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
216218 try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
......@@ -520,8 +522,8 @@ pub const ChildProcess = struct {
520522 const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
521523 defer self.allocator.free(cmd_line);
522524
523 var siStartInfo = windows.STARTUPINFOA{
524 .cb = @sizeOf(windows.STARTUPINFOA),
525 var siStartInfo = windows.STARTUPINFOW{
526 .cb = @sizeOf(windows.STARTUPINFOW),
525527 .hStdError = g_hChildStd_ERR_Wr,
526528 .hStdOutput = g_hChildStd_OUT_Wr,
527529 .hStdInput = g_hChildStd_IN_Rd,
......@@ -545,7 +547,9 @@ pub const ChildProcess = struct {
545547
546548 const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
547549 defer if (cwd_slice) |cwd| self.allocator.free(cwd);
548 const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
550 const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null;
551 defer if (cwd_w) |cwd| self.allocator.free(cwd);
552 const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
549553
550554 const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
551555 defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
......@@ -564,7 +568,13 @@ pub const ChildProcess = struct {
564568 };
565569 defer self.allocator.free(app_name);
566570
567 windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
571 const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
572 defer self.allocator.free(app_name_w);
573
574 const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
575 defer self.allocator.free(cmd_line_w);
576
577 windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
568578 if (no_path_err != error.FileNotFound) return no_path_err;
569579
570580 const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
......@@ -575,7 +585,10 @@ pub const ChildProcess = struct {
575585 const joined_path = try os.path.join(self.allocator, search_path, app_name);
576586 defer self.allocator.free(joined_path);
577587
578 if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| {
588 const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
589 defer self.allocator.free(joined_path_w);
590
591 if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
579592 break;
580593 } else |err| if (err == error.FileNotFound) {
581594 continue;
......@@ -626,15 +639,36 @@ pub const ChildProcess = struct {
626639 }
627640};
628641
629fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
630 if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) {
642fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
643 // TODO the docs for environment pointer say:
644 // > A pointer to the environment block for the new process. If this parameter
645 // > is NULL, the new process uses the environment of the calling process.
646 // > ...
647 // > An environment block can contain either Unicode or ANSI characters. If
648 // > the environment block pointed to by lpEnvironment contains Unicode
649 // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
650 // > If this parameter is NULL and the environment block of the parent process
651 // > contains Unicode characters, you must also ensure that dwCreationFlags
652 // > includes CREATE_UNICODE_ENVIRONMENT.
653 // This seems to imply that we have to somehow know whether our process parent passed
654 // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
655 // Since we do not know this information that would imply that we must not pass NULL
656 // for the parameter.
657 // However this would imply that programs compiled with -DUNICODE could not pass
658 // environment variables to programs that were not, which seems unlikely.
659 // More investigation is needed.
660 if (windows.CreateProcessW(
661 app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT,
662 @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation,
663 ) == 0) {
631664 const err = windows.GetLastError();
632 return switch (err) {
633 windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
665 switch (err) {
666 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
667 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
634668 windows.ERROR.INVALID_PARAMETER => unreachable,
635 windows.ERROR.INVALID_NAME => error.InvalidName,
636 else => os.unexpectedErrorWindows(err),
637 };
669 windows.ERROR.INVALID_NAME => return error.InvalidName,
670 else => return os.unexpectedErrorWindows(err),
671 }
638672 }
639673}
640674
std/os/file.zig+49-34
......@@ -48,18 +48,23 @@ pub const File = struct {
4848 return openReadC(&path_c);
4949 }
5050 if (is_windows) {
51 const handle = try os.windowsOpen(
52 path,
53 windows.GENERIC_READ,
54 windows.FILE_SHARE_READ,
55 windows.OPEN_EXISTING,
56 windows.FILE_ATTRIBUTE_NORMAL,
57 );
58 return openHandle(handle);
51 const path_w = try windows_util.sliceToPrefixedFileW(path);
52 return openReadW(&path_w);
5953 }
6054 @compileError("Unsupported OS");
6155 }
6256
57 pub fn openReadW(path_w: [*]const u16) OpenError!File {
58 const handle = try os.windowsOpenW(
59 path_w,
60 windows.GENERIC_READ,
61 windows.FILE_SHARE_READ,
62 windows.OPEN_EXISTING,
63 windows.FILE_ATTRIBUTE_NORMAL,
64 );
65 return openHandle(handle);
66 }
67
6368 /// Calls `openWriteMode` with os.File.default_mode for the mode.
6469 pub fn openWrite(path: []const u8) OpenError!File {
6570 return openWriteMode(path, os.File.default_mode);
......@@ -74,19 +79,24 @@ pub const File = struct {
7479 const fd = try os.posixOpen(path, flags, file_mode);
7580 return openHandle(fd);
7681 } else if (is_windows) {
77 const handle = try os.windowsOpen(
78 path,
79 windows.GENERIC_WRITE,
80 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
81 windows.CREATE_ALWAYS,
82 windows.FILE_ATTRIBUTE_NORMAL,
83 );
84 return openHandle(handle);
82 const path_w = try windows_util.sliceToPrefixedFileW(path);
83 return openWriteModeW(&path_w, file_mode);
8584 } else {
8685 @compileError("TODO implement openWriteMode for this OS");
8786 }
8887 }
8988
89 pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
90 const handle = try os.windowsOpenW(
91 path_w,
92 windows.GENERIC_WRITE,
93 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
94 windows.CREATE_ALWAYS,
95 windows.FILE_ATTRIBUTE_NORMAL,
96 );
97 return openHandle(handle);
98 }
99
90100 /// If the path does not exist it will be created.
91101 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
92102 /// Call close to clean up.
......@@ -96,19 +106,24 @@ pub const File = struct {
96106 const fd = try os.posixOpen(path, flags, file_mode);
97107 return openHandle(fd);
98108 } else if (is_windows) {
99 const handle = try os.windowsOpen(
100 path,
101 windows.GENERIC_WRITE,
102 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
103 windows.CREATE_NEW,
104 windows.FILE_ATTRIBUTE_NORMAL,
105 );
106 return openHandle(handle);
109 const path_w = try windows_util.sliceToPrefixedFileW(path);
110 return openWriteNoClobberW(&path_w, file_mode);
107111 } else {
108112 @compileError("TODO implement openWriteMode for this OS");
109113 }
110114 }
111115
116 pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
117 const handle = try os.windowsOpenW(
118 path_w,
119 windows.GENERIC_WRITE,
120 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
121 windows.CREATE_NEW,
122 windows.FILE_ATTRIBUTE_NORMAL,
123 );
124 return openHandle(handle);
125 }
126
112127 pub fn openHandle(handle: os.FileHandle) File {
113128 return File{ .handle = handle };
114129 }
......@@ -190,17 +205,16 @@ pub const File = struct {
190205
191206 /// Upon success, the stream is in an uninitialized state. To continue using it,
192207 /// you must use the open() function.
193 pub fn close(self: *File) void {
208 pub fn close(self: File) void {
194209 os.close(self.handle);
195 self.handle = undefined;
196210 }
197211
198212 /// Calls `os.isTty` on `self.handle`.
199 pub fn isTty(self: *File) bool {
213 pub fn isTty(self: File) bool {
200214 return os.isTty(self.handle);
201215 }
202216
203 pub fn seekForward(self: *File, amount: isize) !void {
217 pub fn seekForward(self: File, amount: isize) !void {
204218 switch (builtin.os) {
205219 Os.linux, Os.macosx, Os.ios => {
206220 const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
......@@ -231,7 +245,7 @@ pub const File = struct {
231245 }
232246 }
233247
234 pub fn seekTo(self: *File, pos: usize) !void {
248 pub fn seekTo(self: File, pos: usize) !void {
235249 switch (builtin.os) {
236250 Os.linux, Os.macosx, Os.ios => {
237251 const ipos = try math.cast(isize, pos);
......@@ -256,6 +270,7 @@ pub const File = struct {
256270 const err = windows.GetLastError();
257271 return switch (err) {
258272 windows.ERROR.INVALID_PARAMETER => unreachable,
273 windows.ERROR.INVALID_HANDLE => unreachable,
259274 else => os.unexpectedErrorWindows(err),
260275 };
261276 }
......@@ -264,7 +279,7 @@ pub const File = struct {
264279 }
265280 }
266281
267 pub fn getPos(self: *File) !usize {
282 pub fn getPos(self: File) !usize {
268283 switch (builtin.os) {
269284 Os.linux, Os.macosx, Os.ios => {
270285 const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
......@@ -300,7 +315,7 @@ pub const File = struct {
300315 }
301316 }
302317
303 pub fn getEndPos(self: *File) !usize {
318 pub fn getEndPos(self: File) !usize {
304319 if (is_posix) {
305320 const stat = try os.posixFStat(self.handle);
306321 return @intCast(usize, stat.size);
......@@ -325,7 +340,7 @@ pub const File = struct {
325340 Unexpected,
326341 };
327342
328 pub fn mode(self: *File) ModeError!Mode {
343 pub fn mode(self: File) ModeError!Mode {
329344 if (is_posix) {
330345 var stat: posix.Stat = undefined;
331346 const err = posix.getErrno(posix.fstat(self.handle, &stat));
......@@ -359,7 +374,7 @@ pub const File = struct {
359374 Unexpected,
360375 };
361376
362 pub fn read(self: *File, buffer: []u8) ReadError!usize {
377 pub fn read(self: File, buffer: []u8) ReadError!usize {
363378 if (is_posix) {
364379 var index: usize = 0;
365380 while (index < buffer.len) {
......@@ -407,7 +422,7 @@ pub const File = struct {
407422
408423 pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
409424
410 pub fn write(self: *File, bytes: []const u8) WriteError!void {
425 pub fn write(self: File, bytes: []const u8) WriteError!void {
411426 if (is_posix) {
412427 try os.posixWrite(self.handle, bytes);
413428 } else if (is_windows) {
std/os/index.zig+136-99
......@@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
5757pub const windowsWrite = windows_util.windowsWrite;
5858pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
5959pub const windowsOpen = windows_util.windowsOpen;
60pub const windowsOpenW = windows_util.windowsOpenW;
6061pub const windowsLoadDll = windows_util.windowsLoadDll;
6162pub const windowsUnloadDll = windows_util.windowsUnloadDll;
6263pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
......@@ -660,6 +661,7 @@ pub fn getBaseAddress() usize {
660661 return phdr - @sizeOf(ElfHeader);
661662 },
662663 builtin.Os.macosx => return @ptrToInt(&std.c._mh_execute_header),
664 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
663665 else => @compileError("Unsupported OS"),
664666 }
665667}
......@@ -817,37 +819,40 @@ test "os.getCwd" {
817819
818820pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
819821
820pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
822/// TODO add a symLinkC variant
823pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void {
821824 if (is_windows) {
822 return symLinkWindows(allocator, existing_path, new_path);
825 return symLinkWindows(existing_path, new_path);
823826 } else {
824 return symLinkPosix(allocator, existing_path, new_path);
827 return symLinkPosix(existing_path, new_path);
825828 }
826829}
827830
828831pub const WindowsSymLinkError = error{
829 OutOfMemory,
832 NameTooLong,
833 InvalidUtf8,
834 BadPathName,
830835
831836 /// See https://github.com/ziglang/zig/issues/1396
832837 Unexpected,
833838};
834839
835pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
836 const existing_with_null = try cstr.addNullByte(allocator, existing_path);
837 defer allocator.free(existing_with_null);
838 const new_with_null = try cstr.addNullByte(allocator, new_path);
839 defer allocator.free(new_with_null);
840
841 if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) {
840pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
841 if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
842842 const err = windows.GetLastError();
843 return switch (err) {
844 else => unexpectedErrorWindows(err),
845 };
843 switch (err) {
844 else => return unexpectedErrorWindows(err),
845 }
846846 }
847847}
848848
849pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
850 const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path);
851 const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
852 return symLinkW(&existing_path_w, &new_path_w);
853}
854
849855pub const PosixSymLinkError = error{
850 OutOfMemory,
851856 AccessDenied,
852857 DiskQuota,
853858 PathAlreadyExists,
......@@ -864,43 +869,40 @@ pub const PosixSymLinkError = error{
864869 Unexpected,
865870};
866871
867pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
868 const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
869 defer allocator.free(full_buf);
870
871 const existing_buf = full_buf;
872 mem.copy(u8, existing_buf, existing_path);
873 existing_buf[existing_path.len] = 0;
874
875 const new_buf = full_buf[existing_path.len + 1 ..];
876 mem.copy(u8, new_buf, new_path);
877 new_buf[new_path.len] = 0;
878
879 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
880 if (err > 0) {
881 return switch (err) {
882 posix.EFAULT, posix.EINVAL => unreachable,
883 posix.EACCES, posix.EPERM => error.AccessDenied,
884 posix.EDQUOT => error.DiskQuota,
885 posix.EEXIST => error.PathAlreadyExists,
886 posix.EIO => error.FileSystem,
887 posix.ELOOP => error.SymLinkLoop,
888 posix.ENAMETOOLONG => error.NameTooLong,
889 posix.ENOENT => error.FileNotFound,
890 posix.ENOTDIR => error.NotDir,
891 posix.ENOMEM => error.SystemResources,
892 posix.ENOSPC => error.NoSpaceLeft,
893 posix.EROFS => error.ReadOnlyFileSystem,
894 else => unexpectedErrorPosix(err),
895 };
872pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
873 const err = posix.getErrno(posix.symlink(existing_path, new_path));
874 switch (err) {
875 0 => return,
876 posix.EFAULT => unreachable,
877 posix.EINVAL => unreachable,
878 posix.EACCES => return error.AccessDenied,
879 posix.EPERM => return error.AccessDenied,
880 posix.EDQUOT => return error.DiskQuota,
881 posix.EEXIST => return error.PathAlreadyExists,
882 posix.EIO => return error.FileSystem,
883 posix.ELOOP => return error.SymLinkLoop,
884 posix.ENAMETOOLONG => return error.NameTooLong,
885 posix.ENOENT => return error.FileNotFound,
886 posix.ENOTDIR => return error.NotDir,
887 posix.ENOMEM => return error.SystemResources,
888 posix.ENOSPC => return error.NoSpaceLeft,
889 posix.EROFS => return error.ReadOnlyFileSystem,
890 else => return unexpectedErrorPosix(err),
896891 }
897892}
898893
894pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
895 const existing_path_c = try toPosixPath(existing_path);
896 const new_path_c = try toPosixPath(new_path);
897 return symLinkPosixC(&existing_path_c, &new_path_c);
898}
899
899900// here we replace the standard +/ with -_ so that it can be used in a file name
900901const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
901902
903/// TODO remove the allocator requirement from this API
902904pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
903 if (symLink(allocator, existing_path, new_path)) {
905 if (symLink(existing_path, new_path)) {
904906 return;
905907 } else |err| switch (err) {
906908 error.PathAlreadyExists => {},
......@@ -918,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
918920 try getRandomBytes(rand_buf[0..]);
919921 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
920922
921 if (symLink(allocator, existing_path, tmp_path)) {
923 if (symLink(existing_path, tmp_path)) {
922924 return rename(tmp_path, new_path);
923925 } else |err| switch (err) {
924926 error.PathAlreadyExists => continue,
......@@ -1250,49 +1252,65 @@ pub const DeleteDirError = error{
12501252 NotDir,
12511253 DirNotEmpty,
12521254 ReadOnlyFileSystem,
1253 OutOfMemory,
1255 InvalidUtf8,
1256 BadPathName,
12541257
12551258 /// See https://github.com/ziglang/zig/issues/1396
12561259 Unexpected,
12571260};
12581261
1259/// Returns ::error.DirNotEmpty if the directory is not empty.
1260/// To delete a directory recursively, see ::deleteTree
1261pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
1262 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
1263 defer allocator.free(path_buf);
1262pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
1263 switch (builtin.os) {
1264 Os.windows => {
1265 const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
1266 return deleteDirW(&dir_path_w);
1267 },
1268 Os.linux, Os.macosx, Os.ios => {
1269 const err = posix.getErrno(posix.rmdir(dir_path));
1270 switch (err) {
1271 0 => return,
1272 posix.EACCES => return error.AccessDenied,
1273 posix.EPERM => return error.AccessDenied,
1274 posix.EBUSY => return error.FileBusy,
1275 posix.EFAULT => unreachable,
1276 posix.EINVAL => unreachable,
1277 posix.ELOOP => return error.SymLinkLoop,
1278 posix.ENAMETOOLONG => return error.NameTooLong,
1279 posix.ENOENT => return error.FileNotFound,
1280 posix.ENOMEM => return error.SystemResources,
1281 posix.ENOTDIR => return error.NotDir,
1282 posix.EEXIST => return error.DirNotEmpty,
1283 posix.ENOTEMPTY => return error.DirNotEmpty,
1284 posix.EROFS => return error.ReadOnlyFileSystem,
1285 else => return unexpectedErrorPosix(err),
1286 }
1287 },
1288 else => @compileError("unimplemented"),
1289 }
1290}
12641291
1265 mem.copy(u8, path_buf, dir_path);
1266 path_buf[dir_path.len] = 0;
1292pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
1293 if (windows.RemoveDirectoryW(dir_path_w) == 0) {
1294 const err = windows.GetLastError();
1295 switch (err) {
1296 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1297 windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
1298 else => return unexpectedErrorWindows(err),
1299 }
1300 }
1301}
12671302
1303/// Returns ::error.DirNotEmpty if the directory is not empty.
1304/// To delete a directory recursively, see ::deleteTree
1305pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
12681306 switch (builtin.os) {
12691307 Os.windows => {
1270 if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
1271 const err = windows.GetLastError();
1272 return switch (err) {
1273 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1274 windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
1275 else => unexpectedErrorWindows(err),
1276 };
1277 }
1308 const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
1309 return deleteDirW(&dir_path_w);
12781310 },
12791311 Os.linux, Os.macosx, Os.ios => {
1280 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1281 if (err > 0) {
1282 return switch (err) {
1283 posix.EACCES, posix.EPERM => error.AccessDenied,
1284 posix.EBUSY => error.FileBusy,
1285 posix.EFAULT, posix.EINVAL => unreachable,
1286 posix.ELOOP => error.SymLinkLoop,
1287 posix.ENAMETOOLONG => error.NameTooLong,
1288 posix.ENOENT => error.FileNotFound,
1289 posix.ENOMEM => error.SystemResources,
1290 posix.ENOTDIR => error.NotDir,
1291 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
1292 posix.EROFS => error.ReadOnlyFileSystem,
1293 else => unexpectedErrorPosix(err),
1294 };
1295 }
1312 const dir_path_c = try toPosixPath(dir_path);
1313 return deleteDirC(&dir_path_c);
12961314 },
12971315 else => @compileError("unimplemented"),
12981316 }
......@@ -1344,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13441362 error.IsDir => {},
13451363 error.AccessDenied => got_access_denied = true,
13461364
1365 error.InvalidUtf8,
13471366 error.SymLinkLoop,
13481367 error.NameTooLong,
13491368 error.SystemResources,
......@@ -1351,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13511370 error.NotDir,
13521371 error.FileSystem,
13531372 error.FileBusy,
1354 error.InvalidUtf8,
13551373 error.BadPathName,
13561374 error.Unexpected,
13571375 => return err,
......@@ -1379,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13791397 error.NoSpaceLeft,
13801398 error.PathAlreadyExists,
13811399 error.Unexpected,
1400 error.InvalidUtf8,
1401 error.BadPathName,
13821402 => return err,
13831403 };
13841404 defer dir.close();
......@@ -1396,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13961416 try deleteTree(allocator, full_entry_path);
13971417 }
13981418 }
1399 return deleteDir(allocator, full_path);
1419 return deleteDir(full_path);
14001420 }
14011421}
14021422
......@@ -1420,8 +1440,9 @@ pub const Dir = struct {
14201440 },
14211441 Os.windows => struct {
14221442 handle: windows.HANDLE,
1423 find_file_data: windows.WIN32_FIND_DATAA,
1443 find_file_data: windows.WIN32_FIND_DATAW,
14241444 first: bool,
1445 name_data: [256]u8,
14251446 },
14261447 else => @compileError("unimplemented"),
14271448 };
......@@ -1458,6 +1479,8 @@ pub const Dir = struct {
14581479 NoSpaceLeft,
14591480 PathAlreadyExists,
14601481 OutOfMemory,
1482 InvalidUtf8,
1483 BadPathName,
14611484
14621485 /// See https://github.com/ziglang/zig/issues/1396
14631486 Unexpected,
......@@ -1469,12 +1492,13 @@ pub const Dir = struct {
14691492 .allocator = allocator,
14701493 .handle = switch (builtin.os) {
14711494 Os.windows => blk: {
1472 var find_file_data: windows.WIN32_FIND_DATAA = undefined;
1473 const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
1495 var find_file_data: windows.WIN32_FIND_DATAW = undefined;
1496 const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
14741497 break :blk Handle{
14751498 .handle = handle,
14761499 .find_file_data = find_file_data, // TODO guaranteed copy elision
14771500 .first = true,
1501 .name_data = undefined,
14781502 };
14791503 },
14801504 Os.macosx, Os.ios => Handle{
......@@ -1589,9 +1613,12 @@ pub const Dir = struct {
15891613 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
15901614 return null;
15911615 }
1592 const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
1593 if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
1616 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
1617 if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
15941618 continue;
1619 // Trust that Windows gives us valid UTF-16LE
1620 const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
1621 const name_utf8 = self.handle.name_data[0..name_utf8_len];
15951622 const kind = blk: {
15961623 const attrs = self.handle.find_file_data.dwFileAttributes;
15971624 if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
......@@ -1600,7 +1627,7 @@ pub const Dir = struct {
16001627 break :blk Entry.Kind.Unknown;
16011628 };
16021629 return Entry{
1603 .name = name,
1630 .name = name_utf8,
16041631 .kind = kind,
16051632 };
16061633 }
......@@ -2087,8 +2114,9 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
20872114/// Call this when you made a windows DLL call or something that does SetLastError
20882115/// and you get an unexpected error.
20892116pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
2090 if (true) {
2117 if (unexpected_error_tracing) {
20912118 debug.warn("unexpected GetLastError(): {}\n", err);
2119 @breakpoint();
20922120 debug.dumpCurrentStackTrace(null);
20932121 }
20942122 return error.Unexpected;
......@@ -2103,17 +2131,35 @@ pub fn openSelfExe() !os.File {
21032131 buf[self_exe_path.len] = 0;
21042132 return os.File.openReadC(self_exe_path.ptr);
21052133 },
2134 Os.windows => {
2135 var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
2136 const wide_slice = try selfExePathW(&buf);
2137 return os.File.openReadW(wide_slice.ptr);
2138 },
21062139 else => @compileError("Unsupported OS"),
21072140 }
21082141}
21092142
21102143test "openSelfExe" {
21112144 switch (builtin.os) {
2112 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
2113 else => return error.SkipZigTest, // Unsupported OS
2145 Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(),
2146 else => return error.SkipZigTest, // Unsupported OS.
21142147 }
21152148}
21162149
2150pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 {
2151 const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast
2152 const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len);
2153 assert(rc <= out_buffer.len);
2154 if (rc == 0) {
2155 const err = windows.GetLastError();
2156 switch (err) {
2157 else => return unexpectedErrorWindows(err),
2158 }
2159 }
2160 return out_buffer[0..rc];
2161}
2162
21172163/// Get the path to the current executable.
21182164/// If you only need the directory, use selfExeDirPath.
21192165/// If you only want an open file handle, use openSelfExe.
......@@ -2129,16 +2175,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
21292175 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
21302176 Os.windows => {
21312177 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
2132 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
2133 const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
2134 assert(rc <= utf16le_buf.len);
2135 if (rc == 0) {
2136 const err = windows.GetLastError();
2137 switch (err) {
2138 else => return unexpectedErrorWindows(err),
2139 }
2140 }
2141 const utf16le_slice = utf16le_buf[0..rc];
2178 const utf16le_slice = try selfExePathW(&utf16le_buf);
21422179 // Trust that Windows gives us valid UTF-16LE.
21432180 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
21442181 return out_buffer[0..end_index];
std/os/windows/advapi32.zig+16-5
......@@ -23,11 +23,22 @@ pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlag
2323
2424pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
2525
26pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
27 phkResult: &HKEY,) LSTATUS;
28
29pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
30 lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
26pub extern "advapi32" stdcallcc fn RegOpenKeyExW(
27 hKey: HKEY,
28 lpSubKey: LPCWSTR,
29 ulOptions: DWORD,
30 samDesired: REGSAM,
31 phkResult: &HKEY,
32) LSTATUS;
33
34pub extern "advapi32" stdcallcc fn RegQueryValueExW(
35 hKey: HKEY,
36 lpValueName: LPCWSTR,
37 lpReserved: LPDWORD,
38 lpType: LPDWORD,
39 lpData: LPBYTE,
40 lpcbData: LPDWORD,
41) LSTATUS;
3142
3243// RtlGenRandom is known as SystemFunction036 under advapi32
3344// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
std/os/windows/index.zig+29-15
......@@ -3,10 +3,9 @@ const assert = std.debug.assert;
33
44pub use @import("advapi32.zig");
55pub use @import("kernel32.zig");
6pub use @import("ntdll.zig");
67pub use @import("ole32.zig");
78pub use @import("shell32.zig");
8pub use @import("shlwapi.zig");
9pub use @import("user32.zig");
109
1110test "import" {
1211 _ = @import("util.zig");
......@@ -14,6 +13,7 @@ test "import" {
1413
1514pub const ERROR = @import("error.zig");
1615
16pub const SHORT = c_short;
1717pub const BOOL = c_int;
1818pub const BOOLEAN = BYTE;
1919pub const BYTE = u8;
......@@ -172,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
172172 dwThreadId: DWORD,
173173};
174174
175pub const STARTUPINFOA = extern struct {
175pub const STARTUPINFOW = extern struct {
176176 cb: DWORD,
177 lpReserved: ?LPSTR,
178 lpDesktop: ?LPSTR,
179 lpTitle: ?LPSTR,
177 lpReserved: ?LPWSTR,
178 lpDesktop: ?LPWSTR,
179 lpTitle: ?LPWSTR,
180180 dwX: DWORD,
181181 dwY: DWORD,
182182 dwXSize: DWORD,
......@@ -236,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
236236pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
237237pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
238238
239pub const WIN32_FIND_DATAA = extern struct {
239pub const WIN32_FIND_DATAW = extern struct {
240240 dwFileAttributes: DWORD,
241241 ftCreationTime: FILETIME,
242242 ftLastAccessTime: FILETIME,
......@@ -245,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
245245 nFileSizeLow: DWORD,
246246 dwReserved0: DWORD,
247247 dwReserved1: DWORD,
248 cFileName: [260]CHAR,
249 cAlternateFileName: [14]CHAR,
248 cFileName: [260]u16,
249 cAlternateFileName: [14]u16,
250250};
251251
252252pub const FILETIME = extern struct {
......@@ -288,27 +288,27 @@ pub const GUID = extern struct {
288288 assert(str[index] == '{');
289289 index += 1;
290290
291 guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable;
291 guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable;
292292 index += 8;
293293
294294 assert(str[index] == '-');
295295 index += 1;
296296
297 guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
297 guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable;
298298 index += 4;
299299
300300 assert(str[index] == '-');
301301 index += 1;
302302
303 guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
303 guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable;
304304 index += 4;
305305
306306 assert(str[index] == '-');
307307 index += 1;
308308
309 guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
309 guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
310310 index += 2;
311 guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
311 guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
312312 index += 2;
313313
314314 assert(str[index] == '-');
......@@ -316,7 +316,7 @@ pub const GUID = extern struct {
316316
317317 var i: usize = 2;
318318 while (i < guid.Data4.len) : (i += 1) {
319 guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
319 guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
320320 index += 2;
321321 }
322322
......@@ -363,3 +363,17 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
363363pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
364364pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
365365pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
366
367pub const SMALL_RECT = extern struct {
368 Left: SHORT,
369 Top: SHORT,
370 Right: SHORT,
371 Bottom: SHORT,
372};
373
374pub const COORD = extern struct {
375 X: SHORT,
376 Y: SHORT,
377};
378
379pub const CREATE_UNICODE_ENVIRONMENT = 1024;
std/os/windows/kernel32.zig+30-43
......@@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
44
55pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
66
7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
87pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
98
10pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: [*]const u8, // TODO null terminated pointer type
12 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
15 dwCreationDisposition: DWORD,
16 dwFlagsAndAttributes: DWORD,
17 hTemplateFile: ?HANDLE,
18) HANDLE;
19
209pub extern "kernel32" stdcallcc fn CreateFileW(
2110 lpFileName: [*]const u16, // TODO null terminated pointer type
2211 dwDesiredAccess: DWORD,
......@@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
3423 nSize: DWORD,
3524) BOOL;
3625
37pub extern "kernel32" stdcallcc fn CreateProcessA(
38 lpApplicationName: ?LPCSTR,
39 lpCommandLine: LPSTR,
26pub extern "kernel32" stdcallcc fn CreateProcessW(
27 lpApplicationName: ?LPWSTR,
28 lpCommandLine: LPWSTR,
4029 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
4130 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
4231 bInheritHandles: BOOL,
4332 dwCreationFlags: DWORD,
4433 lpEnvironment: ?*c_void,
45 lpCurrentDirectory: ?LPCSTR,
46 lpStartupInfo: *STARTUPINFOA,
34 lpCurrentDirectory: ?LPWSTR,
35 lpStartupInfo: *STARTUPINFOW,
4736 lpProcessInformation: *PROCESS_INFORMATION,
4837) BOOL;
4938
50pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
51 lpSymlinkFileName: LPCSTR,
52 lpTargetFileName: LPCSTR,
53 dwFlags: DWORD,
54) BOOLEAN;
39pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
5540
5641pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
5742
5843pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
5944
60pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
6145pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
6246
6347pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
6448
65pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
49pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
6650pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
67pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
51pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
6852
6953pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
7054
......@@ -72,7 +56,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
7256
7357pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
7458
75pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
59pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
60
7661pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
7762
7863pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
......@@ -86,12 +71,12 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
8671
8772pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
8873
89pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
9074pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
9175
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
9376pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
9477
78pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
79
9580pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
9681
9782pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
......@@ -101,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
10186 in_dwBufferSize: DWORD,
10287) BOOL;
10388
104pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
105 hFile: HANDLE,
106 lpszFilePath: LPSTR,
107 cchFilePath: DWORD,
108 dwFlags: DWORD,
109) DWORD;
110
11189pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
11290 hFile: HANDLE,
11391 lpszFilePath: [*]u16,
......@@ -138,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
138116
139117pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
140118
141pub extern "kernel32" stdcallcc fn MoveFileExA(
142 lpExistingFileName: [*]const u8,
143 lpNewFileName: [*]const u8,
144 dwFlags: DWORD,
145) BOOL;
146
147119pub extern "kernel32" stdcallcc fn MoveFileExW(
148120 lpExistingFileName: [*]const u16,
149121 lpNewFileName: [*]const u16,
......@@ -175,7 +147,9 @@ pub extern "kernel32" stdcallcc fn ReadFile(
175147 in_out_lpOverlapped: ?*OVERLAPPED,
176148) BOOL;
177149
178pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
150pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
151
152pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
179153
180154pub extern "kernel32" stdcallcc fn SetFilePointerEx(
181155 in_fFile: HANDLE,
......@@ -202,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
202176
203177pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
204178
205//TODO: call unicode versions instead of relying on ANSI code page
206pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
179pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
207180
208181pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
209182
......@@ -232,3 +205,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
232205pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
233206pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
234207pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
208
209
210pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
211 dwSize: COORD,
212 dwCursorPosition: COORD,
213 wAttributes: WORD,
214 srWindow: SMALL_RECT,
215 dwMaximumWindowSize: COORD,
216};
217
218pub const FOREGROUND_BLUE = 1;
219pub const FOREGROUND_GREEN = 2;
220pub const FOREGROUND_RED = 4;
221pub const FOREGROUND_INTENSITY = 8;
std/os/windows/ntdll.zig created+3
......@@ -0,0 +1,3 @@
1use @import("index.zig");
2
3pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD;
std/os/windows/ole32.zig-1
......@@ -5,7 +5,6 @@ pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
55pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
66pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
77
8
98pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
109pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
1110pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
std/os/windows/shlwapi.zig deleted-4
......@@ -1,4 +0,0 @@
1use @import("index.zig");
2
3pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
4
std/os/windows/user32.zig deleted-4
......@@ -1,4 +0,0 @@
1use @import("index.zig");
2
3pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
4
std/os/windows/util.zig+60-43
......@@ -1,6 +1,7 @@
11const std = @import("../../index.zig");
22const builtin = @import("builtin");
33const os = std.os;
4const unicode = std.unicode;
45const windows = std.os.windows;
56const assert = std.debug.assert;
67const mem = std.mem;
......@@ -118,16 +119,14 @@ pub const OpenError = error{
118119 Unexpected,
119120};
120121
121pub fn windowsOpen(
122 file_path: []const u8,
122pub fn windowsOpenW(
123 file_path_w: [*]const u16,
123124 desired_access: windows.DWORD,
124125 share_mode: windows.DWORD,
125126 creation_disposition: windows.DWORD,
126127 flags_and_attrs: windows.DWORD,
127128) OpenError!windows.HANDLE {
128 const file_path_w = try sliceToPrefixedFileW(file_path);
129
130 const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
129 const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
131130
132131 if (result == windows.INVALID_HANDLE_VALUE) {
133132 const err = windows.GetLastError();
......@@ -146,42 +145,63 @@ pub fn windowsOpen(
146145 return result;
147146}
148147
148pub fn windowsOpen(
149 file_path: []const u8,
150 desired_access: windows.DWORD,
151 share_mode: windows.DWORD,
152 creation_disposition: windows.DWORD,
153 flags_and_attrs: windows.DWORD,
154) OpenError!windows.HANDLE {
155 const file_path_w = try sliceToPrefixedFileW(file_path);
156 return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs);
157}
158
149159/// Caller must free result.
150pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {
160pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
151161 // count bytes needed
152 const bytes_needed = x: {
153 var bytes_needed: usize = 1; // 1 for the final null byte
162 const max_chars_needed = x: {
163 var max_chars_needed: usize = 1; // 1 for the final null byte
154164 var it = env_map.iterator();
155165 while (it.next()) |pair| {
156166 // +1 for '='
157167 // +1 for null byte
158 bytes_needed += pair.key.len + pair.value.len + 2;
168 max_chars_needed += pair.key.len + pair.value.len + 2;
159169 }
160 break :x bytes_needed;
170 break :x max_chars_needed;
161171 };
162 const result = try allocator.alloc(u8, bytes_needed);
172 const result = try allocator.alloc(u16, max_chars_needed);
163173 errdefer allocator.free(result);
164174
165175 var it = env_map.iterator();
166176 var i: usize = 0;
167177 while (it.next()) |pair| {
168 mem.copy(u8, result[i..], pair.key);
169 i += pair.key.len;
178 i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
170179 result[i] = '=';
171180 i += 1;
172 mem.copy(u8, result[i..], pair.value);
173 i += pair.value.len;
181 i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
174182 result[i] = 0;
175183 i += 1;
176184 }
177185 result[i] = 0;
178 return result;
186 i += 1;
187 return allocator.shrink(u16, result, i);
179188}
180189
181pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
182 const padded_buff = try cstr.addNullByte(allocator, dll_path);
183 defer allocator.free(padded_buff);
184 return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
190pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
191 return windows.LoadLibraryW(dll_path_w) orelse {
192 const err = windows.GetLastError();
193 switch (err) {
194 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
195 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
196 windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
197 else => return os.unexpectedErrorWindows(err),
198 }
199 };
200}
201
202pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE {
203 const dll_path_w = try sliceToPrefixedFileW(dll_path);
204 return windowsLoadDllW(&dll_path_w);
185205}
186206
187207pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
......@@ -191,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
191211test "InvalidDll" {
192212 if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
193213
194 const DllName = "asdf.dll";
195 const allocator = std.debug.global_allocator;
196 const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
197 assert(err == error.DllNotFound);
214 const handle = os.windowsLoadDll("asdf.dll") catch |err| {
215 assert(err == error.FileNotFound);
198216 return;
199217 };
218 @panic("Expected error from function");
200219}
201220
202221pub fn windowsFindFirstFile(
203 allocator: *mem.Allocator,
204222 dir_path: []const u8,
205 find_file_data: *windows.WIN32_FIND_DATAA,
223 find_file_data: *windows.WIN32_FIND_DATAW,
206224) !windows.HANDLE {
207 const wild_and_null = []u8{ '\\', '*', 0 };
208 const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
209 defer allocator.free(path_with_wild_and_null);
210
211 mem.copy(u8, path_with_wild_and_null, dir_path);
212 mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
213
214 const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
225 const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
226 const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
215227
216228 if (handle == windows.INVALID_HANDLE_VALUE) {
217229 const err = windows.GetLastError();
......@@ -226,8 +238,8 @@ pub fn windowsFindFirstFile(
226238}
227239
228240/// Returns `true` if there was another file, `false` otherwise.
229pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
230 if (windows.FindNextFileA(handle, find_file_data) == 0) {
241pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
242 if (windows.FindNextFileW(handle, find_file_data) == 0) {
231243 const err = windows.GetLastError();
232244 return switch (err) {
233245 windows.ERROR.NO_MORE_FILES => false,
......@@ -288,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
288300}
289301
290302pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
303 return sliceToPrefixedSuffixedFileW(s, []u16{0});
304}
305
306pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
291307 // TODO well defined copy elision
292 var result: [PATH_MAX_WIDE + 1]u16 = undefined;
308 var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
293309
294310 // > File I/O functions in the Windows API convert "/" to "\" as part of
295311 // > converting the name to an NT-style name, except when using the "\\?\"
......@@ -297,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
297313 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
298314 // Because we want the larger maximum path length for absolute paths, we
299315 // disallow forward slashes in zig std lib file functions on Windows.
300 for (s) |byte|
316 for (s) |byte| {
301317 switch (byte) {
302 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
303 else => {},
304 };
318 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
319 else => {},
320 }
321 }
305322 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
306323 const prefix = []u16{ '\\', '\\', '?', '\\' };
307324 mem.copy(u16, result[0..], prefix);
......@@ -309,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
309326 };
310327 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
311328 assert(end_index <= result.len);
312 if (end_index == result.len) return error.NameTooLong;
313 result[end_index] = 0;
329 if (end_index + suffix.len > result.len) return error.NameTooLong;
330 mem.copy(u16, result[end_index..], suffix);
314331 return result;
315332}
std/os/zen.zig+24-25
......@@ -6,32 +6,32 @@ const assert = std.debug.assert;
66//////////////////////////
77
88pub const Message = struct {
9sender: MailboxId,
9 sender: MailboxId,
1010 receiver: MailboxId,
11 code: usize,
12 args: [5]usize,
13 payload: ?[]const u8,
11 code: usize,
12 args: [5]usize,
13 payload: ?[]const u8,
1414
1515 pub fn from(mailbox_id: *const MailboxId) Message {
16 return Message {
17 .sender = MailboxId.Undefined,
16 return Message{
17 .sender = MailboxId.Undefined,
1818 .receiver = mailbox_id.*,
19 .code = undefined,
20 .args = undefined,
21 .payload = null,
19 .code = undefined,
20 .args = undefined,
21 .payload = null,
2222 };
2323 }
2424
2525 pub fn to(mailbox_id: *const MailboxId, msg_code: usize, args: ...) Message {
26 var message = Message {
27 .sender = MailboxId.This,
26 var message = Message{
27 .sender = MailboxId.This,
2828 .receiver = mailbox_id.*,
29 .code = msg_code,
30 .args = undefined,
31 .payload = null,
29 .code = msg_code,
30 .args = undefined,
31 .payload = null,
3232 };
3333
34 assert (args.len <= message.args.len);
34 assert(args.len <= message.args.len);
3535 comptime var i = 0;
3636 inline while (i < args.len) : (i += 1) {
3737 message.args[i] = args[i];
......@@ -111,8 +111,7 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
111111pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
112112 switch (fd) {
113113 STDOUT_FILENO, STDERR_FILENO => {
114 send(Message.to(Server.Terminal, 1)
115 .withPayload(buf[0..count]));
114 send(Message.to(Server.Terminal, 1).withPayload(buf[0..count]));
116115 },
117116 else => unreachable,
118117 }
......@@ -124,14 +123,14 @@ pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
124123///////////////////////////
125124
126125pub const Syscall = enum(usize) {
127 exit = 0,
128 send = 1,
129 receive = 2,
130 subscribeIRQ = 3,
131 inb = 4,
132 outb = 5,
133 map = 6,
134 createThread = 7,
126 exit = 0,
127 send = 1,
128 receive = 2,
129 subscribeIRQ = 3,
130 inb = 4,
131 outb = 5,
132 map = 6,
133 createThread = 7,
135134};
136135
137136////////////////////
std/pdb.zig created+646
......@@ -0,0 +1,646 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const math = std.math;
5const mem = std.mem;
6const os = std.os;
7const warn = std.debug.warn;
8const coff = std.coff;
9
10const ArrayList = std.ArrayList;
11
12// https://llvm.org/docs/PDB/DbiStream.html#stream-header
13pub const DbiStreamHeader = packed struct {
14 VersionSignature: i32,
15 VersionHeader: u32,
16 Age: u32,
17 GlobalStreamIndex: u16,
18 BuildNumber: u16,
19 PublicStreamIndex: u16,
20 PdbDllVersion: u16,
21 SymRecordStream: u16,
22 PdbDllRbld: u16,
23 ModInfoSize: u32,
24 SectionContributionSize: u32,
25 SectionMapSize: u32,
26 SourceInfoSize: i32,
27 TypeServerSize: i32,
28 MFCTypeServerIndex: u32,
29 OptionalDbgHeaderSize: i32,
30 ECSubstreamSize: i32,
31 Flags: u16,
32 Machine: u16,
33 Padding: u32,
34};
35
36pub const SectionContribEntry = packed struct {
37 Section: u16,
38 Padding1: [2]u8,
39 Offset: u32,
40 Size: u32,
41 Characteristics: u32,
42 ModuleIndex: u16,
43 Padding2: [2]u8,
44 DataCrc: u32,
45 RelocCrc: u32,
46};
47
48pub const ModInfo = packed struct {
49 Unused1: u32,
50 SectionContr: SectionContribEntry,
51 Flags: u16,
52 ModuleSymStream: u16,
53 SymByteSize: u32,
54 C11ByteSize: u32,
55 C13ByteSize: u32,
56 SourceFileCount: u16,
57 Padding: [2]u8,
58 Unused2: u32,
59 SourceFileNameIndex: u32,
60 PdbFilePathNameIndex: u32,
61 // These fields are variable length
62 //ModuleName: char[],
63 //ObjFileName: char[],
64};
65
66pub const SectionMapHeader = packed struct {
67 Count: u16, /// Number of segment descriptors
68 LogCount: u16, /// Number of logical segment descriptors
69};
70
71pub const SectionMapEntry = packed struct {
72 Flags: u16 , /// See the SectionMapEntryFlags enum below.
73 Ovl: u16 , /// Logical overlay number
74 Group: u16 , /// Group index into descriptor array.
75 Frame: u16 ,
76 SectionName: u16 , /// Byte index of segment / group name in string table, or 0xFFFF.
77 ClassName: u16 , /// Byte index of class in string table, or 0xFFFF.
78 Offset: u32 , /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group.
79 SectionLength: u32 , /// Byte count of the segment or group.
80};
81
82pub const StreamType = enum(u16) {
83 Pdb = 1,
84 Tpi = 2,
85 Dbi = 3,
86 Ipi = 4,
87};
88
89/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful
90/// for reference purposes and when dealing with unknown record types.
91pub const SymbolKind = packed enum(u16) {
92 S_COMPILE = 1,
93 S_REGISTER_16t = 2,
94 S_CONSTANT_16t = 3,
95 S_UDT_16t = 4,
96 S_SSEARCH = 5,
97 S_SKIP = 7,
98 S_CVRESERVE = 8,
99 S_OBJNAME_ST = 9,
100 S_ENDARG = 10,
101 S_COBOLUDT_16t = 11,
102 S_MANYREG_16t = 12,
103 S_RETURN = 13,
104 S_ENTRYTHIS = 14,
105 S_BPREL16 = 256,
106 S_LDATA16 = 257,
107 S_GDATA16 = 258,
108 S_PUB16 = 259,
109 S_LPROC16 = 260,
110 S_GPROC16 = 261,
111 S_THUNK16 = 262,
112 S_BLOCK16 = 263,
113 S_WITH16 = 264,
114 S_LABEL16 = 265,
115 S_CEXMODEL16 = 266,
116 S_VFTABLE16 = 267,
117 S_REGREL16 = 268,
118 S_BPREL32_16t = 512,
119 S_LDATA32_16t = 513,
120 S_GDATA32_16t = 514,
121 S_PUB32_16t = 515,
122 S_LPROC32_16t = 516,
123 S_GPROC32_16t = 517,
124 S_THUNK32_ST = 518,
125 S_BLOCK32_ST = 519,
126 S_WITH32_ST = 520,
127 S_LABEL32_ST = 521,
128 S_CEXMODEL32 = 522,
129 S_VFTABLE32_16t = 523,
130 S_REGREL32_16t = 524,
131 S_LTHREAD32_16t = 525,
132 S_GTHREAD32_16t = 526,
133 S_SLINK32 = 527,
134 S_LPROCMIPS_16t = 768,
135 S_GPROCMIPS_16t = 769,
136 S_PROCREF_ST = 1024,
137 S_DATAREF_ST = 1025,
138 S_ALIGN = 1026,
139 S_LPROCREF_ST = 1027,
140 S_OEM = 1028,
141 S_TI16_MAX = 4096,
142 S_REGISTER_ST = 4097,
143 S_CONSTANT_ST = 4098,
144 S_UDT_ST = 4099,
145 S_COBOLUDT_ST = 4100,
146 S_MANYREG_ST = 4101,
147 S_BPREL32_ST = 4102,
148 S_LDATA32_ST = 4103,
149 S_GDATA32_ST = 4104,
150 S_PUB32_ST = 4105,
151 S_LPROC32_ST = 4106,
152 S_GPROC32_ST = 4107,
153 S_VFTABLE32 = 4108,
154 S_REGREL32_ST = 4109,
155 S_LTHREAD32_ST = 4110,
156 S_GTHREAD32_ST = 4111,
157 S_LPROCMIPS_ST = 4112,
158 S_GPROCMIPS_ST = 4113,
159 S_COMPILE2_ST = 4115,
160 S_MANYREG2_ST = 4116,
161 S_LPROCIA64_ST = 4117,
162 S_GPROCIA64_ST = 4118,
163 S_LOCALSLOT_ST = 4119,
164 S_PARAMSLOT_ST = 4120,
165 S_ANNOTATION = 4121,
166 S_GMANPROC_ST = 4122,
167 S_LMANPROC_ST = 4123,
168 S_RESERVED1 = 4124,
169 S_RESERVED2 = 4125,
170 S_RESERVED3 = 4126,
171 S_RESERVED4 = 4127,
172 S_LMANDATA_ST = 4128,
173 S_GMANDATA_ST = 4129,
174 S_MANFRAMEREL_ST = 4130,
175 S_MANREGISTER_ST = 4131,
176 S_MANSLOT_ST = 4132,
177 S_MANMANYREG_ST = 4133,
178 S_MANREGREL_ST = 4134,
179 S_MANMANYREG2_ST = 4135,
180 S_MANTYPREF = 4136,
181 S_UNAMESPACE_ST = 4137,
182 S_ST_MAX = 4352,
183 S_WITH32 = 4356,
184 S_MANYREG = 4362,
185 S_LPROCMIPS = 4372,
186 S_GPROCMIPS = 4373,
187 S_MANYREG2 = 4375,
188 S_LPROCIA64 = 4376,
189 S_GPROCIA64 = 4377,
190 S_LOCALSLOT = 4378,
191 S_PARAMSLOT = 4379,
192 S_MANFRAMEREL = 4382,
193 S_MANREGISTER = 4383,
194 S_MANSLOT = 4384,
195 S_MANMANYREG = 4385,
196 S_MANREGREL = 4386,
197 S_MANMANYREG2 = 4387,
198 S_UNAMESPACE = 4388,
199 S_DATAREF = 4390,
200 S_ANNOTATIONREF = 4392,
201 S_TOKENREF = 4393,
202 S_GMANPROC = 4394,
203 S_LMANPROC = 4395,
204 S_ATTR_FRAMEREL = 4398,
205 S_ATTR_REGISTER = 4399,
206 S_ATTR_REGREL = 4400,
207 S_ATTR_MANYREG = 4401,
208 S_SEPCODE = 4402,
209 S_LOCAL_2005 = 4403,
210 S_DEFRANGE_2005 = 4404,
211 S_DEFRANGE2_2005 = 4405,
212 S_DISCARDED = 4411,
213 S_LPROCMIPS_ID = 4424,
214 S_GPROCMIPS_ID = 4425,
215 S_LPROCIA64_ID = 4426,
216 S_GPROCIA64_ID = 4427,
217 S_DEFRANGE_HLSL = 4432,
218 S_GDATA_HLSL = 4433,
219 S_LDATA_HLSL = 4434,
220 S_LOCAL_DPC_GROUPSHARED = 4436,
221 S_DEFRANGE_DPC_PTR_TAG = 4439,
222 S_DPC_SYM_TAG_MAP = 4440,
223 S_ARMSWITCHTABLE = 4441,
224 S_POGODATA = 4444,
225 S_INLINESITE2 = 4445,
226 S_MOD_TYPEREF = 4447,
227 S_REF_MINIPDB = 4448,
228 S_PDBMAP = 4449,
229 S_GDATA_HLSL32 = 4450,
230 S_LDATA_HLSL32 = 4451,
231 S_GDATA_HLSL32_EX = 4452,
232 S_LDATA_HLSL32_EX = 4453,
233 S_FASTLINK = 4455,
234 S_INLINEES = 4456,
235 S_END = 6,
236 S_INLINESITE_END = 4430,
237 S_PROC_ID_END = 4431,
238 S_THUNK32 = 4354,
239 S_TRAMPOLINE = 4396,
240 S_SECTION = 4406,
241 S_COFFGROUP = 4407,
242 S_EXPORT = 4408,
243 S_LPROC32 = 4367,
244 S_GPROC32 = 4368,
245 S_LPROC32_ID = 4422,
246 S_GPROC32_ID = 4423,
247 S_LPROC32_DPC = 4437,
248 S_LPROC32_DPC_ID = 4438,
249 S_REGISTER = 4358,
250 S_PUB32 = 4366,
251 S_PROCREF = 4389,
252 S_LPROCREF = 4391,
253 S_ENVBLOCK = 4413,
254 S_INLINESITE = 4429,
255 S_LOCAL = 4414,
256 S_DEFRANGE = 4415,
257 S_DEFRANGE_SUBFIELD = 4416,
258 S_DEFRANGE_REGISTER = 4417,
259 S_DEFRANGE_FRAMEPOINTER_REL = 4418,
260 S_DEFRANGE_SUBFIELD_REGISTER = 4419,
261 S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 4420,
262 S_DEFRANGE_REGISTER_REL = 4421,
263 S_BLOCK32 = 4355,
264 S_LABEL32 = 4357,
265 S_OBJNAME = 4353,
266 S_COMPILE2 = 4374,
267 S_COMPILE3 = 4412,
268 S_FRAMEPROC = 4114,
269 S_CALLSITEINFO = 4409,
270 S_FILESTATIC = 4435,
271 S_HEAPALLOCSITE = 4446,
272 S_FRAMECOOKIE = 4410,
273 S_CALLEES = 4442,
274 S_CALLERS = 4443,
275 S_UDT = 4360,
276 S_COBOLUDT = 4361,
277 S_BUILDINFO = 4428,
278 S_BPREL32 = 4363,
279 S_REGREL32 = 4369,
280 S_CONSTANT = 4359,
281 S_MANCONSTANT = 4397,
282 S_LDATA32 = 4364,
283 S_GDATA32 = 4365,
284 S_LMANDATA = 4380,
285 S_GMANDATA = 4381,
286 S_LTHREAD32 = 4370,
287 S_GTHREAD32 = 4371,
288};
289
290pub const TypeIndex = u32;
291
292pub const ProcSym = packed struct {
293 Parent: u32 ,
294 End: u32 ,
295 Next: u32 ,
296 CodeSize: u32 ,
297 DbgStart: u32 ,
298 DbgEnd: u32 ,
299 FunctionType: TypeIndex ,
300 CodeOffset: u32,
301 Segment: u16,
302 Flags: ProcSymFlags,
303 // following is a null terminated string
304 // Name: [*]u8,
305};
306
307pub const ProcSymFlags = packed struct {
308 HasFP: bool,
309 HasIRET: bool,
310 HasFRET: bool,
311 IsNoReturn: bool,
312 IsUnreachable: bool,
313 HasCustomCallingConv: bool,
314 IsNoInline: bool,
315 HasOptimizedDebugInfo: bool,
316};
317
318pub const SectionContrSubstreamVersion = enum(u32) {
319 Ver60 = 0xeffe0000 + 19970605,
320 V2 = 0xeffe0000 + 20140516
321};
322
323pub const RecordPrefix = packed struct {
324 RecordLen: u16, /// Record length, starting from &RecordKind.
325 RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind)
326};
327
328pub const LineFragmentHeader = packed struct {
329 RelocOffset: u32, /// Code offset of line contribution.
330 RelocSegment: u16, /// Code segment of line contribution.
331 Flags: LineFlags,
332 CodeSize: u32, /// Code size of this line contribution.
333};
334
335pub const LineFlags = packed struct {
336 LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS
337 unused: u15,
338};
339
340/// The following two variable length arrays appear immediately after the
341/// header. The structure definitions follow.
342/// LineNumberEntry Lines[NumLines];
343/// ColumnNumberEntry Columns[NumLines];
344pub const LineBlockFragmentHeader = packed struct {
345 /// Offset of FileChecksum entry in File
346 /// checksums buffer. The checksum entry then
347 /// contains another offset into the string
348 /// table of the actual name.
349 NameIndex: u32,
350 NumLines: u32,
351 BlockSize: u32, /// code size of block, in bytes
352};
353
354
355pub const LineNumberEntry = packed struct {
356 Offset: u32, /// Offset to start of code bytes for line number
357 Flags: u32,
358
359 /// TODO runtime crash when I make the actual type of Flags this
360 const Flags = packed struct {
361 Start: u24,
362 End: u7,
363 IsStatement: bool,
364 };
365};
366
367pub const ColumnNumberEntry = packed struct {
368 StartColumn: u16,
369 EndColumn: u16,
370};
371
372/// Checksum bytes follow.
373pub const FileChecksumEntryHeader = packed struct {
374 FileNameOffset: u32, /// Byte offset of filename in global string table.
375 ChecksumSize: u8, /// Number of bytes of checksum.
376 ChecksumKind: u8, /// FileChecksumKind
377};
378
379pub const DebugSubsectionKind = packed enum(u32) {
380 None = 0,
381 Symbols = 0xf1,
382 Lines = 0xf2,
383 StringTable = 0xf3,
384 FileChecksums = 0xf4,
385 FrameData = 0xf5,
386 InlineeLines = 0xf6,
387 CrossScopeImports = 0xf7,
388 CrossScopeExports = 0xf8,
389
390 // These appear to relate to .Net assembly info.
391 ILLines = 0xf9,
392 FuncMDTokenMap = 0xfa,
393 TypeMDTokenMap = 0xfb,
394 MergedAssemblyInput = 0xfc,
395
396 CoffSymbolRVA = 0xfd,
397};
398
399
400pub const DebugSubsectionHeader = packed struct {
401 Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum
402 Length: u32, /// number of bytes occupied by this record.
403};
404
405
406pub const PDBStringTableHeader = packed struct {
407 Signature: u32, /// PDBStringTableSignature
408 HashVersion: u32, /// 1 or 2
409 ByteSize: u32, /// Number of bytes of names buffer.
410};
411
412pub const Pdb = struct {
413 in_file: os.File,
414 allocator: *mem.Allocator,
415 coff: *coff.Coff,
416 string_table: *MsfStream,
417 dbi: *MsfStream,
418
419 msf: Msf,
420
421 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
422 self.in_file = try os.File.openRead(file_name);
423 self.allocator = coff_ptr.allocator;
424 self.coff = coff_ptr;
425
426 try self.msf.openFile(self.allocator, self.in_file);
427 }
428
429 pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream {
430 if (id >= self.msf.streams.len)
431 return null;
432 return &self.msf.streams[id];
433 }
434
435 pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream {
436 const id = @enumToInt(stream);
437 return self.getStreamById(id);
438 }
439};
440
441// see https://llvm.org/docs/PDB/MsfFile.html
442const Msf = struct {
443 directory: MsfStream,
444 streams: []MsfStream,
445
446 fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void {
447 var file_stream = io.FileInStream.init(file);
448 const in = &file_stream.stream;
449
450 var superblock: SuperBlock = undefined;
451 try in.readStruct(SuperBlock, &superblock);
452
453 if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic))
454 return error.InvalidDebugInfo;
455
456 switch (superblock.BlockSize) {
457 // llvm only supports 4096 but we can handle any of these values
458 512, 1024, 2048, 4096 => {},
459 else => return error.InvalidDebugInfo
460 }
461
462 if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos())
463 return error.InvalidDebugInfo;
464
465 self.directory = try MsfStream.init(
466 superblock.BlockSize,
467 blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize),
468 superblock.BlockSize * superblock.BlockMapAddr,
469 file,
470 allocator,
471 );
472
473 const stream_count = try self.directory.stream.readIntLe(u32);
474
475 const stream_sizes = try allocator.alloc(u32, stream_count);
476 for (stream_sizes) |*s| {
477 const size = try self.directory.stream.readIntLe(u32);
478 s.* = blockCountFromSize(size, superblock.BlockSize);
479 }
480
481 self.streams = try allocator.alloc(MsfStream, stream_count);
482 for (self.streams) |*stream, i| {
483 stream.* = try MsfStream.init(
484 superblock.BlockSize,
485 stream_sizes[i],
486 // MsfStream.init expects the file to be at the part where it reads [N]u32
487 try file.getPos(),
488 file,
489 allocator,
490 );
491 }
492 }
493};
494
495fn blockCountFromSize(size: u32, block_size: u32) u32 {
496 return (size + block_size - 1) / block_size;
497}
498
499// https://llvm.org/docs/PDB/MsfFile.html#the-superblock
500const SuperBlock = packed struct {
501 /// The LLVM docs list a space between C / C++ but empirically this is not the case.
502 const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
503
504 FileMagic: [file_magic.len]u8,
505
506 /// The block size of the internal file system. Valid values are 512, 1024,
507 /// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending
508 /// on the block sizes. For the purposes of LLVM, we handle only block sizes of
509 /// 4KiB, and all further discussion assumes a block size of 4KiB.
510 BlockSize: u32,
511
512 /// The index of a block within the file, at which begins a bitfield representing
513 /// the set of all blocks within the file which are “free” (i.e. the data within
514 /// that block is not used). See The Free Block Map for more information. Important:
515 /// FreeBlockMapBlock can only be 1 or 2!
516 FreeBlockMapBlock: u32,
517
518 /// The total number of blocks in the file. NumBlocks * BlockSize should equal the
519 /// size of the file on disk.
520 NumBlocks: u32,
521
522 /// The size of the stream directory, in bytes. The stream directory contains
523 /// information about each stream’s size and the set of blocks that it occupies.
524 /// It will be described in more detail later.
525 NumDirectoryBytes: u32,
526
527 Unknown: u32,
528
529 /// The index of a block within the MSF file. At this block is an array of
530 /// ulittle32_t’s listing the blocks that the stream directory resides on.
531 /// For large MSF files, the stream directory (which describes the block
532 /// layout of each stream) may not fit entirely on a single block. As a
533 /// result, this extra layer of indirection is introduced, whereby this
534 /// block contains the list of blocks that the stream directory occupies,
535 /// and the stream directory itself can be stitched together accordingly.
536 /// The number of ulittle32_t’s in this array is given by
537 /// ceil(NumDirectoryBytes / BlockSize).
538 BlockMapAddr: u32,
539
540};
541
542const MsfStream = struct {
543 in_file: os.File,
544 pos: usize,
545 blocks: []u32,
546 block_size: u32,
547
548 /// Implementation of InStream trait for Pdb.MsfStream
549 stream: Stream,
550
551 pub const Error = @typeOf(read).ReturnType.ErrorSet;
552 pub const Stream = io.InStream(Error);
553
554 fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream {
555 var stream = MsfStream {
556 .in_file = file,
557 .pos = 0,
558 .blocks = try allocator.alloc(u32, block_count),
559 .block_size = block_size,
560 .stream = Stream {
561 .readFn = readFn,
562 },
563 };
564
565 var file_stream = io.FileInStream.init(file);
566 const in = &file_stream.stream;
567 try file.seekTo(pos);
568
569 var i: u32 = 0;
570 while (i < block_count) : (i += 1) {
571 stream.blocks[i] = try in.readIntLe(u32);
572 }
573
574 return stream;
575 }
576
577 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
578 var list = ArrayList(u8).init(allocator);
579 defer list.deinit();
580 while (true) {
581 const byte = try self.stream.readByte();
582 if (byte == 0) {
583 return list.toSlice();
584 }
585 try list.append(byte);
586 }
587 }
588
589 fn read(self: *MsfStream, buffer: []u8) !usize {
590 var block_id = self.pos / self.block_size;
591 var block = self.blocks[block_id];
592 var offset = self.pos % self.block_size;
593
594 try self.in_file.seekTo(block * self.block_size + offset);
595 var file_stream = io.FileInStream.init(self.in_file);
596 const in = &file_stream.stream;
597
598 var size: usize = 0;
599 for (buffer) |*byte| {
600 byte.* = try in.readByte();
601
602 offset += 1;
603 size += 1;
604
605 // If we're at the end of a block, go to the next one.
606 if (offset == self.block_size) {
607 offset = 0;
608 block_id += 1;
609 block = self.blocks[block_id];
610 try self.in_file.seekTo(block * self.block_size);
611 }
612 }
613
614 self.pos += size;
615 return size;
616 }
617
618 fn seekForward(self: *MsfStream, len: usize) !void {
619 self.pos += len;
620 if (self.pos >= self.blocks.len * self.block_size)
621 return error.EOF;
622 }
623
624 fn seekTo(self: *MsfStream, len: usize) !void {
625 self.pos = len;
626 if (self.pos >= self.blocks.len * self.block_size)
627 return error.EOF;
628 }
629
630 fn getSize(self: *const MsfStream) usize {
631 return self.blocks.len * self.block_size;
632 }
633
634 fn getFilePos(self: MsfStream) usize {
635 const block_id = self.pos / self.block_size;
636 const block = self.blocks[block_id];
637 const offset = self.pos % self.block_size;
638
639 return block * self.block_size + offset;
640 }
641
642 fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
643 const self = @fieldParentPtr(MsfStream, "stream", in_stream);
644 return self.read(buffer);
645 }
646};
std/rb.zig+16-14
......@@ -9,9 +9,7 @@ const Color = enum(u1) {
99const Red = Color.Red;
1010const Black = Color.Black;
1111
12const ReplaceError = error {
13 NotEqual,
14};
12const ReplaceError = error{NotEqual};
1513
1614/// Insert this into your struct that you want to add to a red-black tree.
1715/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
......@@ -21,13 +19,15 @@ const ReplaceError = error {
2119/// node: rb.Node,
2220/// value: i32,
2321/// };
24/// fn number(node: *Node) Number {
22/// fn number(node: *rb.Node) Number {
2523/// return @fieldParentPtr(Number, "node", node);
2624/// }
2725pub const Node = struct {
2826 left: ?*Node,
2927 right: ?*Node,
30 parent_and_color: usize, /// parent | color
28
29 /// parent | color
30 parent_and_color: usize,
3131
3232 pub fn next(constnode: *Node) ?*Node {
3333 var node = constnode;
......@@ -130,7 +130,7 @@ pub const Node = struct {
130130
131131pub const Tree = struct {
132132 root: ?*Node,
133 compareFn: fn(*Node, *Node) mem.Compare,
133 compareFn: fn (*Node, *Node) mem.Compare,
134134
135135 /// If you have a need for a version that caches this, please file a bug.
136136 pub fn first(tree: *Tree) ?*Node {
......@@ -180,7 +180,7 @@ pub const Tree = struct {
180180 while (node.get_parent()) |*parent| {
181181 if (parent.*.is_black())
182182 break;
183 // the root is always black
183 // the root is always black
184184 var grandpa = parent.*.get_parent() orelse unreachable;
185185
186186 if (parent.* == grandpa.left) {
......@@ -206,7 +206,7 @@ pub const Tree = struct {
206206 }
207207 } else {
208208 var maybe_uncle = grandpa.left;
209
209
210210 if (maybe_uncle) |uncle| {
211211 if (uncle.is_black())
212212 break;
......@@ -259,7 +259,7 @@ pub const Tree = struct {
259259 if (node.left == null) {
260260 next = node.right.?; // Not both null as per above
261261 } else if (node.right == null) {
262 next = node.left.?; // Not both null as per above
262 next = node.left.?; // Not both null as per above
263263 } else
264264 next = node.right.?.get_first(); // Just checked for null above
265265
......@@ -313,7 +313,7 @@ pub const Tree = struct {
313313 var parent = maybe_parent.?;
314314 if (node == parent.left) {
315315 var sibling = parent.right.?; // Same number of black nodes.
316
316
317317 if (sibling.is_red()) {
318318 sibling.set_color(Black);
319319 parent.set_color(Red);
......@@ -321,7 +321,8 @@ pub const Tree = struct {
321321 sibling = parent.right.?; // Just rotated
322322 }
323323 if ((if (sibling.left) |n| n.is_black() else true) and
324 (if (sibling.right) |n| n.is_black() else true)) {
324 (if (sibling.right) |n| n.is_black() else true))
325 {
325326 sibling.set_color(Red);
326327 node = parent;
327328 maybe_parent = parent.get_parent();
......@@ -341,7 +342,7 @@ pub const Tree = struct {
341342 break;
342343 } else {
343344 var sibling = parent.left.?; // Same number of black nodes.
344
345
345346 if (sibling.is_red()) {
346347 sibling.set_color(Black);
347348 parent.set_color(Red);
......@@ -349,7 +350,8 @@ pub const Tree = struct {
349350 sibling = parent.left.?; // Just rotated
350351 }
351352 if ((if (sibling.left) |n| n.is_black() else true) and
352 (if (sibling.right) |n| n.is_black() else true)) {
353 (if (sibling.right) |n| n.is_black() else true))
354 {
353355 sibling.set_color(Red);
354356 node = parent;
355357 maybe_parent = parent.get_parent();
......@@ -397,7 +399,7 @@ pub const Tree = struct {
397399 new.* = old.*;
398400 }
399401
400 pub fn init(tree: *Tree, f: fn(*Node, *Node) mem.Compare) void {
402 pub fn init(tree: *Tree, f: fn (*Node, *Node) mem.Compare) void {
401403 tree.root = null;
402404 tree.compareFn = f;
403405 }
std/special/build_runner.zig+2-2
......@@ -49,14 +49,14 @@ pub fn main() !void {
4949
5050 var stderr_file = io.getStdErr();
5151 var stderr_file_stream: io.FileOutStream = undefined;
52 var stderr_stream = if (stderr_file) |*f| x: {
52 var stderr_stream = if (stderr_file) |f| x: {
5353 stderr_file_stream = io.FileOutStream.init(f);
5454 break :x &stderr_file_stream.stream;
5555 } else |err| err;
5656
5757 var stdout_file = io.getStdOut();
5858 var stdout_file_stream: io.FileOutStream = undefined;
59 var stdout_stream = if (stdout_file) |*f| x: {
59 var stdout_stream = if (stdout_file) |f| x: {
6060 stdout_file_stream = io.FileOutStream.init(f);
6161 break :x &stdout_file_stream.stream;
6262 } else |err| err;
std/zig/parse.zig+56-13
......@@ -340,7 +340,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
340340 const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
341341 node_ptr.* = &node.base;
342342
343 stack.append(State{ .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
343 try stack.append(State{
344 .FieldListCommaOrEnd = FieldCtx{
345 .doc_comments = &node.doc_comments,
346 .container_decl = ctx.container_decl,
347 },
348 });
344349 try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.type_expr } });
345350 try stack.append(State{ .ExpectToken = Token.Id.Colon });
346351 continue;
......@@ -458,7 +463,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
458463 const node_ptr = try container_decl.fields_and_decls.addOne();
459464 node_ptr.* = &node.base;
460465
461 try stack.append(State{ .FieldListCommaOrEnd = container_decl });
466 try stack.append(State{
467 .FieldListCommaOrEnd = FieldCtx{
468 .doc_comments = &node.doc_comments,
469 .container_decl = container_decl,
470 },
471 });
462472 try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.type_expr } });
463473 try stack.append(State{ .ExpectToken = Token.Id.Colon });
464474 continue;
......@@ -473,7 +483,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
473483 });
474484 try container_decl.fields_and_decls.push(&node.base);
475485
476 stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable;
486 try stack.append(State{
487 .FieldListCommaOrEnd = FieldCtx{
488 .doc_comments = &node.doc_comments,
489 .container_decl = container_decl,
490 },
491 });
477492 try stack.append(State{ .FieldInitValue = OptionalCtx{ .RequiredNull = &node.value_expr } });
478493 try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &node.type_expr } });
479494 try stack.append(State{ .IfToken = Token.Id.Colon });
......@@ -488,7 +503,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
488503 });
489504 try container_decl.fields_and_decls.push(&node.base);
490505
491 stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable;
506 try stack.append(State{
507 .FieldListCommaOrEnd = FieldCtx{
508 .doc_comments = &node.doc_comments,
509 .container_decl = container_decl,
510 },
511 });
492512 try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &node.value } });
493513 try stack.append(State{ .IfToken = Token.Id.Equal });
494514 continue;
......@@ -1265,17 +1285,35 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
12651285 },
12661286 }
12671287 },
1268 State.FieldListCommaOrEnd => |container_decl| {
1269 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
1270 ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
1271 container_decl.rbrace_token = end;
1288 State.FieldListCommaOrEnd => |field_ctx| {
1289 const end_token = nextToken(&tok_it, &tree);
1290 const end_token_index = end_token.index;
1291 const end_token_ptr = end_token.ptr;
1292 switch (end_token_ptr.id) {
1293 Token.Id.Comma => {
1294 if (eatToken(&tok_it, &tree, Token.Id.DocComment)) |doc_comment_token| {
1295 const loc = tree.tokenLocation(end_token_ptr.end, doc_comment_token);
1296 if (loc.line == 0) {
1297 try pushDocComment(arena, doc_comment_token, field_ctx.doc_comments);
1298 } else {
1299 prevToken(&tok_it, &tree);
1300 }
1301 }
1302
1303 try stack.append(State{ .ContainerDecl = field_ctx.container_decl });
12721304 continue;
1273 } else {
1274 try stack.append(State{ .ContainerDecl = container_decl });
1305 },
1306 Token.Id.RBrace => {
1307 field_ctx.container_decl.rbrace_token = end_token_index;
12751308 continue;
12761309 },
1277 ExpectCommaOrEndResult.parse_error => |e| {
1278 try tree.errors.push(e);
1310 else => {
1311 try tree.errors.push(Error{
1312 .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd{
1313 .token = end_token_index,
1314 .end_id = end_token_ptr.id,
1315 },
1316 });
12791317 return tree;
12801318 },
12811319 }
......@@ -2813,6 +2851,11 @@ const ExprListCtx = struct {
28132851 ptr: *TokenIndex,
28142852};
28152853
2854const FieldCtx = struct {
2855 container_decl: *ast.Node.ContainerDecl,
2856 doc_comments: *?*ast.Node.DocComment,
2857};
2858
28162859fn ListSave(comptime List: type) type {
28172860 return struct {
28182861 list: *List,
......@@ -2950,7 +2993,7 @@ const State = union(enum) {
29502993 ExprListCommaOrEnd: ExprListCtx,
29512994 FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
29522995 FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
2953 FieldListCommaOrEnd: *ast.Node.ContainerDecl,
2996 FieldListCommaOrEnd: FieldCtx,
29542997 FieldInitValue: OptionalCtx,
29552998 ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
29562999 ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
std/zig/parser_test.zig+18-1
......@@ -1,3 +1,20 @@
1test "zig fmt: correctly move doc comments on struct fields" {
2 try testTransform(
3 \\pub const section_64 = extern struct {
4 \\ sectname: [16]u8, /// name of this section
5 \\ segname: [16]u8, /// segment this section goes in
6 \\};
7 ,
8 \\pub const section_64 = extern struct {
9 \\ /// name of this section
10 \\ sectname: [16]u8,
11 \\ /// segment this section goes in
12 \\ segname: [16]u8,
13 \\};
14 \\
15 );
16}
17
118test "zig fmt: preserve space between async fn definitions" {
219 try testCanonical(
320 \\async fn a() void {}
......@@ -1848,7 +1865,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
18481865
18491866fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
18501867 var stderr_file = try io.getStdErr();
1851 var stderr = &io.FileOutStream.init(&stderr_file).stream;
1868 var stderr = &io.FileOutStream.init(stderr_file).stream;
18521869
18531870 var tree = try std.zig.parse(allocator, source);
18541871 defer tree.deinit();
test/behavior.zig+1
......@@ -11,6 +11,7 @@ comptime {
1111 _ = @import("cases/bugs/1111.zig");
1212 _ = @import("cases/bugs/1230.zig");
1313 _ = @import("cases/bugs/1277.zig");
14 _ = @import("cases/bugs/1421.zig");
1415 _ = @import("cases/bugs/394.zig");
1516 _ = @import("cases/bugs/655.zig");
1617 _ = @import("cases/bugs/656.zig");
test/cases/bugs/1421.zig created+14
......@@ -0,0 +1,14 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4
5const S = struct {
6 fn method() builtin.TypeInfo {
7 return @typeInfo(S);
8 }
9};
10
11test "functions with return type required to be comptime are generic" {
12 const ti = S.method();
13 assert(builtin.TypeId(ti) == builtin.TypeId.Struct);
14}
test/cases/cast.zig+38-9
......@@ -487,12 +487,41 @@ fn MakeType(comptime T: type) type {
487487}
488488
489489test "implicit cast from *[N]T to ?[*]T" {
490 var x: ?[*]u16 = null;
491 var y: [4]u16 = [4]u16 {0, 1, 2, 3};
492
493 x = &y;
494 assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
495 x.?[0] = 8;
496 y[3] = 6;
497 assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
498}
\ No newline at end of file
490 var x: ?[*]u16 = null;
491 var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
492
493 x = &y;
494 assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
495 x.?[0] = 8;
496 y[3] = 6;
497 assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
498}
499
500test "implicit cast from *T to ?*c_void" {
501 var a: u8 = 1;
502 incrementVoidPtrValue(&a);
503 std.debug.assert(a == 2);
504}
505
506fn incrementVoidPtrValue(value: ?*c_void) void {
507 @ptrCast(*u8, value.?).* += 1;
508}
509
510test "implicit cast from [*]T to ?*c_void" {
511 var a = []u8{ 3, 2, 1 };
512 incrementVoidPtrArray(a[0..].ptr, 3);
513 assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 }));
514}
515
516fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
517 var n: usize = 0;
518 while (n < len) : (n += 1) {
519 @ptrCast([*]u8, array.?)[n] += 1;
520 }
521}
522
523test "*usize to *void" {
524 var i = usize(0);
525 var v = @ptrCast(*void, &i);
526 v.* = {};
527}
test/cases/eval.zig+22
......@@ -652,3 +652,25 @@ fn loopNTimes(comptime n: usize) void {
652652 comptime var i = 0;
653653 inline while (i < n) : (i += 1) {}
654654}
655
656test "variable inside inline loop that has different types on different iterations" {
657 testVarInsideInlineLoop(true, u32(42));
658}
659
660fn testVarInsideInlineLoop(args: ...) void {
661 comptime var i = 0;
662 inline while (i < args.len) : (i += 1) {
663 const x = args[i];
664 if (i == 0) assert(x);
665 if (i == 1) assert(x == 42);
666 }
667}
668
669test "inline for with same type but different values" {
670 var res: usize = 0;
671 inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| {
672 var a: T = undefined;
673 res += a.len;
674 }
675 assert(res == 5);
676}
test/cases/for.zig+2-4
......@@ -71,8 +71,7 @@ fn testBreakOuter() void {
7171 var array = "aoeu";
7272 var count: usize = 0;
7373 outer: for (array) |_| {
74 // TODO shouldn't get error for redeclaring "_"
75 for (array) |_2| {
74 for (array) |_| {
7675 count += 1;
7776 break :outer;
7877 }
......@@ -89,8 +88,7 @@ fn testContinueOuter() void {
8988 var array = "aoeu";
9089 var counter: usize = 0;
9190 outer: for (array) |_| {
92 // TODO shouldn't get error for redeclaring "_"
93 for (array) |_2| {
91 for (array) |_| {
9492 counter += 1;
9593 continue :outer;
9694 }
test/compare_output.zig+15-15
......@@ -19,7 +19,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
1919 \\
2020 \\pub fn main() void {
2121 \\ privateFunction();
22 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
22 \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
2323 \\ stdout.print("OK 2\n") catch unreachable;
2424 \\}
2525 \\
......@@ -34,7 +34,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
3434 \\// purposefully conflicting function with main.zig
3535 \\// but it's private so it should be OK
3636 \\fn privateFunction() void {
37 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
37 \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
3838 \\ stdout.print("OK 1\n") catch unreachable;
3939 \\}
4040 \\
......@@ -60,7 +60,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
6060 tc.addSourceFile("foo.zig",
6161 \\use @import("std").io;
6262 \\pub fn foo_function() void {
63 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
63 \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
6464 \\ stdout.print("OK\n") catch unreachable;
6565 \\}
6666 );
......@@ -71,7 +71,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
7171 \\
7272 \\pub fn bar_function() void {
7373 \\ if (foo_function()) {
74 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
74 \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
7575 \\ stdout.print("OK\n") catch unreachable;
7676 \\ }
7777 \\}
......@@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
103103 \\pub const a_text = "OK\n";
104104 \\
105105 \\pub fn ok() void {
106 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
106 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
107107 \\ stdout.print(b_text) catch unreachable;
108108 \\}
109109 );
......@@ -121,7 +121,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
121121 \\const io = @import("std").io;
122122 \\
123123 \\pub fn main() void {
124 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
124 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
125125 \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
126126 \\}
127127 , "Hello, world!\n0012 012 a\n");
......@@ -274,7 +274,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
274274 \\ var x_local : i32 = print_ok(x);
275275 \\}
276276 \\fn print_ok(val: @typeOf(x)) @typeOf(foo) {
277 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
277 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
278278 \\ stdout.print("OK\n") catch unreachable;
279279 \\ return 0;
280280 \\}
......@@ -356,7 +356,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
356356 \\pub fn main() void {
357357 \\ const bar = Bar {.field2 = 13,};
358358 \\ const foo = Foo {.field1 = bar,};
359 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
359 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
360360 \\ if (!foo.method()) {
361361 \\ stdout.print("BAD\n") catch unreachable;
362362 \\ }
......@@ -370,7 +370,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
370370 cases.add("defer with only fallthrough",
371371 \\const io = @import("std").io;
372372 \\pub fn main() void {
373 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
373 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
374374 \\ stdout.print("before\n") catch unreachable;
375375 \\ defer stdout.print("defer1\n") catch unreachable;
376376 \\ defer stdout.print("defer2\n") catch unreachable;
......@@ -383,7 +383,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
383383 \\const io = @import("std").io;
384384 \\const os = @import("std").os;
385385 \\pub fn main() void {
386 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
386 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
387387 \\ stdout.print("before\n") catch unreachable;
388388 \\ defer stdout.print("defer1\n") catch unreachable;
389389 \\ defer stdout.print("defer2\n") catch unreachable;
......@@ -400,7 +400,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
400400 \\ do_test() catch return;
401401 \\}
402402 \\fn do_test() !void {
403 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
403 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
404404 \\ stdout.print("before\n") catch unreachable;
405405 \\ defer stdout.print("defer1\n") catch unreachable;
406406 \\ errdefer stdout.print("deferErr\n") catch unreachable;
......@@ -419,7 +419,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
419419 \\ do_test() catch return;
420420 \\}
421421 \\fn do_test() !void {
422 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
422 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
423423 \\ stdout.print("before\n") catch unreachable;
424424 \\ defer stdout.print("defer1\n") catch unreachable;
425425 \\ errdefer stdout.print("deferErr\n") catch unreachable;
......@@ -436,7 +436,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
436436 \\const io = @import("std").io;
437437 \\
438438 \\pub fn main() void {
439 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
439 \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
440440 \\ stdout.print(foo_txt) catch unreachable;
441441 \\}
442442 , "1234\nabcd\n");
......@@ -456,7 +456,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
456456 \\pub fn main() !void {
457457 \\ var args_it = os.args();
458458 \\ var stdout_file = try io.getStdOut();
459 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
459 \\ var stdout_adapter = io.FileOutStream.init(stdout_file);
460460 \\ const stdout = &stdout_adapter.stream;
461461 \\ var index: usize = 0;
462462 \\ _ = args_it.skip();
......@@ -497,7 +497,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
497497 \\pub fn main() !void {
498498 \\ var args_it = os.args();
499499 \\ var stdout_file = try io.getStdOut();
500 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
500 \\ var stdout_adapter = io.FileOutStream.init(stdout_file);
501501 \\ const stdout = &stdout_adapter.stream;
502502 \\ var index: usize = 0;
503503 \\ _ = args_it.skip();
test/compile_errors.zig+43
......@@ -1,6 +1,49 @@
11const tests = @import("tests.zig");
22
33pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "switch with invalid expression parameter",
6 \\export fn entry() void {
7 \\ Test(i32);
8 \\}
9 \\fn Test(comptime T: type) void {
10 \\ const x = switch (T) {
11 \\ []u8 => |x| 123,
12 \\ i32 => |x| 456,
13 \\ else => unreachable,
14 \\ };
15 \\}
16 ,
17 ".tmp_source.zig:7:17: error: switch on type 'type' provides no expression parameter",
18 );
19
20 cases.add(
21 "function protoype with no body",
22 \\fn foo() void;
23 \\export fn entry() void {
24 \\ foo();
25 \\}
26 ,
27 ".tmp_source.zig:1:1: error: non-extern function has no body",
28 );
29
30 cases.add(
31 "@typeInfo causing depend on itself compile error",
32 \\const start = struct {
33 \\ fn crash() bug() {
34 \\ return bug;
35 \\ }
36 \\};
37 \\fn bug() void {
38 \\ _ = @typeInfo(start).Struct;
39 \\}
40 \\export fn entry() void {
41 \\ var boom = start.crash();
42 \\}
43 ,
44 ".tmp_source.zig:2:5: error: 'crash' depends on itself",
45 );
46
447 cases.add(
548 "@handle() called outside of function definition",
649 \\var handle_undef: promise = undefined;
test/tests.zig+6-6
......@@ -263,8 +263,8 @@ pub const CompareOutputContext = struct {
263263 var stdout = Buffer.initNull(b.allocator);
264264 var stderr = Buffer.initNull(b.allocator);
265265
266 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
267 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
266 var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
267 var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
268268
269269 stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
270270 stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
......@@ -578,8 +578,8 @@ pub const CompileErrorContext = struct {
578578 var stdout_buf = Buffer.initNull(b.allocator);
579579 var stderr_buf = Buffer.initNull(b.allocator);
580580
581 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
582 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
581 var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
582 var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
583583
584584 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
585585 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
......@@ -842,8 +842,8 @@ pub const TranslateCContext = struct {
842842 var stdout_buf = Buffer.initNull(b.allocator);
843843 var stderr_buf = Buffer.initNull(b.allocator);
844844
845 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
846 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
845 var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
846 var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
847847
848848 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
849849 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;