Process N leaves (8KiB chunks) in parallel - generic version
fn processLeaves(
comptime Variant: type,
comptime N: usize,
data: []const u8,
result: *[N * Variant.cv_size]u8,
) void
fn processLeaves(
comptime Variant: type,
comptime N: usize,
data: []const u8,
result: *[N * Variant.cv_size]u8,
) void {
const rate_in_lanes: usize = Variant.rate_in_lanes;
const rate_in_bytes: usize = rate_in_lanes * 8;
const cv_size: usize = Variant.cv_size;
// Initialize N all-zero states with cache alignment
var states: [5][5]@Vector(N, u64) align(cache_line_size) = undefined;
inline for (0..5) |x| {
inline for (0..5) |y| {
states[x][y] = @splat(0);
}
}
// Process complete blocks
var j: usize = 0;
while (j + rate_in_bytes <= chunk_size) : (j += rate_in_bytes) {
addLanesAll(N, &states, data[j..], rate_in_lanes, chunk_size / 8);
keccakP1600timesN(N, &states);
}
// Process last incomplete block
const remaining_lanes = (chunk_size - j) / 8;
if (remaining_lanes > 0) {
addLanesAll(N, &states, data[j..], remaining_lanes, chunk_size / 8);
}
// Add suffix 0x0B and padding
const suffix_pos = Variant.separation_byte_pos;
const padding_pos = Variant.padding_pos;
const suffix_splat: @Vector(N, u64) = @splat(0x0B);
states[suffix_pos.x][suffix_pos.y] ^= suffix_splat;
const padding_splat: @Vector(N, u64) = @splat(0x8000000000000000);
states[padding_pos.x][padding_pos.y] ^= padding_splat;
keccakP1600timesN(N, &states);
// Extract chaining values from each state
const lanes_to_extract = cv_size / 8;
comptime var lane_idx: usize = 0;
inline while (lane_idx < lanes_to_extract) : (lane_idx += 1) {
const x = lane_idx % 5;
const y = lane_idx / 5;
inline for (0..N) |i| {
store64(states[x][y][i], result[i * cv_size + lane_idx * 8 ..]);
}
}
}