Coverage Report

Created: 2026-01-05 15:06

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