master
  1const std = @import("std");
  2const builtin = @import("builtin");
  3const testing = std.testing;
  4const native_endian = builtin.target.cpu.arch.endian();
  5
  6const default_seed: u32 = 0xc70f6907;
  7
  8pub const Murmur2_32 = struct {
  9    const Self = @This();
 10
 11    pub fn hash(str: []const u8) u32 {
 12        return @call(.always_inline, Self.hashWithSeed, .{ str, default_seed });
 13    }
 14
 15    pub fn hashWithSeed(str: []const u8, seed: u32) u32 {
 16        const m: u32 = 0x5bd1e995;
 17        const len: u32 = @truncate(str.len);
 18        var h1: u32 = seed ^ len;
 19        for (@as([*]align(1) const u32, @ptrCast(str.ptr))[0..(len >> 2)]) |v| {
 20            var k1: u32 = v;
 21            if (native_endian == .big)
 22                k1 = @byteSwap(k1);
 23            k1 *%= m;
 24            k1 ^= k1 >> 24;
 25            k1 *%= m;
 26            h1 *%= m;
 27            h1 ^= k1;
 28        }
 29        const offset = len & 0xfffffffc;
 30        const rest = len & 3;
 31        if (rest >= 3) {
 32            h1 ^= @as(u32, @intCast(str[offset + 2])) << 16;
 33        }
 34        if (rest >= 2) {
 35            h1 ^= @as(u32, @intCast(str[offset + 1])) << 8;
 36        }
 37        if (rest >= 1) {
 38            h1 ^= @as(u32, @intCast(str[offset + 0]));
 39            h1 *%= m;
 40        }
 41        h1 ^= h1 >> 13;
 42        h1 *%= m;
 43        h1 ^= h1 >> 15;
 44        return h1;
 45    }
 46
 47    pub fn hashUint32(v: u32) u32 {
 48        return @call(.always_inline, Self.hashUint32WithSeed, .{ v, default_seed });
 49    }
 50
 51    pub fn hashUint32WithSeed(v: u32, seed: u32) u32 {
 52        const m: u32 = 0x5bd1e995;
 53        const len: u32 = 4;
 54        var h1: u32 = seed ^ len;
 55        var k1: u32 = undefined;
 56        k1 = v *% m;
 57        k1 ^= k1 >> 24;
 58        k1 *%= m;
 59        h1 *%= m;
 60        h1 ^= k1;
 61        h1 ^= h1 >> 13;
 62        h1 *%= m;
 63        h1 ^= h1 >> 15;
 64        return h1;
 65    }
 66
 67    pub fn hashUint64(v: u64) u32 {
 68        return @call(.always_inline, Self.hashUint64WithSeed, .{ v, default_seed });
 69    }
 70
 71    pub fn hashUint64WithSeed(v: u64, seed: u32) u32 {
 72        const m: u32 = 0x5bd1e995;
 73        const len: u32 = 8;
 74        var h1: u32 = seed ^ len;
 75        var k1: u32 = undefined;
 76        k1 = @as(u32, @truncate(v)) *% m;
 77        k1 ^= k1 >> 24;
 78        k1 *%= m;
 79        h1 *%= m;
 80        h1 ^= k1;
 81        k1 = @as(u32, @truncate(v >> 32)) *% m;
 82        k1 ^= k1 >> 24;
 83        k1 *%= m;
 84        h1 *%= m;
 85        h1 ^= k1;
 86        h1 ^= h1 >> 13;
 87        h1 *%= m;
 88        h1 ^= h1 >> 15;
 89        return h1;
 90    }
 91};
 92
 93pub const Murmur2_64 = struct {
 94    const Self = @This();
 95
 96    pub fn hash(str: []const u8) u64 {
 97        return @call(.always_inline, Self.hashWithSeed, .{ str, default_seed });
 98    }
 99
100    pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
101        const m: u64 = 0xc6a4a7935bd1e995;
102        var h1: u64 = seed ^ (@as(u64, str.len) *% m);
103        for (@as([*]align(1) const u64, @ptrCast(str.ptr))[0 .. str.len / 8]) |v| {
104            var k1: u64 = v;
105            if (native_endian == .big)
106                k1 = @byteSwap(k1);
107            k1 *%= m;
108            k1 ^= k1 >> 47;
109            k1 *%= m;
110            h1 ^= k1;
111            h1 *%= m;
112        }
113        const rest = str.len & 7;
114        const offset = str.len - rest;
115        if (rest > 0) {
116            var k1: u64 = 0;
117            @memcpy(@as([*]u8, @ptrCast(&k1))[0..rest], str[offset..]);
118            if (native_endian == .big)
119                k1 = @byteSwap(k1);
120            h1 ^= k1;
121            h1 *%= m;
122        }
123        h1 ^= h1 >> 47;
124        h1 *%= m;
125        h1 ^= h1 >> 47;
126        return h1;
127    }
128
129    pub fn hashUint32(v: u32) u64 {
130        return @call(.always_inline, Self.hashUint32WithSeed, .{ v, default_seed });
131    }
132
133    pub fn hashUint32WithSeed(v: u32, seed: u64) u64 {
134        const m: u64 = 0xc6a4a7935bd1e995;
135        const len: u64 = 4;
136        var h1: u64 = seed ^ (len *% m);
137        const k1: u64 = v;
138        h1 ^= k1;
139        h1 *%= m;
140        h1 ^= h1 >> 47;
141        h1 *%= m;
142        h1 ^= h1 >> 47;
143        return h1;
144    }
145
146    pub fn hashUint64(v: u64) u64 {
147        return @call(.always_inline, Self.hashUint64WithSeed, .{ v, default_seed });
148    }
149
150    pub fn hashUint64WithSeed(v: u64, seed: u64) u64 {
151        const m: u64 = 0xc6a4a7935bd1e995;
152        const len: u64 = 8;
153        var h1: u64 = seed ^ (len *% m);
154        var k1: u64 = undefined;
155        k1 = v *% m;
156        k1 ^= k1 >> 47;
157        k1 *%= m;
158        h1 ^= k1;
159        h1 *%= m;
160        h1 ^= h1 >> 47;
161        h1 *%= m;
162        h1 ^= h1 >> 47;
163        return h1;
164    }
165};
166
167pub const Murmur3_32 = struct {
168    const Self = @This();
169
170    fn rotl32(x: u32, comptime r: u32) u32 {
171        return (x << r) | (x >> (32 - r));
172    }
173
174    pub fn hash(str: []const u8) u32 {
175        return @call(.always_inline, Self.hashWithSeed, .{ str, default_seed });
176    }
177
178    pub fn hashWithSeed(str: []const u8, seed: u32) u32 {
179        const c1: u32 = 0xcc9e2d51;
180        const c2: u32 = 0x1b873593;
181        const len: u32 = @truncate(str.len);
182        var h1: u32 = seed;
183        for (@as([*]align(1) const u32, @ptrCast(str.ptr))[0..(len >> 2)]) |v| {
184            var k1: u32 = v;
185            if (native_endian == .big)
186                k1 = @byteSwap(k1);
187            k1 *%= c1;
188            k1 = rotl32(k1, 15);
189            k1 *%= c2;
190            h1 ^= k1;
191            h1 = rotl32(h1, 13);
192            h1 *%= 5;
193            h1 +%= 0xe6546b64;
194        }
195        {
196            var k1: u32 = 0;
197            const offset = len & 0xfffffffc;
198            const rest = len & 3;
199            if (rest == 3) {
200                k1 ^= @as(u32, @intCast(str[offset + 2])) << 16;
201            }
202            if (rest >= 2) {
203                k1 ^= @as(u32, @intCast(str[offset + 1])) << 8;
204            }
205            if (rest >= 1) {
206                k1 ^= @as(u32, @intCast(str[offset + 0]));
207                k1 *%= c1;
208                k1 = rotl32(k1, 15);
209                k1 *%= c2;
210                h1 ^= k1;
211            }
212        }
213        h1 ^= len;
214        h1 ^= h1 >> 16;
215        h1 *%= 0x85ebca6b;
216        h1 ^= h1 >> 13;
217        h1 *%= 0xc2b2ae35;
218        h1 ^= h1 >> 16;
219        return h1;
220    }
221
222    pub fn hashUint32(v: u32) u32 {
223        return @call(.always_inline, Self.hashUint32WithSeed, .{ v, default_seed });
224    }
225
226    pub fn hashUint32WithSeed(v: u32, seed: u32) u32 {
227        const c1: u32 = 0xcc9e2d51;
228        const c2: u32 = 0x1b873593;
229        const len: u32 = 4;
230        var h1: u32 = seed;
231        var k1: u32 = undefined;
232        k1 = v *% c1;
233        k1 = rotl32(k1, 15);
234        k1 *%= c2;
235        h1 ^= k1;
236        h1 = rotl32(h1, 13);
237        h1 *%= 5;
238        h1 +%= 0xe6546b64;
239        h1 ^= len;
240        h1 ^= h1 >> 16;
241        h1 *%= 0x85ebca6b;
242        h1 ^= h1 >> 13;
243        h1 *%= 0xc2b2ae35;
244        h1 ^= h1 >> 16;
245        return h1;
246    }
247
248    pub fn hashUint64(v: u64) u32 {
249        return @call(.always_inline, Self.hashUint64WithSeed, .{ v, default_seed });
250    }
251
252    pub fn hashUint64WithSeed(v: u64, seed: u32) u32 {
253        const c1: u32 = 0xcc9e2d51;
254        const c2: u32 = 0x1b873593;
255        const len: u32 = 8;
256        var h1: u32 = seed;
257        var k1: u32 = undefined;
258        k1 = @as(u32, @truncate(v)) *% c1;
259        k1 = rotl32(k1, 15);
260        k1 *%= c2;
261        h1 ^= k1;
262        h1 = rotl32(h1, 13);
263        h1 *%= 5;
264        h1 +%= 0xe6546b64;
265        k1 = @as(u32, @truncate(v >> 32)) *% c1;
266        k1 = rotl32(k1, 15);
267        k1 *%= c2;
268        h1 ^= k1;
269        h1 = rotl32(h1, 13);
270        h1 *%= 5;
271        h1 +%= 0xe6546b64;
272        h1 ^= len;
273        h1 ^= h1 >> 16;
274        h1 *%= 0x85ebca6b;
275        h1 ^= h1 >> 13;
276        h1 *%= 0xc2b2ae35;
277        h1 ^= h1 >> 16;
278        return h1;
279    }
280};
281
282const verify = @import("verify.zig");
283
284test "murmur2_32" {
285    const v0: u32 = 0x12345678;
286    const v1: u64 = 0x1234567812345678;
287    const v0le: u32, const v1le: u64 = switch (native_endian) {
288        .little => .{ v0, v1 },
289        .big => .{ @byteSwap(v0), @byteSwap(v1) },
290    };
291    try testing.expectEqual(Murmur2_32.hash(@as([*]const u8, @ptrCast(&v0le))[0..4]), Murmur2_32.hashUint32(v0));
292    try testing.expectEqual(Murmur2_32.hash(@as([*]const u8, @ptrCast(&v1le))[0..8]), Murmur2_32.hashUint64(v1));
293}
294
295test "murmur2_32 smhasher" {
296    const Test = struct {
297        fn do() !void {
298            try testing.expectEqual(verify.smhasher(Murmur2_32.hashWithSeed), 0x27864C1E);
299        }
300    };
301    try Test.do();
302    @setEvalBranchQuota(30000);
303    try comptime Test.do();
304}
305
306test "murmur2_64" {
307    const v0: u32 = 0x12345678;
308    const v1: u64 = 0x1234567812345678;
309    const v0le: u32, const v1le: u64 = switch (native_endian) {
310        .little => .{ v0, v1 },
311        .big => .{ @byteSwap(v0), @byteSwap(v1) },
312    };
313    try testing.expectEqual(Murmur2_64.hash(@as([*]const u8, @ptrCast(&v0le))[0..4]), Murmur2_64.hashUint32(v0));
314    try testing.expectEqual(Murmur2_64.hash(@as([*]const u8, @ptrCast(&v1le))[0..8]), Murmur2_64.hashUint64(v1));
315}
316
317test "mumur2_64 smhasher" {
318    const Test = struct {
319        fn do() !void {
320            try std.testing.expectEqual(verify.smhasher(Murmur2_64.hashWithSeed), 0x1F0D3804);
321        }
322    };
323    try Test.do();
324    @setEvalBranchQuota(30000);
325    try comptime Test.do();
326}
327
328test "murmur3_32" {
329    const v0: u32 = 0x12345678;
330    const v1: u64 = 0x1234567812345678;
331    const v0le: u32, const v1le: u64 = switch (native_endian) {
332        .little => .{ v0, v1 },
333        .big => .{ @byteSwap(v0), @byteSwap(v1) },
334    };
335    try testing.expectEqual(Murmur3_32.hash(@as([*]const u8, @ptrCast(&v0le))[0..4]), Murmur3_32.hashUint32(v0));
336    try testing.expectEqual(Murmur3_32.hash(@as([*]const u8, @ptrCast(&v1le))[0..8]), Murmur3_32.hashUint64(v1));
337}
338
339test "mumur3_32 smhasher" {
340    const Test = struct {
341        fn do() !void {
342            try std.testing.expectEqual(verify.smhasher(Murmur3_32.hashWithSeed), 0xB0F57EE3);
343        }
344    };
345    try Test.do();
346    @setEvalBranchQuota(30000);
347    try comptime Test.do();
348}