Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

SipHashStateless

siphash.SipHashStateless
fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type

File

lib/std/crypto/siphash.zig:43

Code

fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
    assert(T == u64 or T == u128);
    assert(c_rounds > 0 and d_rounds > 0);

    return struct {
        const Self = @This();
        const block_length = 64;
        const key_length = 16;

        v0: u64,
        v1: u64,
        v2: u64,
        v3: u64,
        msg_len: u8,

        fn init(key: *const [key_length]u8) Self {
            const k0 = mem.readInt(u64, key[0..8], .little);
            const k1 = mem.readInt(u64, key[8..16], .little);

            var d = Self{
                .v0 = k0 ^ 0x736f6d6570736575,
                .v1 = k1 ^ 0x646f72616e646f6d,
                .v2 = k0 ^ 0x6c7967656e657261,
                .v3 = k1 ^ 0x7465646279746573,
                .msg_len = 0,
            };

            if (T == u128) {
                d.v1 ^= 0xee;
            }

            return d;
        }

        fn update(self: *Self, b: []const u8) void {
            std.debug.assert(b.len % 8 == 0);

            var off: usize = 0;
            while (off < b.len) : (off += 8) {
                const blob = b[off..][0..8].*;
                @call(.always_inline, round, .{ self, blob });
            }

            self.msg_len +%= @as(u8, @truncate(b.len));
        }

        fn final(self: *Self, b: []const u8) T {
            std.debug.assert(b.len < 8);

            self.msg_len +%= @as(u8, @truncate(b.len));

            var buf: [8]u8 = @splat(0);
            @memcpy(buf[0..b.len], b);
            buf[7] = self.msg_len;
            self.round(buf);

            if (T == u128) {
                self.v2 ^= 0xee;
            } else {
                self.v2 ^= 0xff;
            }

            comptime var i: usize = 0;
            inline while (i < d_rounds) : (i += 1) {
                @call(.always_inline, sipRound, .{self});
            }

            const b1 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
            if (T == u64) {
                return b1;
            }

            self.v1 ^= 0xdd;

            comptime var j: usize = 0;
            inline while (j < d_rounds) : (j += 1) {
                @call(.always_inline, sipRound, .{self});
            }

            const b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
            return (@as(u128, b2) << 64) | b1;
        }

        fn round(self: *Self, b: [8]u8) void {
            const m = mem.readInt(u64, &b, .little);
            self.v3 ^= m;

            comptime var i: usize = 0;
            inline while (i < c_rounds) : (i += 1) {
                @call(.always_inline, sipRound, .{self});
            }

            self.v0 ^= m;
        }

        fn sipRound(d: *Self) void {
            d.v0 +%= d.v1;
            d.v1 = math.rotl(u64, d.v1, @as(u64, 13));
            d.v1 ^= d.v0;
            d.v0 = math.rotl(u64, d.v0, @as(u64, 32));
            d.v2 +%= d.v3;
            d.v3 = math.rotl(u64, d.v3, @as(u64, 16));
            d.v3 ^= d.v2;
            d.v0 +%= d.v3;
            d.v3 = math.rotl(u64, d.v3, @as(u64, 21));
            d.v3 ^= d.v0;
            d.v2 +%= d.v1;
            d.v1 = math.rotl(u64, d.v1, @as(u64, 17));
            d.v1 ^= d.v2;
            d.v2 = math.rotl(u64, d.v2, @as(u64, 32));
        }

        fn hash(msg: []const u8, key: *const [key_length]u8) T {
            const aligned_len = msg.len - (msg.len % 8);
            var c = Self.init(key);
            @call(.always_inline, update, .{ &c, msg[0..aligned_len] });
            return @call(.always_inline, final, .{ &c, msg[aligned_len..] });
        }
    };
}