Coverage Report

Created: 2025-12-28 00:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/util/hash_util.hpp
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
// This file is copied from
18
// https://github.com/apache/impala/blob/branch-2.9.0/be/src/util/hash-util.h
19
// and modified by Doris
20
21
#pragma once
22
23
#include <crc32c/crc32c.h>
24
#include <gen_cpp/Types_types.h>
25
#include <xxh3.h>
26
#include <xxhash.h>
27
#include <zlib.h>
28
29
#include <bit>
30
#include <functional>
31
32
#include "common/compiler_util.h" // IWYU pragma: keep
33
#include "util/cpu_info.h"
34
#include "util/hash/city.h"
35
#include "util/murmur_hash3.h"
36
#include "util/sse_util.hpp"
37
38
namespace doris {
39
#include "common/compile_check_begin.h"
40
// Utility class to compute hash values.
41
class HashUtil {
42
public:
43
3.78M
    static uint32_t zlib_crc_hash(const void* data, uint32_t bytes, uint32_t hash) {
44
3.78M
        return (uint32_t)crc32(hash, (const unsigned char*)data, bytes);
45
3.78M
    }
46
47
1.08M
    static uint32_t zlib_crc_hash_null(uint32_t hash) {
48
        // null is treat as 0 when hash
49
1.08M
        static const int INT_VALUE = 0;
50
1.08M
        return (uint32_t)crc32(hash, (const unsigned char*)(&INT_VALUE), 4);
51
1.08M
    }
52
53
    template <typename T>
54
0
    static uint32_t crc32c_fixed(const T& value, uint32_t hash) {
55
0
        if constexpr (sizeof(T) == 1) {
56
0
            return _mm_crc32_u8(hash, *reinterpret_cast<const uint8_t*>(&value));
57
0
        } else if constexpr (sizeof(T) == 2) {
58
0
            return _mm_crc32_u16(hash, *reinterpret_cast<const uint16_t*>(&value));
59
0
        } else if constexpr (sizeof(T) == 4) {
60
0
            return _mm_crc32_u32(hash, *reinterpret_cast<const uint32_t*>(&value));
61
0
        } else if constexpr (sizeof(T) == 8) {
62
0
            return (uint32_t)_mm_crc32_u64(hash, *reinterpret_cast<const uint64_t*>(&value));
63
0
        } else {
64
0
            return crc32c_extend(hash, (const uint8_t*)&value, sizeof(T));
65
0
        }
66
0
    }
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIiEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedINS_10vectorized7DecimalIiEEEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedINS_10vectorized7DecimalIlEEEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedINS_10vectorized7DecimalInEEEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedINS_10vectorized12Decimal128V3EEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedINS_10vectorized7DecimalIN4wide7integerILm256EiEEEEEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIhEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIaEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIsEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIlEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedInEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIfEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIdEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIjEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedIoEEjRKT_j
Unexecuted instantiation: _ZN5doris8HashUtil12crc32c_fixedImEEjRKT_j
67
68
0
    static uint32_t crc32c_null(uint32_t hash) {
69
        // null is treat as 0 when hash
70
0
        static const int INT_VALUE = 0;
71
0
        return crc32c_fixed(INT_VALUE, hash);
72
0
    }
73
74
    // Compute the Crc32 hash for data using SSE4 instructions.  The input hash parameter is
75
    // the current hash/seed value.
76
    // This should only be called if SSE is supported.
77
    // This is ~4x faster than Fnv/Boost Hash.
78
    // NOTE: DO NOT use this method for checksum! This does not generate the standard CRC32 checksum!
79
    //       For checksum, use CRC-32C algorithm from crc32c.h
80
    // NOTE: Any changes made to this function need to be reflected in Codegen::GetHashFn.
81
    // TODO: crc32 hashes with different seeds do not result in different hash functions.
82
    // The resulting hashes are correlated.
83
    // ATTN: prefer do not use this function anymore, use crc32c::Extend instead
84
    // This function is retained because it is not certain whether there are compatibility issues with historical data.
85
188k
    static uint32_t crc_hash(const void* data, uint32_t bytes, uint32_t hash) {
86
188k
        if (!CpuInfo::is_supported(CpuInfo::SSE4_2)) {
87
0
            return zlib_crc_hash(data, bytes, hash);
88
0
        }
89
188k
        uint32_t words = bytes / sizeof(uint32_t);
90
188k
        bytes = bytes % sizeof(uint32_t);
91
92
188k
        const uint32_t* p = reinterpret_cast<const uint32_t*>(data);
93
94
341k
        while (words--) {
95
152k
            hash = _mm_crc32_u32(hash, *p);
96
152k
            ++p;
97
152k
        }
98
99
188k
        const uint8_t* s = reinterpret_cast<const uint8_t*>(p);
100
101
192k
        while (bytes--) {
102
4.36k
            hash = _mm_crc32_u8(hash, *s);
103
4.36k
            ++s;
104
4.36k
        }
105
106
        // The lower half of the CRC hash has has poor uniformity, so swap the halves
107
        // for anyone who only uses the first several bits of the hash.
108
188k
        hash = (hash << 16) | (hash >> 16);
109
188k
        return hash;
110
188k
    }
111
112
0
    static uint64_t crc_hash64(const void* data, uint32_t bytes, uint64_t hash) {
113
0
        uint32_t words = bytes / sizeof(uint32_t);
114
0
        bytes = bytes % sizeof(uint32_t);
115
116
0
        uint32_t h1 = hash >> 32;
117
0
        uint32_t h2 = (hash << 32) >> 32;
118
119
0
        const uint32_t* p = reinterpret_cast<const uint32_t*>(data);
120
0
        while (words--) {
121
0
            (words & 1) ? (h1 = _mm_crc32_u32(h1, *p)) : (h2 = _mm_crc32_u32(h2, *p));
122
0
            ++p;
123
0
        }
124
125
0
        const uint8_t* s = reinterpret_cast<const uint8_t*>(p);
126
0
        while (bytes--) {
127
0
            (bytes & 1) ? (h1 = _mm_crc32_u8(h1, *s)) : (h2 = _mm_crc32_u8(h2, *s));
128
0
            ++s;
129
0
        }
130
0
        union {
131
0
            uint64_t u64;
132
0
            uint32_t u32[2];
133
0
        } converter;
134
0
        converter.u64 = hash;
135
136
0
        h1 = (h1 << 16) | (h1 >> 16);
137
0
        h2 = (h2 << 16) | (h2 >> 16);
138
0
        converter.u32[0] = h1;
139
0
        converter.u32[1] = h2;
140
141
0
        return converter.u64;
142
0
    }
143
144
    // refer to https://github.com/apache/commons-codec/blob/master/src/main/java/org/apache/commons/codec/digest/MurmurHash3.java
145
    static const uint32_t MURMUR3_32_SEED = 104729;
146
147
    // modify from https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
148
20
    static uint32_t murmur_hash3_32(const void* key, int64_t len, uint32_t seed) {
149
20
        uint32_t out = 0;
150
20
        murmur_hash3_x86_32(key, len, seed, &out);
151
20
        return out;
152
20
    }
153
154
    template <bool is_mmh64_v2>
155
15
    static uint64_t murmur_hash3_64(const void* key, int64_t len, uint64_t seed) {
156
15
        uint64_t out = 0;
157
15
        if constexpr (is_mmh64_v2) {
158
3
            murmur_hash3_x64_64_shared(key, len, seed, &out);
159
12
        } else {
160
12
            murmur_hash3_x64_64(key, len, seed, &out);
161
12
        }
162
15
        return out;
163
15
    }
_ZN5doris8HashUtil15murmur_hash3_64ILb0EEEmPKvlm
Line
Count
Source
155
12
    static uint64_t murmur_hash3_64(const void* key, int64_t len, uint64_t seed) {
156
12
        uint64_t out = 0;
157
        if constexpr (is_mmh64_v2) {
158
            murmur_hash3_x64_64_shared(key, len, seed, &out);
159
12
        } else {
160
12
            murmur_hash3_x64_64(key, len, seed, &out);
161
12
        }
162
12
        return out;
163
12
    }
_ZN5doris8HashUtil15murmur_hash3_64ILb1EEEmPKvlm
Line
Count
Source
155
3
    static uint64_t murmur_hash3_64(const void* key, int64_t len, uint64_t seed) {
156
3
        uint64_t out = 0;
157
3
        if constexpr (is_mmh64_v2) {
158
3
            murmur_hash3_x64_64_shared(key, len, seed, &out);
159
        } else {
160
            murmur_hash3_x64_64(key, len, seed, &out);
161
        }
162
3
        return out;
163
3
    }
164
165
    static const int MURMUR_R = 47;
166
167
    // Murmur2 hash implementation returning 64-bit hashes.
168
0
    static uint64_t murmur_hash2_64(const void* input, int len, uint64_t seed) {
169
0
        uint64_t h = seed ^ (len * MURMUR_PRIME);
170
0
171
0
        const uint64_t* data = reinterpret_cast<const uint64_t*>(input);
172
0
        const uint64_t* end = data + (len / sizeof(uint64_t));
173
0
174
0
        while (data != end) {
175
0
            uint64_t k = *data++;
176
0
            k *= MURMUR_PRIME;
177
0
            k ^= k >> MURMUR_R;
178
0
            k *= MURMUR_PRIME;
179
0
            h ^= k;
180
0
            h *= MURMUR_PRIME;
181
0
        }
182
0
183
0
        const uint8_t* data2 = reinterpret_cast<const uint8_t*>(data);
184
0
        switch (len & 7) {
185
0
        case 7:
186
0
            h ^= uint64_t(data2[6]) << 48;
187
0
            [[fallthrough]];
188
0
        case 6:
189
0
            h ^= uint64_t(data2[5]) << 40;
190
0
            [[fallthrough]];
191
0
        case 5:
192
0
            h ^= uint64_t(data2[4]) << 32;
193
0
            [[fallthrough]];
194
0
        case 4:
195
0
            h ^= uint64_t(data2[3]) << 24;
196
0
            [[fallthrough]];
197
0
        case 3:
198
0
            h ^= uint64_t(data2[2]) << 16;
199
0
            [[fallthrough]];
200
0
        case 2:
201
0
            h ^= uint64_t(data2[1]) << 8;
202
0
            [[fallthrough]];
203
0
        case 1:
204
0
            h ^= uint64_t(data2[0]);
205
0
            h *= MURMUR_PRIME;
206
0
        }
207
0
208
0
        h ^= h >> MURMUR_R;
209
0
        h *= MURMUR_PRIME;
210
0
        h ^= h >> MURMUR_R;
211
0
        return h;
212
0
    }
213
214
    // default values recommended by http://isthe.com/chongo/tech/comp/fnv/
215
    static const uint32_t FNV_PRIME = 0x01000193; //   16777619
216
    static const uint32_t FNV_SEED = 0x811C9DC5;  // 2166136261
217
    static const uint64_t FNV64_PRIME = 1099511628211UL;
218
    static const uint64_t FNV64_SEED = 14695981039346656037UL;
219
    static const uint64_t MURMUR_PRIME = 0xc6a4a7935bd1e995ULL;
220
    static const uint32_t MURMUR_SEED = 0xadc83b19ULL;
221
    // Implementation of the Fowler–Noll–Vo hash function.  This is not as performant
222
    // as boost's hash on int types (2x slower) but has bit entropy.
223
    // For ints, boost just returns the value of the int which can be pathological.
224
    // For example, if the data is <1000, 2000, 3000, 4000, ..> and then the mod of 1000
225
    // is taken on the hash, all values will collide to the same bucket.
226
    // For string values, Fnv is slightly faster than boost.
227
0
    static uint32_t fnv_hash(const void* data, uint32_t bytes, uint32_t hash) {
228
0
        const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
229
230
0
        while (bytes--) {
231
0
            hash = (*ptr ^ hash) * FNV_PRIME;
232
0
            ++ptr;
233
0
        }
234
235
0
        return hash;
236
0
    }
237
238
0
    static uint64_t fnv_hash64(const void* data, uint32_t bytes, uint64_t hash) {
239
0
        const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
240
0
241
0
        while (bytes--) {
242
0
            hash = (*ptr ^ hash) * FNV64_PRIME;
243
0
            ++ptr;
244
0
        }
245
0
246
0
        return hash;
247
0
    }
248
249
    // Our hash function is MurmurHash2, 64 bit version.
250
    // It was modified in order to provide the same result in
251
    // big and little endian archs (endian neutral).
252
67.8k
    static uint64_t murmur_hash64A(const void* key, int64_t len, unsigned int seed) {
253
67.8k
        const uint64_t m = MURMUR_PRIME;
254
67.8k
        const int r = 47;
255
67.8k
        uint64_t h = seed ^ (len * m);
256
67.8k
        const uint8_t* data = (const uint8_t*)key;
257
67.8k
        const uint8_t* end = data + (len - (len & 7));
258
259
135k
        while (data != end) {
260
67.8k
            uint64_t k;
261
            if constexpr (std::endian::native == std::endian::big) {
262
                k = (uint64_t)data[0];
263
                k |= (uint64_t)data[1] << 8;
264
                k |= (uint64_t)data[2] << 16;
265
                k |= (uint64_t)data[3] << 24;
266
                k |= (uint64_t)data[4] << 32;
267
                k |= (uint64_t)data[5] << 40;
268
                k |= (uint64_t)data[6] << 48;
269
                k |= (uint64_t)data[7] << 56;
270
67.8k
            } else if constexpr (std::endian::native == std::endian::little) {
271
67.8k
                memcpy(&k, data, sizeof(k));
272
            } else {
273
                static_assert(std::endian::native == std::endian::big ||
274
                                      std::endian::native == std::endian::little,
275
                              "Unsupported endianness");
276
            }
277
278
67.8k
            k *= m;
279
67.8k
            k ^= k >> r;
280
67.8k
            k *= m;
281
67.8k
            h ^= k;
282
67.8k
            h *= m;
283
67.8k
            data += 8;
284
67.8k
        }
285
286
67.8k
        switch (len & 7) {
287
0
        case 7:
288
0
            h ^= (uint64_t)data[6] << 48;
289
0
            [[fallthrough]];
290
0
        case 6:
291
0
            h ^= (uint64_t)data[5] << 40;
292
0
            [[fallthrough]];
293
0
        case 5:
294
0
            h ^= (uint64_t)data[4] << 32;
295
0
            [[fallthrough]];
296
3
        case 4:
297
3
            h ^= (uint64_t)data[3] << 24;
298
3
            [[fallthrough]];
299
3
        case 3:
300
3
            h ^= (uint64_t)data[2] << 16;
301
3
            [[fallthrough]];
302
3
        case 2:
303
3
            h ^= (uint64_t)data[1] << 8;
304
3
            [[fallthrough]];
305
6
        case 1:
306
6
            h ^= (uint64_t)data[0];
307
6
            h *= m;
308
67.8k
        }
309
310
67.8k
        h ^= h >> r;
311
67.8k
        h *= m;
312
67.8k
        h ^= h >> r;
313
67.8k
        return h;
314
67.8k
    }
315
316
    // Computes the hash value for data.  Will call either CrcHash or FnvHash
317
    // depending on hardware capabilities.
318
    // Seed values for different steps of the query execution should use different seeds
319
    // to prevent accidental key collisions. (See IMPALA-219 for more details).
320
188k
    static uint32_t hash(const void* data, uint32_t bytes, uint32_t seed) {
321
188k
#ifdef __SSE4_2__
322
323
188k
        if (LIKELY(CpuInfo::is_supported(CpuInfo::SSE4_2))) {
324
188k
            return crc_hash(data, bytes, seed);
325
188k
        } else {
326
0
            return fnv_hash(data, bytes, seed);
327
0
        }
328
329
#else
330
        return fnv_hash(data, bytes, seed);
331
#endif
332
188k
    }
333
334
76.6k
    static uint64_t hash64(const void* data, uint64_t bytes, uint64_t seed) {
335
#ifdef _SSE4_2_
336
        if (LIKELY(CpuInfo::is_supported(CpuInfo::SSE4_2))) {
337
            return crc_hash64(data, bytes, seed);
338
339
        } else {
340
            uint64_t hash = 0;
341
            murmur_hash3_x64_64(data, bytes, seed, &hash);
342
            return hash;
343
        }
344
#else
345
76.6k
        uint64_t hash = 0;
346
76.6k
        murmur_hash3_x64_64(data, bytes, seed, &hash);
347
76.6k
        return hash;
348
76.6k
#endif
349
76.6k
    }
350
    // hash_combine is the same with boost hash_combine,
351
    // except replace boost::hash with std::hash
352
    template <class T>
353
389
    static inline void hash_combine(std::size_t& seed, const T& v) {
354
389
        std::hash<T> hasher;
355
389
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
356
389
    }
_ZN5doris8HashUtil12hash_combineINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEEvRmRKT_
Line
Count
Source
353
387
    static inline void hash_combine(std::size_t& seed, const T& v) {
354
387
        std::hash<T> hasher;
355
387
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
356
387
    }
_ZN5doris8HashUtil12hash_combineIlEEvRmRKT_
Line
Count
Source
353
2
    static inline void hash_combine(std::size_t& seed, const T& v) {
354
2
        std::hash<T> hasher;
355
2
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
356
2
    }
357
358
#if defined(__clang__)
359
#pragma clang diagnostic push
360
#pragma clang diagnostic ignored "-Wused-but-marked-unused"
361
#endif
362
    // xxHash function for a byte array.  For convenience, a 64-bit seed is also
363
    // hashed into the result.  The mapping may change from time to time.
364
24
    static xxh_u32 xxHash32WithSeed(const char* s, size_t len, xxh_u32 seed) {
365
24
        return XXH32(s, len, seed);
366
24
    }
367
368
    // same to the up function, just for null value
369
0
    static xxh_u32 xxHash32NullWithSeed(xxh_u32 seed) {
370
0
        static const int INT_VALUE = 0;
371
0
        return XXH32(reinterpret_cast<const char*>(&INT_VALUE), sizeof(int), seed);
372
0
    }
373
374
149k
    static xxh_u64 xxHash64WithSeed(const char* s, size_t len, xxh_u64 seed) {
375
149k
        return XXH3_64bits_withSeed(s, len, seed);
376
149k
    }
377
378
    // same to the up function, just for null value
379
1.08M
    static xxh_u64 xxHash64NullWithSeed(xxh_u64 seed) {
380
1.08M
        static const int INT_VALUE = 0;
381
1.08M
        return XXH3_64bits_withSeed(reinterpret_cast<const char*>(&INT_VALUE), sizeof(int), seed);
382
1.08M
    }
383
384
21
    static xxh_u64 xxhash64_compat_with_seed(const char* s, size_t len, xxh_u64 seed) {
385
21
        return XXH64(reinterpret_cast<const void*>(s), len, seed);
386
21
    }
387
388
0
    static xxh_u64 xxhash64_compat_null_with_seed(xxh_u64 seed) {
389
0
        static const int INT_VALUE = 0;
390
0
        return XXH64(reinterpret_cast<const void*>(&INT_VALUE), sizeof(int), seed);
391
0
    }
392
393
#if defined(__clang__)
394
#pragma clang diagnostic pop
395
#endif
396
};
397
398
} // namespace doris
399
400
template <>
401
struct std::hash<doris::TUniqueId> {
402
7.44k
    size_t operator()(const doris::TUniqueId& id) const {
403
7.44k
        uint32_t seed = 0;
404
7.44k
        seed = doris::HashUtil::hash(&id.lo, sizeof(id.lo), seed);
405
7.44k
        seed = doris::HashUtil::hash(&id.hi, sizeof(id.hi), seed);
406
7.44k
        return seed;
407
7.44k
    }
408
};
409
410
template <>
411
struct std::hash<doris::TNetworkAddress> {
412
0
    size_t operator()(const doris::TNetworkAddress& address) const {
413
0
        uint32_t seed = 0;
414
0
        seed = doris::HashUtil::hash(address.hostname.data(), (uint32_t)address.hostname.size(),
415
0
                                     seed);
416
0
        seed = doris::HashUtil::hash(&address.port, 4, seed);
417
0
        return seed;
418
0
    }
419
};
420
421
template <>
422
struct std::hash<std::pair<doris::TUniqueId, int64_t>> {
423
0
    size_t operator()(const std::pair<doris::TUniqueId, int64_t>& pair) const {
424
0
        uint32_t seed = 0;
425
0
        seed = doris::HashUtil::hash(&pair.first.lo, sizeof(pair.first.lo), seed);
426
0
        seed = doris::HashUtil::hash(&pair.first.hi, sizeof(pair.first.hi), seed);
427
0
        seed = doris::HashUtil::hash(&pair.second, sizeof(pair.second), seed);
428
0
        return seed;
429
0
    }
430
};
431
432
template <class First, class Second>
433
struct std::hash<std::pair<First, Second>> {
434
37.0k
    size_t operator()(const pair<First, Second>& p) const {
435
37.0k
        size_t h1 = std::hash<First>()(p.first);
436
37.0k
        size_t h2 = std::hash<Second>()(p.second);
437
37.0k
        return doris::util_hash::HashLen16(h1, h2);
438
37.0k
    }
Unexecuted instantiation: _ZNKSt4hashISt4pairIlN5doris8RowsetIdEEEclERKS3_
_ZNKSt4hashISt4pairINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEElEEclERKS7_
Line
Count
Source
434
32
    size_t operator()(const pair<First, Second>& p) const {
435
32
        size_t h1 = std::hash<First>()(p.first);
436
32
        size_t h2 = std::hash<Second>()(p.second);
437
32
        return doris::util_hash::HashLen16(h1, h2);
438
32
    }
_ZNKSt4hashISt4pairIiN5doris10vectorized10PathInDataEEEclERKS4_
Line
Count
Source
434
36.9k
    size_t operator()(const pair<First, Second>& p) const {
435
36.9k
        size_t h1 = std::hash<First>()(p.first);
436
36.9k
        size_t h2 = std::hash<Second>()(p.second);
437
36.9k
        return doris::util_hash::HashLen16(h1, h2);
438
36.9k
    }
_ZNKSt4hashISt4pairIllEEclERKS1_
Line
Count
Source
434
64
    size_t operator()(const pair<First, Second>& p) const {
435
64
        size_t h1 = std::hash<First>()(p.first);
436
64
        size_t h2 = std::hash<Second>()(p.second);
437
64
        return doris::util_hash::HashLen16(h1, h2);
438
64
    }
Unexecuted instantiation: _ZNKSt4hashISt4pairIN5doris9TUniqueIdEiEEclERKS3_
439
};
440
441
#include "common/compile_check_end.h"