diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-10-08 15:47:45 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-10-08 15:47:45 -0700 |
| commit | 8b7539bd9554d25599ccff6f0e9ea79aa051946b (patch) | |
| tree | 9157fafcbe37f904346fd73042be1d1d299d4da2 /lib/std | |
| parent | b5a36f676b1fd69f195d9f1eb6e35f3eb2f15946 (diff) | |
| parent | b02341d6f58e0b8a87fc2ab589dcfd85e5dc96cd (diff) | |
| download | zig-8b7539bd9554d25599ccff6f0e9ea79aa051946b.tar.gz zig-8b7539bd9554d25599ccff6f0e9ea79aa051946b.zip | |
Merge remote-tracking branch 'origin/master' into llvm11
Conflicts:
src/clang.zig
Master branch renamed an enum; this branch gave it an explicit tag type
and explicitly initialized values. This commit combines the changes
together.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/builtin.zig | 9 | ||||
| -rw-r--r-- | lib/std/c.zig | 4 | ||||
| -rw-r--r-- | lib/std/crypto/aes.zig | 10 | ||||
| -rw-r--r-- | lib/std/crypto/aes/aesni.zig | 1 | ||||
| -rw-r--r-- | lib/std/crypto/aes/armcrypto.zig | 490 | ||||
| -rw-r--r-- | lib/std/crypto/ghash.zig | 20 | ||||
| -rw-r--r-- | lib/std/event.zig | 2 | ||||
| -rw-r--r-- | lib/std/event/loop.zig | 2 | ||||
| -rw-r--r-- | lib/std/event/wait_group.zig | 120 | ||||
| -rw-r--r-- | lib/std/fmt.zig | 7 | ||||
| -rw-r--r-- | lib/std/math.zig | 13 | ||||
| -rw-r--r-- | lib/std/os/linux/bpf/kern.zig | 50 | ||||
| -rw-r--r-- | lib/std/os/uefi.zig | 6 | ||||
| -rw-r--r-- | lib/std/os/uefi/protocols/hii.zig | 2 | ||||
| -rw-r--r-- | lib/std/os/windows/bits.zig | 30 | ||||
| -rw-r--r-- | lib/std/os/windows/ws2_32.zig | 2 | ||||
| -rw-r--r-- | lib/std/zig/ast.zig | 2 | ||||
| -rw-r--r-- | lib/std/zig/parse.zig | 3 | ||||
| -rw-r--r-- | lib/std/zig/render.zig | 14 | ||||
| -rw-r--r-- | lib/std/zig/tokenizer.zig | 3 |
20 files changed, 732 insertions, 58 deletions
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 68bbbe3b2d..8543461f33 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -151,7 +151,6 @@ pub const Mode = enum { pub const CallingConvention = enum { Unspecified, C, - Cold, Naked, Async, Interrupt, @@ -199,7 +198,7 @@ pub const TypeInfo = union(enum) { Union: Union, Fn: Fn, BoundFn: Fn, - Opaque: void, + Opaque: Opaque, Frame: Frame, AnyFrame: AnyFrame, Vector: Vector, @@ -362,6 +361,12 @@ pub const TypeInfo = union(enum) { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. + pub const Opaque = struct { + decls: []const Declaration, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. pub const Frame = struct { function: anytype, }; diff --git a/lib/std/c.zig b/lib/std/c.zig index 7cfc44714f..a75fcaa84b 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -329,8 +329,8 @@ pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int; pub extern "c" fn pthread_cond_broadcast(cond: *pthread_cond_t) c_int; pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int; -pub const pthread_t = *@Type(.Opaque); -pub const FILE = @Type(.Opaque); +pub const pthread_t = *opaque {}; +pub const FILE = opaque {}; pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void; pub extern "c" fn dlclose(handle: *c_void) c_int; diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig index 7c509b297f..9040524939 100644 --- a/lib/std/crypto/aes.zig +++ b/lib/std/crypto/aes.zig @@ -10,7 +10,15 @@ const builtin = std.builtin; const has_aesni = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .aes); const has_avx = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .avx); -const impl = if (std.Target.current.cpu.arch == .x86_64 and has_aesni and has_avx) @import("aes/aesni.zig") else @import("aes/soft.zig"); +const has_armaes = comptime std.Target.aarch64.featureSetHas(std.Target.current.cpu.features, .aes); +const impl = if (std.Target.current.cpu.arch == .x86_64 and has_aesni and has_avx) impl: { + break :impl @import("aes/aesni.zig"); +} else if (std.Target.current.cpu.arch == .aarch64 and has_armaes) +impl: { + break :impl @import("aes/armcrypto.zig"); +} else impl: { + break :impl @import("aes/soft.zig"); +}; pub const Block = impl.Block; pub const AESEncryptCtx = impl.AESEncryptCtx; diff --git a/lib/std/crypto/aes/aesni.zig b/lib/std/crypto/aes/aesni.zig index 47dd029bec..c1c077a988 100644 --- a/lib/std/crypto/aes/aesni.zig +++ b/lib/std/crypto/aes/aesni.zig @@ -3,7 +3,6 @@ // This file is part of [zig](https://ziglang.org/), which is MIT licensed. // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. -// Based on Go stdlib implementation const std = @import("../../std.zig"); const mem = std.mem; diff --git a/lib/std/crypto/aes/armcrypto.zig b/lib/std/crypto/aes/armcrypto.zig new file mode 100644 index 0000000000..b1d8252c94 --- /dev/null +++ b/lib/std/crypto/aes/armcrypto.zig @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("../../std.zig"); +const mem = std.mem; +const debug = std.debug; +const Vector = std.meta.Vector; + +const BlockVec = Vector(2, u64); + +/// A single AES block. +pub const Block = struct { + pub const block_size: usize = 16; + + /// Internal representation of a block. + repr: BlockVec, + + /// Convert a byte sequence into an internal representation. + pub inline fn fromBytes(bytes: *const [16]u8) Block { + const repr = mem.bytesToValue(BlockVec, bytes); + return Block{ .repr = repr }; + } + + /// Convert the internal representation of a block into a byte sequence. + pub inline fn toBytes(block: Block) [16]u8 { + return mem.toBytes(block.repr); + } + + /// XOR the block with a byte sequence. + pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 { + const x = block.repr ^ fromBytes(bytes).repr; + return mem.toBytes(x); + } + + const zero = Vector(2, u64){ 0, 0 }; + + /// Encrypt a block with a round key. + pub inline fn encrypt(block: Block, round_key: Block) Block { + return Block{ + .repr = asm ( + \\ mov %[out].16b, %[in].16b + \\ aese %[out].16b, %[zero].16b + \\ aesmc %[out].16b, %[out].16b + \\ eor %[out].16b, %[out].16b, %[rk].16b + : [out] "=&x" (-> BlockVec) + : [in] "x" (block.repr), + [rk] "x" (round_key.repr), + [zero] "x" (zero) + ), + }; + } + + /// Encrypt a block with the last round key. + pub inline fn encryptLast(block: Block, round_key: Block) Block { + return Block{ + .repr = asm ( + \\ mov %[out].16b, %[in].16b + \\ aese %[out].16b, %[zero].16b + \\ eor %[out].16b, %[out].16b, %[rk].16b + : [out] "=&x" (-> BlockVec) + : [in] "x" (block.repr), + [rk] "x" (round_key.repr), + [zero] "x" (zero) + ), + }; + } + + /// Decrypt a block with a round key. + pub inline fn decrypt(block: Block, inv_round_key: Block) Block { + return Block{ + .repr = asm ( + \\ mov %[out].16b, %[in].16b + \\ aesd %[out].16b, %[zero].16b + \\ aesimc %[out].16b, %[out].16b + \\ eor %[out].16b, %[out].16b, %[rk].16b + : [out] "=&x" (-> BlockVec) + : [in] "x" (block.repr), + [rk] "x" (inv_round_key.repr), + [zero] "x" (zero) + ), + }; + } + + /// Decrypt a block with the last round key. + pub inline fn decryptLast(block: Block, inv_round_key: Block) Block { + return Block{ + .repr = asm ( + \\ mov %[out].16b, %[in].16b + \\ aesd %[out].16b, %[zero].16b + \\ eor %[out].16b, %[out].16b, %[rk].16b + : [out] "=&x" (-> BlockVec) + : [in] "x" (block.repr), + [rk] "x" (inv_round_key.repr), + [zero] "x" (zero) + ), + }; + } + + /// Apply the bitwise XOR operation to the content of two blocks. + pub inline fn xorBlocks(block1: Block, block2: Block) Block { + return Block{ .repr = block1.repr ^ block2.repr }; + } + + /// Apply the bitwise AND operation to the content of two blocks. + pub inline fn andBlocks(block1: Block, block2: Block) Block { + return Block{ .repr = block1.repr & block2.repr }; + } + + /// Apply the bitwise OR operation to the content of two blocks. + pub inline fn orBlocks(block1: Block, block2: Block) Block { + return Block{ .repr = block1.repr | block2.repr }; + } + + /// Perform operations on multiple blocks in parallel. + pub const parallel = struct { + /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation. + pub const optimal_parallel_blocks = 8; + + /// Encrypt multiple blocks in parallel, each their own round key. + pub inline fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block { + comptime var i = 0; + var out: [count]Block = undefined; + inline while (i < count) : (i += 1) { + out[i] = blocks[i].encrypt(round_keys[i]); + } + return out; + } + + /// Decrypt multiple blocks in parallel, each their own round key. + pub inline fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block { + comptime var i = 0; + var out: [count]Block = undefined; + inline while (i < count) : (i += 1) { + out[i] = blocks[i].decrypt(round_keys[i]); + } + return out; + } + + /// Encrypt multple blocks in parallel with the same round key. + pub inline fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + comptime var i = 0; + var out: [count]Block = undefined; + inline while (i < count) : (i += 1) { + out[i] = blocks[i].encrypt(round_key); + } + return out; + } + + /// Decrypt multple blocks in parallel with the same round key. + pub inline fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + comptime var i = 0; + var out: [count]Block = undefined; + inline while (i < count) : (i += 1) { + out[i] = blocks[i].decrypt(round_key); + } + return out; + } + + /// Encrypt multple blocks in parallel with the same last round key. + pub inline fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + comptime var i = 0; + var out: [count]Block = undefined; + inline while (i < count) : (i += 1) { + out[i] = blocks[i].encryptLast(round_key); + } + return out; + } + + /// Decrypt multple blocks in parallel with the same last round key. + pub inline fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + comptime var i = 0; + var out: [count]Block = undefined; + inline while (i < count) : (i += 1) { + out[i] = blocks[i].decryptLast(round_key); + } + return out; + } + }; +}; + +fn KeySchedule(comptime AES: type) type { + std.debug.assert(AES.rounds == 10 or AES.rounds == 14); + const rounds = AES.rounds; + + return struct { + const Self = @This(); + + const zero = Vector(2, u64){ 0, 0 }; + const mask1 = @Vector(16, u8){ 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12 }; + const mask2 = @Vector(16, u8){ 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15 }; + + round_keys: [rounds + 1]Block, + + fn drc128(comptime rc: u8, t: BlockVec) BlockVec { + var v1: BlockVec = undefined; + var v2: BlockVec = undefined; + var v3: BlockVec = undefined; + var v4: BlockVec = undefined; + + return asm ( + \\ movi %[v2].4s, %[rc] + \\ tbl %[v4].16b, {%[t].16b}, %[mask].16b + \\ ext %[r].16b, %[zero].16b, %[t].16b, #12 + \\ aese %[v4].16b, %[zero].16b + \\ eor %[v2].16b, %[r].16b, %[v2].16b + \\ ext %[r].16b, %[zero].16b, %[r].16b, #12 + \\ eor %[v1].16b, %[v2].16b, %[t].16b + \\ ext %[v3].16b, %[zero].16b, %[r].16b, #12 + \\ eor %[v1].16b, %[v1].16b, %[r].16b + \\ eor %[r].16b, %[v1].16b, %[v3].16b + \\ eor %[r].16b, %[r].16b, %[v4].16b + : [r] "=&x" (-> BlockVec), + [v1] "=&x" (v1), + [v2] "=&x" (v2), + [v3] "=&x" (v3), + [v4] "=&x" (v4) + : [rc] "N" (rc), + [t] "x" (t), + [zero] "x" (zero), + [mask] "x" (mask1) + ); + } + + fn drc256(comptime second: bool, comptime rc: u8, t: BlockVec, tx: BlockVec) BlockVec { + var v1: BlockVec = undefined; + var v2: BlockVec = undefined; + var v3: BlockVec = undefined; + var v4: BlockVec = undefined; + + return asm ( + \\ movi %[v2].4s, %[rc] + \\ tbl %[v4].16b, {%[t].16b}, %[mask].16b + \\ ext %[r].16b, %[zero].16b, %[tx].16b, #12 + \\ aese %[v4].16b, %[zero].16b + \\ eor %[v1].16b, %[tx].16b, %[r].16b + \\ ext %[r].16b, %[zero].16b, %[r].16b, #12 + \\ eor %[v1].16b, %[v1].16b, %[r].16b + \\ ext %[v3].16b, %[zero].16b, %[r].16b, #12 + \\ eor %[v1].16b, %[v1].16b, %[v2].16b + \\ eor %[v1].16b, %[v1].16b, %[v3].16b + \\ eor %[r].16b, %[v1].16b, %[v4].16b + : [r] "=&x" (-> BlockVec), + [v1] "=&x" (v1), + [v2] "=&x" (v2), + [v3] "=&x" (v3), + [v4] "=&x" (v4) + : [rc] "N" (if (second) @as(u8, 0) else rc), + [t] "x" (t), + [tx] "x" (tx), + [zero] "x" (zero), + [mask] "x" (if (second) mask2 else mask1) + ); + } + + fn expand128(t1: *Block) Self { + var round_keys: [11]Block = undefined; + const rcs = [_]u8{ 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 }; + inline for (rcs) |rc, round| { + round_keys[round] = t1.*; + t1.repr = drc128(rc, t1.repr); + } + round_keys[rcs.len] = t1.*; + return Self{ .round_keys = round_keys }; + } + + fn expand256(t1: *Block, t2: *Block) Self { + var round_keys: [15]Block = undefined; + const rcs = [_]u8{ 1, 2, 4, 8, 16, 32 }; + round_keys[0] = t1.*; + inline for (rcs) |rc, round| { + round_keys[round * 2 + 1] = t2.*; + t1.repr = drc256(false, rc, t2.repr, t1.repr); + round_keys[round * 2 + 2] = t1.*; + t2.repr = drc256(true, rc, t1.repr, t2.repr); + } + round_keys[rcs.len * 2 + 1] = t2.*; + t1.repr = drc256(false, 64, t2.repr, t1.repr); + round_keys[rcs.len * 2 + 2] = t1.*; + return Self{ .round_keys = round_keys }; + } + + /// Invert the key schedule. + pub fn invert(key_schedule: Self) Self { + const round_keys = &key_schedule.round_keys; + var inv_round_keys: [rounds + 1]Block = undefined; + inv_round_keys[0] = round_keys[rounds]; + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + inv_round_keys[i] = Block{ + .repr = asm ( + \\ aesimc %[inv_rk].16b, %[rk].16b + : [inv_rk] "=x" (-> BlockVec) + : [rk] "x" (round_keys[rounds - i].repr) + ), + }; + } + inv_round_keys[rounds] = round_keys[0]; + return Self{ .round_keys = inv_round_keys }; + } + }; +} + +/// A context to perform encryption using the standard AES key schedule. +pub fn AESEncryptCtx(comptime AES: type) type { + std.debug.assert(AES.key_bits == 128 or AES.key_bits == 256); + const rounds = AES.rounds; + + return struct { + const Self = @This(); + pub const block = AES.block; + pub const block_size = block.block_size; + key_schedule: KeySchedule(AES), + + /// Create a new encryption context with the given key. + pub fn init(key: [AES.key_bits / 8]u8) Self { + var t1 = Block.fromBytes(key[0..16]); + const key_schedule = if (AES.key_bits == 128) ks: { + break :ks KeySchedule(AES).expand128(&t1); + } else ks: { + var t2 = Block.fromBytes(key[16..32]); + break :ks KeySchedule(AES).expand256(&t1, &t2); + }; + return Self{ + .key_schedule = key_schedule, + }; + } + + /// Encrypt a single block. + pub fn encrypt(ctx: Self, dst: *[16]u8, src: *const [16]u8) void { + const round_keys = ctx.key_schedule.round_keys; + var t = Block.fromBytes(src).xorBlocks(round_keys[0]); + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + t = t.encrypt(round_keys[i]); + } + t = t.encryptLast(round_keys[rounds]); + dst.* = t.toBytes(); + } + + /// Encrypt+XOR a single block. + pub fn xor(ctx: Self, dst: *[16]u8, src: *const [16]u8, counter: [16]u8) void { + const round_keys = ctx.key_schedule.round_keys; + var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]); + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + t = t.encrypt(round_keys[i]); + } + t = t.encryptLast(round_keys[rounds]); + dst.* = t.xorBytes(src); + } + + /// Encrypt multiple blocks, possibly leveraging parallelization. + pub fn encryptWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8) void { + const round_keys = ctx.key_schedule.round_keys; + var ts: [count]Block = undefined; + comptime var j = 0; + inline while (j < count) : (j += 1) { + ts[j] = Block.fromBytes(src[j * 16 .. j * 16 + 16][0..16]).xorBlocks(round_keys[0]); + } + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + ts = Block.parallel.encryptWide(count, ts, round_keys[i]); + } + i = 1; + inline while (i < count) : (i += 1) { + ts = Block.parallel.encryptLastWide(count, ts, round_keys[i]); + } + j = 0; + inline while (j < count) : (j += 1) { + dst[16 * j .. 16 * j + 16].* = ts[j].toBytes(); + } + } + + /// Encrypt+XOR multiple blocks, possibly leveraging parallelization. + pub fn xorWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8, counters: [16 * count]u8) void { + const round_keys = ctx.key_schedule.round_keys; + var ts: [count]Block = undefined; + comptime var j = 0; + inline while (j < count) : (j += 1) { + ts[j] = Block.fromBytes(counters[j * 16 .. j * 16 + 16][0..16]).xorBlocks(round_keys[0]); + } + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + ts = Block.parallel.encryptWide(count, ts, round_keys[i]); + } + ts = Block.parallel.encryptLastWide(count, ts, round_keys[i]); + j = 0; + inline while (j < count) : (j += 1) { + dst[16 * j .. 16 * j + 16].* = ts[j].xorBytes(src[16 * j .. 16 * j + 16]); + } + } + }; +} + +/// A context to perform decryption using the standard AES key schedule. +pub fn AESDecryptCtx(comptime AES: type) type { + std.debug.assert(AES.key_bits == 128 or AES.key_bits == 256); + const rounds = AES.rounds; + + return struct { + const Self = @This(); + pub const block = AES.block; + pub const block_size = block.block_size; + key_schedule: KeySchedule(AES), + + /// Create a decryption context from an existing encryption context. + pub fn initFromEnc(ctx: AESEncryptCtx(AES)) Self { + return Self{ + .key_schedule = ctx.key_schedule.invert(), + }; + } + + /// Create a new decryption context with the given key. + pub fn init(key: [AES.key_bits / 8]u8) Self { + const enc_ctx = AESEncryptCtx(AES).init(key); + return initFromEnc(enc_ctx); + } + + /// Decrypt a single block. + pub fn decrypt(ctx: Self, dst: *[16]u8, src: *const [16]u8) void { + const inv_round_keys = ctx.key_schedule.round_keys; + var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]); + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + t = t.decrypt(inv_round_keys[i]); + } + t = t.decryptLast(inv_round_keys[rounds]); + dst.* = t.toBytes(); + } + + /// Decrypt multiple blocks, possibly leveraging parallelization. + pub fn decryptWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8) void { + const inv_round_keys = ctx.key_schedule.round_keys; + var ts: [count]Block = undefined; + comptime var j = 0; + inline while (j < count) : (j += 1) { + ts[j] = Block.fromBytes(src[j * 16 .. j * 16 + 16][0..16]).xorBlocks(inv_round_keys[0]); + } + comptime var i = 1; + inline while (i < rounds) : (i += 1) { + ts = Block.parallel.decryptWide(count, ts, inv_round_keys[i]); + } + i = 1; + inline while (i < count) : (i += 1) { + ts = Block.parallel.decryptLastWide(count, ts, inv_round_keys[i]); + } + j = 0; + inline while (j < count) : (j += 1) { + dst[16 * j .. 16 * j + 16].* = ts[j].toBytes(); + } + } + }; +} + +/// AES-128 with the standard key schedule. +pub const AES128 = struct { + pub const key_bits: usize = 128; + pub const rounds = ((key_bits - 64) / 32 + 8); + pub const block = Block; + + /// Create a new context for encryption. + pub fn initEnc(key: [key_bits / 8]u8) AESEncryptCtx(AES128) { + return AESEncryptCtx(AES128).init(key); + } + + /// Create a new context for decryption. + pub fn initDec(key: [key_bits / 8]u8) AESDecryptCtx(AES128) { + return AESDecryptCtx(AES128).init(key); + } +}; + +/// AES-256 with the standard key schedule. +pub const AES256 = struct { + pub const key_bits: usize = 256; + pub const rounds = ((key_bits - 64) / 32 + 8); + pub const block = Block; + + /// Create a new context for encryption. + pub fn initEnc(key: [key_bits / 8]u8) AESEncryptCtx(AES256) { + return AESEncryptCtx(AES256).init(key); + } + + /// Create a new context for decryption. + pub fn initDec(key: [key_bits / 8]u8) AESDecryptCtx(AES256) { + return AESDecryptCtx(AES256).init(key); + } +}; diff --git a/lib/std/crypto/ghash.zig b/lib/std/crypto/ghash.zig index 04bc6a8275..83446f280b 100644 --- a/lib/std/crypto/ghash.zig +++ b/lib/std/crypto/ghash.zig @@ -105,6 +105,17 @@ pub const Ghash = struct { return product[0]; } + inline fn clmul_pmull(x: u64, y: u64) u64 { + const Vector = std.meta.Vector; + const product = asm ( + \\ pmull %[out].1q, %[x].1d, %[y].1d + : [out] "=w" (-> Vector(2, u64)) + : [x] "w" (@bitCast(Vector(2, u64), @as(u128, x))), + [y] "w" (@bitCast(Vector(2, u64), @as(u128, y))) + ); + return product[0]; + } + fn clmul_soft(x: u64, y: u64) u64 { const x0 = x & 0x1111111111111111; const x1 = x & 0x2222222222222222; @@ -127,7 +138,14 @@ pub const Ghash = struct { const has_pclmul = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .pclmul); const has_avx = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .avx); - const clmul = if (std.Target.current.cpu.arch == .x86_64 and has_pclmul and has_avx) clmul_pclmul else clmul_soft; + const has_armaes = comptime std.Target.aarch64.featureSetHas(std.Target.current.cpu.features, .aes); + const clmul = if (std.Target.current.cpu.arch == .x86_64 and has_pclmul and has_avx) impl: { + break :impl clmul_pclmul; + } else if (std.Target.current.cpu.arch == .aarch64 and has_armaes) impl: { + break :impl clmul_pmull; + } else impl: { + break :impl clmul_soft; + }; fn blocks(st: *Ghash, msg: []const u8) void { assert(msg.len % 16 == 0); // GHASH blocks() expects full blocks diff --git a/lib/std/event.zig b/lib/std/event.zig index 42f3176a1b..eeee8be65b 100644 --- a/lib/std/event.zig +++ b/lib/std/event.zig @@ -12,6 +12,7 @@ pub const Locked = @import("event/locked.zig").Locked; pub const RwLock = @import("event/rwlock.zig").RwLock; pub const RwLocked = @import("event/rwlocked.zig").RwLocked; pub const Loop = @import("event/loop.zig").Loop; +pub const WaitGroup = @import("event/WaitGroup.zig").WaitGroup; test "import event tests" { _ = @import("event/channel.zig"); @@ -23,4 +24,5 @@ test "import event tests" { _ = @import("event/rwlock.zig"); _ = @import("event/rwlocked.zig"); _ = @import("event/loop.zig"); + _ = @import("event/wait_group.zig"); } diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 226d5f1d52..a064f711e2 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -660,9 +660,11 @@ pub const Loop = struct { const Wrapper = struct { const Args = @TypeOf(args); fn run(func_args: Args, loop: *Loop, allocator: *mem.Allocator) void { + loop.beginOneEvent(); loop.yield(); const result = @call(.{}, func, func_args); suspend { + loop.finishOneEvent(); allocator.destroy(@frame()); } } diff --git a/lib/std/event/wait_group.zig b/lib/std/event/wait_group.zig new file mode 100644 index 0000000000..f0222e7644 --- /dev/null +++ b/lib/std/event/wait_group.zig @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const Loop = std.event.Loop; + +/// A WaitGroup keeps track and waits for a group of async tasks to finish. +/// Call `begin` when creating new tasks, and have tasks call `finish` when done. +/// You can provide a count for both operations to perform them in bulk. +/// Call `wait` to suspend until all tasks are completed. +/// Multiple waiters are supported. +/// +/// WaitGroup is an instance of WaitGroupGeneric, which takes in a bitsize +/// for the internal counter. WaitGroup defaults to a `usize` counter. +/// It's also possible to define a max value for the counter so that +/// `begin` will return error.Overflow when the limit is reached, even +/// if the integer type has not has not overflowed. +/// By default `max_value` is set to std.math.maxInt(CounterType). +pub const WaitGroup = WaitGroupGeneric(std.meta.bitCount(usize)); + +pub fn WaitGroupGeneric(comptime counter_size: u16) type { + const CounterType = std.meta.Int(false, counter_size); + + const global_event_loop = Loop.instance orelse + @compileError("std.event.WaitGroup currently only works with event-based I/O"); + + return struct { + counter: CounterType = 0, + max_counter: CounterType = std.math.maxInt(CounterType), + mutex: std.Mutex = .{}, + waiters: ?*Waiter = null, + const Waiter = struct { + next: ?*Waiter, + tail: *Waiter, + node: Loop.NextTickNode, + }; + + const Self = @This(); + pub fn begin(self: *Self, count: CounterType) error{Overflow}!void { + const held = self.mutex.acquire(); + defer held.release(); + + const new_counter = try std.math.add(CounterType, self.counter, count); + if (new_counter > self.max_counter) return error.Overflow; + self.counter = new_counter; + } + + pub fn finish(self: *Self, count: CounterType) void { + var waiters = blk: { + const held = self.mutex.acquire(); + defer held.release(); + self.counter = std.math.sub(CounterType, self.counter, count) catch unreachable; + if (self.counter == 0) { + const temp = self.waiters; + self.waiters = null; + break :blk temp; + } + break :blk null; + }; + + // We don't need to hold the lock to reschedule any potential waiter. + while (waiters) |w| { + const temp_w = w; + waiters = w.next; + global_event_loop.onNextTick(&temp_w.node); + } + } + + pub fn wait(self: *Self) void { + const held = self.mutex.acquire(); + + if (self.counter == 0) { + held.release(); + return; + } + + var self_waiter: Waiter = undefined; + self_waiter.node.data = @frame(); + if (self.waiters) |head| { + head.tail.next = &self_waiter; + head.tail = &self_waiter; + } else { + self.waiters = &self_waiter; + self_waiter.tail = &self_waiter; + self_waiter.next = null; + } + suspend { + held.release(); + } + } + }; +} + +test "basic WaitGroup usage" { + if (!std.io.is_async) return error.SkipZigTest; + + // TODO https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + + // TODO https://github.com/ziglang/zig/issues/3251 + if (builtin.os.tag == .freebsd) return error.SkipZigTest; + + var initial_wg = WaitGroup{}; + var final_wg = WaitGroup{}; + + try initial_wg.begin(1); + try final_wg.begin(1); + var task_frame = async task(&initial_wg, &final_wg); + initial_wg.finish(1); + final_wg.wait(); + await task_frame; +} + +fn task(wg_i: *WaitGroup, wg_f: *WaitGroup) void { + wg_i.wait(); + wg_f.finish(1); +} diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index ab2cc1577d..ce90f67585 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1181,13 +1181,16 @@ fn bufPrintIntToSlice(buf: []u8, value: anytype, base: u8, uppercase: bool, opti return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; } -pub fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args)]u8 { - comptime var buf: [count(fmt, args)]u8 = undefined; +pub fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args):0]u8 { + comptime var buf: [count(fmt, args):0]u8 = undefined; _ = bufPrint(&buf, fmt, args) catch unreachable; + buf[buf.len] = 0; return &buf; } test "comptimePrint" { + @setEvalBranchQuota(2000); + std.testing.expectEqual(*const [3:0]u8, @TypeOf(comptime comptimePrint("{}", .{100}))); std.testing.expectEqualSlices(u8, "100", comptime comptimePrint("{}", .{100})); } diff --git a/lib/std/math.zig b/lib/std/math.zig index f05c967b2d..a1db1309c9 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -110,7 +110,12 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { } pub fn doNotOptimizeAway(value: anytype) void { - mem.doNotOptimizeAway(value); + // TODO: use @declareSideEffect() when it is available. + // https://github.com/ziglang/zig/issues/6168 + const T = @TypeOf(value); + var x: T = undefined; + const p = @ptrCast(*volatile T, &x); + p.* = x; } pub fn raiseInvalid() void { @@ -1131,3 +1136,9 @@ test "compare between signed and unsigned" { testing.expect(!compare(@as(u8, 255), .eq, @as(i8, -1))); testing.expect(compare(@as(u8, 1), .eq, @as(u8, 1))); } + +test "math.comptime" { + comptime const v = sin(@as(f32, 1)) + ln(@as(f32, 5)); + testing.expect(v == sin(@as(f32, 1)) + ln(@as(f32, 5))); +} + diff --git a/lib/std/os/linux/bpf/kern.zig b/lib/std/os/linux/bpf/kern.zig index 3bd605301a..a2e9d36aa1 100644 --- a/lib/std/os/linux/bpf/kern.zig +++ b/lib/std/os/linux/bpf/kern.zig @@ -12,28 +12,28 @@ const in_bpf_program = switch (std.builtin.arch) { pub const helpers = if (in_bpf_program) @import("helpers.zig") else struct {}; -pub const BpfSock = @Type(.Opaque); -pub const BpfSockAddr = @Type(.Opaque); -pub const FibLookup = @Type(.Opaque); -pub const MapDef = @Type(.Opaque); -pub const PerfEventData = @Type(.Opaque); -pub const PerfEventValue = @Type(.Opaque); -pub const PidNsInfo = @Type(.Opaque); -pub const SeqFile = @Type(.Opaque); -pub const SkBuff = @Type(.Opaque); -pub const SkMsgMd = @Type(.Opaque); -pub const SkReusePortMd = @Type(.Opaque); -pub const Sock = @Type(.Opaque); -pub const SockAddr = @Type(.Opaque); -pub const SockOps = @Type(.Opaque); -pub const SockTuple = @Type(.Opaque); -pub const SpinLock = @Type(.Opaque); -pub const SysCtl = @Type(.Opaque); -pub const Tcp6Sock = @Type(.Opaque); -pub const TcpRequestSock = @Type(.Opaque); -pub const TcpSock = @Type(.Opaque); -pub const TcpTimewaitSock = @Type(.Opaque); -pub const TunnelKey = @Type(.Opaque); -pub const Udp6Sock = @Type(.Opaque); -pub const XdpMd = @Type(.Opaque); -pub const XfrmState = @Type(.Opaque); +pub const BpfSock = opaque {}; +pub const BpfSockAddr = opaque {}; +pub const FibLookup = opaque {}; +pub const MapDef = opaque {}; +pub const PerfEventData = opaque {}; +pub const PerfEventValue = opaque {}; +pub const PidNsInfo = opaque {}; +pub const SeqFile = opaque {}; +pub const SkBuff = opaque {}; +pub const SkMsgMd = opaque {}; +pub const SkReusePortMd = opaque {}; +pub const Sock = opaque {}; +pub const SockAddr = opaque {}; +pub const SockOps = opaque {}; +pub const SockTuple = opaque {}; +pub const SpinLock = opaque {}; +pub const SysCtl = opaque {}; +pub const Tcp6Sock = opaque {}; +pub const TcpRequestSock = opaque {}; +pub const TcpSock = opaque {}; +pub const TcpTimewaitSock = opaque {}; +pub const TunnelKey = opaque {}; +pub const Udp6Sock = opaque {}; +pub const XdpMd = opaque {}; +pub const XfrmState = opaque {}; diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 0127033db2..ba1544105c 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -17,7 +17,7 @@ pub var handle: Handle = undefined; pub var system_table: *tables.SystemTable = undefined; /// A handle to an event structure. -pub const Event = *@Type(.Opaque); +pub const Event = *opaque {}; /// GUIDs must be align(8) pub const Guid = extern struct { @@ -51,7 +51,7 @@ pub const Guid = extern struct { }; /// An EFI Handle represents a collection of related interfaces. -pub const Handle = *@Type(.Opaque); +pub const Handle = *opaque {}; /// This structure represents time information. pub const Time = extern struct { @@ -108,4 +108,4 @@ pub const TimeCapabilities = extern struct { }; /// File Handle as specified in the EFI Shell Spec -pub const FileHandle = *@Type(.Opaque); +pub const FileHandle = *opaque {}; diff --git a/lib/std/os/uefi/protocols/hii.zig b/lib/std/os/uefi/protocols/hii.zig index 960402828e..ed7c40d6ac 100644 --- a/lib/std/os/uefi/protocols/hii.zig +++ b/lib/std/os/uefi/protocols/hii.zig @@ -6,7 +6,7 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -pub const HIIHandle = *@Type(.Opaque); +pub const HIIHandle = *opaque {}; /// The header found at the start of each package. pub const HIIPackageHeader = packed struct { diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index d22f42d6e8..bf786d5c91 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -32,16 +32,16 @@ pub const UCHAR = u8; pub const FLOAT = f32; pub const HANDLE = *c_void; pub const HCRYPTPROV = ULONG_PTR; -pub const HBRUSH = *@Type(.Opaque); -pub const HCURSOR = *@Type(.Opaque); -pub const HICON = *@Type(.Opaque); -pub const HINSTANCE = *@Type(.Opaque); -pub const HMENU = *@Type(.Opaque); -pub const HMODULE = *@Type(.Opaque); -pub const HWND = *@Type(.Opaque); -pub const HDC = *@Type(.Opaque); -pub const HGLRC = *@Type(.Opaque); -pub const FARPROC = *@Type(.Opaque); +pub const HBRUSH = *opaque {}; +pub const HCURSOR = *opaque {}; +pub const HICON = *opaque {}; +pub const HINSTANCE = *opaque {}; +pub const HMENU = *opaque {}; +pub const HMODULE = *opaque {}; +pub const HWND = *opaque {}; +pub const HDC = *opaque {}; +pub const HGLRC = *opaque {}; +pub const FARPROC = *opaque {}; pub const INT = c_int; pub const LPBYTE = *BYTE; pub const LPCH = *CHAR; @@ -81,7 +81,7 @@ pub const WPARAM = usize; pub const LPARAM = ?*c_void; pub const LRESULT = ?*c_void; -pub const va_list = *@Type(.Opaque); +pub const va_list = *opaque {}; pub const TRUE = 1; pub const FALSE = 0; @@ -1175,10 +1175,10 @@ pub const UNICODE_STRING = extern struct { Buffer: [*]WCHAR, }; -const ACTIVATION_CONTEXT_DATA = @Type(.Opaque); -const ASSEMBLY_STORAGE_MAP = @Type(.Opaque); -const FLS_CALLBACK_INFO = @Type(.Opaque); -const RTL_BITMAP = @Type(.Opaque); +const ACTIVATION_CONTEXT_DATA = opaque {}; +const ASSEMBLY_STORAGE_MAP = opaque {}; +const FLS_CALLBACK_INFO = opaque {}; +const RTL_BITMAP = opaque {}; pub const PRTL_BITMAP = *RTL_BITMAP; const KAFFINITY = usize; diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index ac21b6ffc9..19bfc0d83e 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -5,7 +5,7 @@ // and substantial portions of the software. usingnamespace @import("bits.zig"); -pub const SOCKET = *@Type(.Opaque); +pub const SOCKET = *opaque {}; pub const INVALID_SOCKET = @intToPtr(SOCKET, ~@as(usize, 0)); pub const SOCKET_ERROR = -1; diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index d8943adde0..0973877aa8 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -288,7 +288,7 @@ pub const Error = union(enum) { pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{}'"); pub const ExpectedFn = SingleTokenError("Expected function, found '{}'"); pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{}'"); - pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', or '" ++ Token.Id.Keyword_enum.symbol() ++ "', found '{}'"); + pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{}'"); pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{}'"); pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{}'"); pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{}'"); diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 2af2ee4a45..467b06a5ca 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -2896,11 +2896,12 @@ const Parser = struct { /// <- KEYWORD_struct /// / KEYWORD_enum (LPAREN Expr RPAREN)? /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? + /// / KEYWORD_opaque fn parseContainerDeclType(p: *Parser) !?ContainerDeclType { const kind_token = p.nextToken(); const init_arg_expr = switch (p.token_ids[kind_token]) { - .Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} }, + .Keyword_struct, .Keyword_opaque => Node.ContainerDecl.InitArg{ .None = {} }, .Keyword_enum => blk: { if (p.eatToken(.LParen) != null) { const expr = try p.expectNode(parseExpr, .{ diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 67afbb77d9..8c8a2fc50b 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1492,7 +1492,19 @@ fn renderExpression( // TODO remove after 0.7.0 release if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@OpaqueType")) - return ais.writer().writeAll("@Type(.Opaque)"); + return ais.writer().writeAll("opaque {}"); + + // TODO remove after 0.7.0 release + { + const params = builtin_call.paramsConst(); + if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@Type") and + params.len == 1) + { + if (params[0].castTag(.EnumLiteral)) |enum_literal| + if (mem.eql(u8, tree.tokenSlice(enum_literal.name), "Opaque")) + return ais.writer().writeAll("opaque {}"); + } + } try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index e40483c022..c8f33dbfaa 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -47,6 +47,7 @@ pub const Token = struct { .{ "noinline", .Keyword_noinline }, .{ "nosuspend", .Keyword_nosuspend }, .{ "null", .Keyword_null }, + .{ "opaque", .Keyword_opaque }, .{ "or", .Keyword_or }, .{ "orelse", .Keyword_orelse }, .{ "packed", .Keyword_packed }, @@ -173,6 +174,7 @@ pub const Token = struct { Keyword_noinline, Keyword_nosuspend, Keyword_null, + Keyword_opaque, Keyword_or, Keyword_orelse, Keyword_packed, @@ -296,6 +298,7 @@ pub const Token = struct { .Keyword_noinline => "noinline", .Keyword_nosuspend => "nosuspend", .Keyword_null => "null", + .Keyword_opaque => "opaque", .Keyword_or => "or", .Keyword_orelse => "orelse", .Keyword_packed => "packed", |
