structstd.hash.xxhash.XxHash32[src]

Fields

accumulator: Accumulator
seed: u32
buf: [16]u8
buf_len: usize
byte_count: usize

Functions

Functioninit[src]

pub fn init(seed: u32) XxHash32

Parameters

seed: u32

Source Code

Source code
pub fn init(seed: u32) XxHash32 {
    return XxHash32{
        .accumulator = Accumulator.init(seed),
        .seed = seed,
        .buf = undefined,
        .buf_len = 0,
        .byte_count = 0,
    };
}

Functionupdate[src]

pub fn update(self: *XxHash32, input: []const u8) void

Parameters

self: *XxHash32
input: []const u8

Source Code

Source code
pub fn update(self: *XxHash32, input: []const u8) void {
    if (input.len < 16 - self.buf_len) {
        @memcpy(self.buf[self.buf_len..][0..input.len], input);
        self.buf_len += input.len;
        return;
    }

    var i: usize = 0;

    if (self.buf_len > 0) {
        i = 16 - self.buf_len;
        @memcpy(self.buf[self.buf_len..][0..i], input[0..i]);
        self.accumulator.processStripe(&self.buf);
        self.byte_count += self.buf_len;
        self.buf_len = 0;
    }

    i += self.accumulator.updateEmpty(input[i..], 16);
    self.byte_count += i;

    const remaining_bytes = input[i..];
    @memcpy(self.buf[0..remaining_bytes.len], remaining_bytes);
    self.buf_len = remaining_bytes.len;
}

Functionfinal[src]

pub fn final(self: *XxHash32) u32

Parameters

self: *XxHash32

Source Code

Source code
pub fn final(self: *XxHash32) u32 {
    const unfinished = if (self.byte_count < 16)
        self.seed +% prime_5
    else
        self.accumulator.merge();

    return finalize(unfinished, self.byte_count, self.buf[0..self.buf_len]);
}

Functionhash[src]

pub fn hash(seed: u32, input: anytype) u32

Parameters

seed: u32

Source Code

Source code
pub fn hash(seed: u32, input: anytype) u32 {
    if (input.len < 16) {
        return finalize(seed +% prime_5, 0, input);
    } else {
        var hasher = Accumulator.init(seed);
        const i = hasher.updateEmpty(input, 0);
        return finalize(hasher.merge(), i, input[i..]);
    }
}

Source Code

Source code
pub const XxHash32 = struct {
    accumulator: Accumulator,
    seed: u32,
    buf: [16]u8,
    buf_len: usize,
    byte_count: usize,

    const prime_1 = 0x9E3779B1; // 0b10011110001101110111100110110001
    const prime_2 = 0x85EBCA77; // 0b10000101111010111100101001110111
    const prime_3 = 0xC2B2AE3D; // 0b11000010101100101010111000111101
    const prime_4 = 0x27D4EB2F; // 0b00100111110101001110101100101111
    const prime_5 = 0x165667B1; // 0b00010110010101100110011110110001

    const Accumulator = struct {
        acc1: u32,
        acc2: u32,
        acc3: u32,
        acc4: u32,

        fn init(seed: u32) Accumulator {
            return .{
                .acc1 = seed +% prime_1 +% prime_2,
                .acc2 = seed +% prime_2,
                .acc3 = seed,
                .acc4 = seed -% prime_1,
            };
        }

        fn updateEmpty(self: *Accumulator, input: anytype, comptime unroll_count: usize) usize {
            var i: usize = 0;

            if (unroll_count > 0) {
                const unrolled_bytes = unroll_count * 16;
                while (i + unrolled_bytes <= input.len) : (i += unrolled_bytes) {
                    inline for (0..unroll_count) |j| {
                        self.processStripe(input[i + j * 16 ..][0..16]);
                    }
                }
            }

            while (i + 16 <= input.len) : (i += 16) {
                self.processStripe(input[i..][0..16]);
            }

            return i;
        }

        fn processStripe(self: *Accumulator, buf: *const [16]u8) void {
            self.acc1 = round(self.acc1, mem.readInt(u32, buf[0..4], .little));
            self.acc2 = round(self.acc2, mem.readInt(u32, buf[4..8], .little));
            self.acc3 = round(self.acc3, mem.readInt(u32, buf[8..12], .little));
            self.acc4 = round(self.acc4, mem.readInt(u32, buf[12..16], .little));
        }

        fn merge(self: Accumulator) u32 {
            return rotl(u32, self.acc1, 1) +% rotl(u32, self.acc2, 7) +%
                rotl(u32, self.acc3, 12) +% rotl(u32, self.acc4, 18);
        }
    };

    pub fn init(seed: u32) XxHash32 {
        return XxHash32{
            .accumulator = Accumulator.init(seed),
            .seed = seed,
            .buf = undefined,
            .buf_len = 0,
            .byte_count = 0,
        };
    }

    pub fn update(self: *XxHash32, input: []const u8) void {
        if (input.len < 16 - self.buf_len) {
            @memcpy(self.buf[self.buf_len..][0..input.len], input);
            self.buf_len += input.len;
            return;
        }

        var i: usize = 0;

        if (self.buf_len > 0) {
            i = 16 - self.buf_len;
            @memcpy(self.buf[self.buf_len..][0..i], input[0..i]);
            self.accumulator.processStripe(&self.buf);
            self.byte_count += self.buf_len;
            self.buf_len = 0;
        }

        i += self.accumulator.updateEmpty(input[i..], 16);
        self.byte_count += i;

        const remaining_bytes = input[i..];
        @memcpy(self.buf[0..remaining_bytes.len], remaining_bytes);
        self.buf_len = remaining_bytes.len;
    }

    fn round(acc: u32, lane: u32) u32 {
        const a = acc +% (lane *% prime_2);
        const b = rotl(u32, a, 13);
        return b *% prime_1;
    }

    pub fn final(self: *XxHash32) u32 {
        const unfinished = if (self.byte_count < 16)
            self.seed +% prime_5
        else
            self.accumulator.merge();

        return finalize(unfinished, self.byte_count, self.buf[0..self.buf_len]);
    }

    fn finalize(unfinished: u32, byte_count: usize, partial: anytype) u32 {
        std.debug.assert(partial.len < 16);
        var acc = unfinished +% @as(u32, @intCast(byte_count)) +% @as(u32, @intCast(partial.len));

        switch (partial.len) {
            inline 0, 1, 2, 3 => |count| {
                inline for (0..count) |i| acc = finalize1(acc, partial[i]);
                return avalanche(acc);
            },
            inline 4, 5, 6, 7 => |count| {
                acc = finalize4(acc, partial[0..4]);
                inline for (4..count) |i| acc = finalize1(acc, partial[i]);
                return avalanche(acc);
            },
            inline 8, 9, 10, 11 => |count| {
                acc = finalize4(acc, partial[0..4]);
                acc = finalize4(acc, partial[4..8]);
                inline for (8..count) |i| acc = finalize1(acc, partial[i]);
                return avalanche(acc);
            },
            inline 12, 13, 14, 15 => |count| {
                acc = finalize4(acc, partial[0..4]);
                acc = finalize4(acc, partial[4..8]);
                acc = finalize4(acc, partial[8..12]);
                inline for (12..count) |i| acc = finalize1(acc, partial[i]);
                return avalanche(acc);
            },
            else => unreachable,
        }

        return avalanche(acc);
    }

    fn finalize4(v: u32, bytes: *const [4]u8) u32 {
        var acc = v;
        const lane = mem.readInt(u32, bytes, .little);
        acc +%= lane *% prime_3;
        acc = rotl(u32, acc, 17) *% prime_4;
        return acc;
    }

    fn finalize1(v: u32, byte: u8) u32 {
        var acc = v;
        const lane = @as(u32, byte);
        acc +%= lane *% prime_5;
        acc = rotl(u32, acc, 11) *% prime_1;
        return acc;
    }

    fn avalanche(value: u32) u32 {
        var acc = value ^ value >> 15;
        acc *%= prime_2;
        acc ^= acc >> 13;
        acc *%= prime_3;
        acc ^= acc >> 16;

        return acc;
    }

    pub fn hash(seed: u32, input: anytype) u32 {
        if (input.len < 16) {
            return finalize(seed +% prime_5, 0, input);
        } else {
            var hasher = Accumulator.init(seed);
            const i = hasher.updateEmpty(input, 0);
            return finalize(hasher.merge(), i, input[i..]);
        }
    }
}