Coverage Report

Created: 2026-03-16 16:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/hash/murmur_hash3.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
//-----------------------------------------------------------------------------
19
// MurmurHash3 was written by Austin Appleby, and is placed in the public
20
// domain. The author hereby disclaims copyright to this source code.
21
22
// Note - The x86 and x64 versions do _not_ produce the same results, as the
23
// algorithms are optimized for their respective platforms. You can still
24
// compile and run any of them on any platform, but your performance with the
25
// non-native version will be less than optimal.
26
27
#include "util/hash/murmur_hash3.h"
28
29
#include "util/unaligned.h"
30
31
namespace doris {
32
33
#include "common/compile_check_begin.h"
34
#if defined(_MSC_VER)
35
36
#define FORCE_INLINE __forceinline
37
38
#include <stdlib.h>
39
40
#define ROTL32(x, y) _rotl(x, y)
41
#define ROTL64(x, y) _rotl64(x, y)
42
43
#define BIG_CONSTANT(x) (x)
44
45
// Other compilers
46
47
#else // defined(_MSC_VER)
48
49
#define FORCE_INLINE inline __attribute__((always_inline))
50
51
1.54k
FORCE_INLINE uint32_t rotl32(uint32_t x, int8_t r) {
52
1.54k
    return (x << r) | (x >> (32 - r));
53
1.54k
}
54
55
1.72G
FORCE_INLINE uint64_t rotl64(uint64_t x, int8_t r) {
56
1.72G
    return (x << r) | (x >> (64 - r));
57
1.72G
}
58
59
1.54k
#define ROTL32(x, y) rotl32(x, y)
60
1.72G
#define ROTL64(x, y) rotl64(x, y)
61
62
223M
#define BIG_CONSTANT(x) (x##LLU)
63
64
#endif // !defined(_MSC_VER)
65
66
//-----------------------------------------------------------------------------
67
// Block read - if your platform needs to do endian-swapping or can only
68
// handle aligned reads, do the conversion here
69
70
252
FORCE_INLINE uint32_t getblock32(const uint32_t* p, int i) {
71
252
    return unaligned_load<uint32_t>(&p[i]);
72
252
}
73
74
848M
FORCE_INLINE uint64_t getblock64(const uint64_t* p, int i) {
75
848M
    return unaligned_load<uint64_t>(&p[i]);
76
848M
}
77
78
//-----------------------------------------------------------------------------
79
// Finalization mix - force all bits of a hash block to avalanche
80
81
1.10k
FORCE_INLINE uint32_t fmix32(uint32_t h) {
82
1.10k
    h ^= h >> 16;
83
1.10k
    h *= 0x85ebca6b;
84
1.10k
    h ^= h >> 13;
85
1.10k
    h *= 0xc2b2ae35;
86
1.10k
    h ^= h >> 16;
87
88
1.10k
    return h;
89
1.10k
}
90
91
//----------
92
93
55.8M
FORCE_INLINE uint64_t fmix64(uint64_t k) {
94
55.8M
    k ^= k >> 33;
95
55.8M
    k *= BIG_CONSTANT(0xff51afd7ed558ccd);
96
55.8M
    k ^= k >> 33;
97
55.8M
    k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
98
55.8M
    k ^= k >> 33;
99
100
55.8M
    return k;
101
55.8M
}
102
103
//-----------------------------------------------------------------------------
104
105
1.10k
void murmur_hash3_x86_32(const void* key, int64_t len, uint32_t seed, void* out) {
106
1.10k
    const uint8_t* data = (const uint8_t*)key;
107
1.10k
    const int nblocks = (int)len / 4;
108
109
1.10k
    uint32_t h1 = seed;
110
111
1.10k
    const uint32_t c1 = 0xcc9e2d51;
112
1.10k
    const uint32_t c2 = 0x1b873593;
113
114
    //----------
115
    // body
116
117
1.10k
    const uint32_t* blocks = (const uint32_t*)(data + nblocks * 4);
118
119
1.35k
    for (int i = -nblocks; i; i++) {
120
252
        uint32_t k1 = getblock32(blocks, i);
121
122
252
        k1 *= c1;
123
252
        k1 = ROTL32(k1, 15);
124
252
        k1 *= c2;
125
126
252
        h1 ^= k1;
127
252
        h1 = ROTL32(h1, 13);
128
252
        h1 = h1 * 5 + 0xe6546b64;
129
252
    }
130
131
    //----------
132
    // tail
133
134
1.10k
    const uint8_t* tail = (const uint8_t*)(data + nblocks * 4);
135
136
1.10k
    uint32_t k1 = 0;
137
138
1.10k
    switch (len & 3) {
139
49
    case 3:
140
49
        k1 ^= tail[2] << 16;
141
49
        [[fallthrough]];
142
271
    case 2:
143
271
        k1 ^= tail[1] << 8;
144
271
        [[fallthrough]];
145
1.03k
    case 1:
146
1.03k
        k1 ^= tail[0];
147
1.03k
        k1 *= c1;
148
1.03k
        k1 = ROTL32(k1, 15);
149
1.03k
        k1 *= c2;
150
1.03k
        h1 ^= k1;
151
1.10k
    };
152
153
    //----------
154
    // finalization
155
156
1.10k
    h1 ^= len;
157
158
1.10k
    h1 = fmix32(h1);
159
160
1.10k
    *(uint32_t*)out = h1;
161
1.10k
}
162
163
//-----------------------------------------------------------------------------
164
165
0
void murmur_hash3_x86_128(const void* key, const int len, uint32_t seed, void* out) {
166
0
    const uint8_t* data = (const uint8_t*)key;
167
0
    const int nblocks = len / 16;
168
169
0
    uint32_t h1 = seed;
170
0
    uint32_t h2 = seed;
171
0
    uint32_t h3 = seed;
172
0
    uint32_t h4 = seed;
173
174
0
    const uint32_t c1 = 0x239b961b;
175
0
    const uint32_t c2 = 0xab0e9789;
176
0
    const uint32_t c3 = 0x38b34ae5;
177
0
    const uint32_t c4 = 0xa1e38b93;
178
179
    //----------
180
    // body
181
182
0
    const uint32_t* blocks = (const uint32_t*)(data + nblocks * 16);
183
184
0
    for (int i = -nblocks; i; i++) {
185
0
        uint32_t k1 = getblock32(blocks, i * 4 + 0);
186
0
        uint32_t k2 = getblock32(blocks, i * 4 + 1);
187
0
        uint32_t k3 = getblock32(blocks, i * 4 + 2);
188
0
        uint32_t k4 = getblock32(blocks, i * 4 + 3);
189
190
0
        k1 *= c1;
191
0
        k1 = ROTL32(k1, 15);
192
0
        k1 *= c2;
193
0
        h1 ^= k1;
194
195
0
        h1 = ROTL32(h1, 19);
196
0
        h1 += h2;
197
0
        h1 = h1 * 5 + 0x561ccd1b;
198
199
0
        k2 *= c2;
200
0
        k2 = ROTL32(k2, 16);
201
0
        k2 *= c3;
202
0
        h2 ^= k2;
203
204
0
        h2 = ROTL32(h2, 17);
205
0
        h2 += h3;
206
0
        h2 = h2 * 5 + 0x0bcaa747;
207
208
0
        k3 *= c3;
209
0
        k3 = ROTL32(k3, 17);
210
0
        k3 *= c4;
211
0
        h3 ^= k3;
212
213
0
        h3 = ROTL32(h3, 15);
214
0
        h3 += h4;
215
0
        h3 = h3 * 5 + 0x96cd1c35;
216
217
0
        k4 *= c4;
218
0
        k4 = ROTL32(k4, 18);
219
0
        k4 *= c1;
220
0
        h4 ^= k4;
221
222
0
        h4 = ROTL32(h4, 13);
223
0
        h4 += h1;
224
0
        h4 = h4 * 5 + 0x32ac3b17;
225
0
    }
226
227
    //----------
228
    // tail
229
230
0
    const uint8_t* tail = (const uint8_t*)(data + nblocks * 16);
231
232
0
    uint32_t k1 = 0;
233
0
    uint32_t k2 = 0;
234
0
    uint32_t k3 = 0;
235
0
    uint32_t k4 = 0;
236
237
0
    switch (len & 15) {
238
0
    case 15:
239
0
        k4 ^= tail[14] << 16;
240
0
        [[fallthrough]];
241
0
    case 14:
242
0
        k4 ^= tail[13] << 8;
243
0
        [[fallthrough]];
244
0
    case 13:
245
0
        k4 ^= tail[12] << 0;
246
0
        k4 *= c4;
247
0
        k4 = ROTL32(k4, 18);
248
0
        k4 *= c1;
249
0
        h4 ^= k4;
250
0
        [[fallthrough]];
251
0
    case 12:
252
0
        k3 ^= tail[11] << 24;
253
0
        [[fallthrough]];
254
0
    case 11:
255
0
        k3 ^= tail[10] << 16;
256
0
        [[fallthrough]];
257
0
    case 10:
258
0
        k3 ^= tail[9] << 8;
259
0
        [[fallthrough]];
260
0
    case 9:
261
0
        k3 ^= tail[8] << 0;
262
0
        k3 *= c3;
263
0
        k3 = ROTL32(k3, 17);
264
0
        k3 *= c4;
265
0
        h3 ^= k3;
266
0
        [[fallthrough]];
267
0
    case 8:
268
0
        k2 ^= tail[7] << 24;
269
0
        [[fallthrough]];
270
0
    case 7:
271
0
        k2 ^= tail[6] << 16;
272
0
        [[fallthrough]];
273
0
    case 6:
274
0
        k2 ^= tail[5] << 8;
275
0
        [[fallthrough]];
276
0
    case 5:
277
0
        k2 ^= tail[4] << 0;
278
0
        k2 *= c2;
279
0
        k2 = ROTL32(k2, 16);
280
0
        k2 *= c3;
281
0
        h2 ^= k2;
282
0
        [[fallthrough]];
283
0
    case 4:
284
0
        k1 ^= tail[3] << 24;
285
0
        [[fallthrough]];
286
0
    case 3:
287
0
        k1 ^= tail[2] << 16;
288
0
        [[fallthrough]];
289
0
    case 2:
290
0
        k1 ^= tail[1] << 8;
291
0
        [[fallthrough]];
292
0
    case 1:
293
0
        k1 ^= tail[0] << 0;
294
0
        k1 *= c1;
295
0
        k1 = ROTL32(k1, 15);
296
0
        k1 *= c2;
297
0
        h1 ^= k1;
298
0
    };
299
300
    //----------
301
    // finalization
302
303
0
    h1 ^= len;
304
0
    h2 ^= len;
305
0
    h3 ^= len;
306
0
    h4 ^= len;
307
308
0
    h1 += h2;
309
0
    h1 += h3;
310
0
    h1 += h4;
311
0
    h2 += h1;
312
0
    h3 += h1;
313
0
    h4 += h1;
314
315
0
    h1 = fmix32(h1);
316
0
    h2 = fmix32(h2);
317
0
    h3 = fmix32(h3);
318
0
    h4 = fmix32(h4);
319
320
0
    h1 += h2;
321
0
    h1 += h3;
322
0
    h1 += h4;
323
0
    h2 += h1;
324
0
    h3 += h1;
325
0
    h4 += h1;
326
327
0
    ((uint32_t*)out)[0] = h1;
328
0
    ((uint32_t*)out)[1] = h2;
329
0
    ((uint32_t*)out)[2] = h3;
330
0
    ((uint32_t*)out)[3] = h4;
331
0
}
332
333
//-----------------------------------------------------------------------------
334
335
// Helper function that implements the core MurmurHash3 128-bit hashing algorithm
336
10
void murmur_hash3_x64_process(const void* key, const int len, uint64_t& h1, uint64_t& h2) {
337
10
    const uint8_t* data = (const uint8_t*)key;
338
10
    const int nblocks = len / 16;
339
340
10
    const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
341
10
    const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
342
343
    //----------
344
    // body
345
346
10
    const uint64_t* blocks = (const uint64_t*)(data);
347
348
12
    for (int i = 0; i < nblocks; i++) {
349
2
        uint64_t k1 = getblock64(blocks, i * 2 + 0);
350
2
        uint64_t k2 = getblock64(blocks, i * 2 + 1);
351
352
2
        k1 *= c1;
353
2
        k1 = ROTL64(k1, 31);
354
2
        k1 *= c2;
355
2
        h1 ^= k1;
356
357
2
        h1 = ROTL64(h1, 27);
358
2
        h1 += h2;
359
2
        h1 = h1 * 5 + 0x52dce729;
360
361
2
        k2 *= c2;
362
2
        k2 = ROTL64(k2, 33);
363
2
        k2 *= c1;
364
2
        h2 ^= k2;
365
366
2
        h2 = ROTL64(h2, 31);
367
2
        h2 += h1;
368
2
        h2 = h2 * 5 + 0x38495ab5;
369
2
    }
370
371
    //----------
372
    // tail
373
374
10
    const uint8_t* tail = (const uint8_t*)(data + nblocks * 16);
375
376
10
    uint64_t k1 = 0;
377
10
    uint64_t k2 = 0;
378
379
10
    switch (len & 15) {
380
0
    case 15:
381
0
        k2 ^= ((uint64_t)tail[14]) << 48;
382
0
        [[fallthrough]];
383
0
    case 14:
384
0
        k2 ^= ((uint64_t)tail[13]) << 40;
385
0
        [[fallthrough]];
386
0
    case 13:
387
0
        k2 ^= ((uint64_t)tail[12]) << 32;
388
0
        [[fallthrough]];
389
2
    case 12:
390
2
        k2 ^= ((uint64_t)tail[11]) << 24;
391
2
        [[fallthrough]];
392
7
    case 11:
393
7
        k2 ^= ((uint64_t)tail[10]) << 16;
394
7
        [[fallthrough]];
395
7
    case 10:
396
7
        k2 ^= ((uint64_t)tail[9]) << 8;
397
7
        [[fallthrough]];
398
7
    case 9:
399
7
        k2 ^= ((uint64_t)tail[8]) << 0;
400
7
        k2 *= c2;
401
7
        k2 = ROTL64(k2, 33);
402
7
        k2 *= c1;
403
7
        h2 ^= k2;
404
7
        [[fallthrough]];
405
7
    case 8:
406
7
        k1 ^= ((uint64_t)tail[7]) << 56;
407
7
        [[fallthrough]];
408
7
    case 7:
409
7
        k1 ^= ((uint64_t)tail[6]) << 48;
410
7
        [[fallthrough]];
411
7
    case 6:
412
7
        k1 ^= ((uint64_t)tail[5]) << 40;
413
7
        [[fallthrough]];
414
9
    case 5:
415
9
        k1 ^= ((uint64_t)tail[4]) << 32;
416
9
        [[fallthrough]];
417
9
    case 4:
418
9
        k1 ^= ((uint64_t)tail[3]) << 24;
419
9
        [[fallthrough]];
420
9
    case 3:
421
9
        k1 ^= ((uint64_t)tail[2]) << 16;
422
9
        [[fallthrough]];
423
9
    case 2:
424
9
        k1 ^= ((uint64_t)tail[1]) << 8;
425
9
        [[fallthrough]];
426
9
    case 1:
427
9
        k1 ^= ((uint64_t)tail[0]) << 0;
428
9
        k1 *= c1;
429
9
        k1 = ROTL64(k1, 31);
430
9
        k1 *= c2;
431
9
        h1 ^= k1;
432
10
    };
433
434
    //----------
435
    // finalization
436
437
10
    h1 ^= len;
438
10
    h2 ^= len;
439
440
10
    h1 += h2;
441
10
    h2 += h1;
442
443
10
    h1 = fmix64(h1);
444
10
    h2 = fmix64(h2);
445
446
10
    h1 += h2;
447
10
    h2 += h1;
448
10
}
449
450
//-----------------------------------------------------------------------------
451
452
// The origin function `murmur_hash3_x64_128` is copied from: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
453
// And Doris modified it into function `murmur_hash3_x64_process`
454
// For this reason, this function is still retained even though it has no calls.
455
1
void murmur_hash3_x64_128(const void* key, const int len, const uint32_t seed, void* out) {
456
1
    uint64_t h1 = seed;
457
1
    uint64_t h2 = seed;
458
1
    murmur_hash3_x64_process(key, len, h1, h2);
459
1
    ((uint64_t*)out)[0] = h1;
460
1
    ((uint64_t*)out)[1] = h2;
461
1
}
462
463
//-----------------------------------------------------------------------------
464
465
// MurmurHash3 x64 64-bit variant using shared 128-bit processing function
466
// This implementation reuses the murmur_hash3_x64_process function and only outputs the first hash value
467
// Used for function mmh3_64_v2
468
void murmur_hash3_x64_64_shared(const void* key, const int64_t len, const uint64_t seed,
469
7
                                void* out) {
470
7
    uint64_t h1 = seed;
471
7
    uint64_t h2 = seed;
472
7
    murmur_hash3_x64_process(key, static_cast<int>(len), h1, h2);
473
7
    ((uint64_t*)out)[0] = h1;
474
7
}
475
476
//-----------------------------------------------------------------------------
477
478
// MurmurHash3 x64 64-bit variant with optimized standalone implementation
479
// This implementation is specifically optimized for 64-bit output
480
// Used for function mmh3_64
481
55.9M
void murmur_hash3_x64_64(const void* key, const int64_t len, const uint64_t seed, void* out) {
482
55.9M
    const uint8_t* data = (const uint8_t*)key;
483
55.9M
    const int nblocks = (int)len / 8;
484
55.9M
    uint64_t h1 = seed;
485
486
55.9M
    const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
487
55.9M
    const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
488
489
    //----------
490
    // body
491
492
55.9M
    const uint64_t* blocks = (const uint64_t*)(data);
493
494
904M
    for (int i = 0; i < nblocks; i++) {
495
848M
        uint64_t k1 = getblock64(blocks, i);
496
497
848M
        k1 *= c1;
498
848M
        k1 = ROTL64(k1, 31);
499
848M
        k1 *= c2;
500
848M
        h1 ^= k1;
501
502
848M
        h1 = ROTL64(h1, 27);
503
848M
        h1 = h1 * 5 + 0x52dce729;
504
848M
    }
505
506
    //----------
507
    // tail
508
509
55.9M
    const uint8_t* tail = (const uint8_t*)(data + nblocks * 8);
510
55.9M
    uint64_t k1 = 0;
511
512
55.9M
    switch (len & 7) {
513
2.65M
    case 7:
514
2.65M
        k1 ^= ((uint64_t)tail[6]) << 48;
515
2.65M
        [[fallthrough]];
516
4.21M
    case 6:
517
4.21M
        k1 ^= ((uint64_t)tail[5]) << 40;
518
4.21M
        [[fallthrough]];
519
6.84M
    case 5:
520
6.84M
        k1 ^= ((uint64_t)tail[4]) << 32;
521
6.84M
        [[fallthrough]];
522
17.3M
    case 4:
523
17.3M
        k1 ^= ((uint64_t)tail[3]) << 24;
524
17.3M
        [[fallthrough]];
525
18.9M
    case 3:
526
18.9M
        k1 ^= ((uint64_t)tail[2]) << 16;
527
18.9M
        [[fallthrough]];
528
21.8M
    case 2:
529
21.8M
        k1 ^= ((uint64_t)tail[1]) << 8;
530
21.8M
        [[fallthrough]];
531
32.3M
    case 1:
532
32.3M
        k1 ^= ((uint64_t)tail[0]) << 0;
533
32.3M
        k1 *= c1;
534
32.3M
        k1 = ROTL64(k1, 31);
535
32.3M
        k1 *= c2;
536
32.3M
        h1 ^= k1;
537
55.9M
    };
538
539
    //----------
540
    // finalization
541
542
55.8M
    h1 ^= len;
543
55.8M
    h1 = fmix64(h1);
544
545
55.8M
    ((uint64_t*)out)[0] = h1;
546
55.8M
}
547
#include "common/compile_check_end.h"
548
549
} // namespace doris