feature. See also
. The project being documented here (as the example) is the Zig library itself.
siphash.SipHashStateless
fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type
File
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..] });
}
};
}