Coverage Report

Created: 2024-11-22 12:06

/root/doris/be/src/gutil/endian.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2005 Google Inc.
2
//
3
// Licensed to the Apache Software Foundation (ASF) under one
4
// or more contributor license agreements.  See the NOTICE file
5
// distributed with this work for additional information
6
// regarding copyright ownership.  The ASF licenses this file
7
// to you under the Apache License, Version 2.0 (the
8
// "License"); you may not use this file except in compliance
9
// with the License.  You may obtain a copy of the License at
10
//
11
//   http://www.apache.org/licenses/LICENSE-2.0
12
//
13
// Unless required by applicable law or agreed to in writing,
14
// software distributed under the License is distributed on an
15
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
// KIND, either express or implied.  See the License for the
17
// specific language governing permissions and limitations
18
// under the License.
19
//
20
// ---
21
//
22
//
23
// Utility functions that depend on bytesex. We define htonll and ntohll,
24
// as well as "Google" versions of all the standards: ghtonl, ghtons, and
25
// so on. These functions do exactly the same as their standard variants,
26
// but don't require including the dangerous netinet/in.h.
27
//
28
// Buffer routines will copy to and from buffers without causing
29
// a bus error when the architecture requires different byte alignments
30
31
#pragma once
32
33
#include <assert.h>
34
35
#include "gutil/int128.h"
36
#include "gutil/integral_types.h"
37
#include "gutil/port.h"
38
#include "vec/core/wide_integer.h"
39
40
57.1k
inline uint64 gbswap_64(uint64 host_int) {
41
57.1k
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__)
42
    // Adapted from /usr/include/byteswap.h.  Not available on Mac.
43
57.1k
    if (__builtin_constant_p(host_int)) {
44
0
        return __bswap_constant_64(host_int);
45
57.1k
    } else {
46
57.1k
        uint64 result;
47
57.1k
        __asm__("bswap %0" : "=r"(result) : "0"(host_int));
48
57.1k
        return result;
49
57.1k
    }
50
#elif defined(bswap_64)
51
    return bswap_64(host_int);
52
#else
53
    return static_cast<uint64>(bswap_32(static_cast<uint32>(host_int >> 32))) |
54
           (static_cast<uint64>(bswap_32(static_cast<uint32>(host_int))) << 32);
55
#endif // bswap_64
56
57.1k
}
57
58
208
inline unsigned __int128 gbswap_128(unsigned __int128 host_int) {
59
208
    return static_cast<unsigned __int128>(bswap_64(static_cast<uint64>(host_int >> 64))) |
60
208
           (static_cast<unsigned __int128>(bswap_64(static_cast<uint64>(host_int))) << 64);
61
208
}
62
63
1
inline wide::UInt256 gbswap_256(wide::UInt256 host_int) {
64
1
    wide::UInt256 result {gbswap_64(host_int.items[3]), gbswap_64(host_int.items[2]),
65
1
                          gbswap_64(host_int.items[1]), gbswap_64(host_int.items[0])};
66
1
    return result;
67
1
}
68
69
// Swap bytes of a 24-bit value.
70
209
inline uint32_t bswap_24(uint32_t x) {
71
209
    return ((x & 0x0000ffULL) << 16) | ((x & 0x00ff00ULL)) | ((x & 0xff0000ULL) >> 16);
72
209
}
73
74
#ifdef IS_LITTLE_ENDIAN
75
76
// Definitions for ntohl etc. that don't require us to include
77
// netinet/in.h. We wrap bswap_32 and bswap_16 in functions rather
78
// than just #defining them because in debug mode, gcc doesn't
79
// correctly handle the (rather involved) definitions of bswap_32.
80
// gcc guarantees that inline functions are as fast as macros, so
81
// this isn't a performance hit.
82
0
inline uint16 ghtons(uint16 x) {
83
0
    return bswap_16(x);
84
0
}
85
0
inline uint32 ghtonl(uint32 x) {
86
0
    return bswap_32(x);
87
0
}
88
0
inline uint64 ghtonll(uint64 x) {
89
0
    return gbswap_64(x);
90
0
}
91
92
#elif defined IS_BIG_ENDIAN
93
94
// These definitions are simpler on big-endian machines
95
// These are functions instead of macros to avoid self-assignment warnings
96
// on calls such as "i = ghtnol(i);".  This also provides type checking.
97
inline uint16 ghtons(uint16 x) {
98
    return x;
99
}
100
inline uint32 ghtonl(uint32 x) {
101
    return x;
102
}
103
inline uint64 ghtonll(uint64 x) {
104
    return x;
105
}
106
107
#else
108
#error "Unsupported bytesex: Either IS_BIG_ENDIAN or IS_LITTLE_ENDIAN must be defined"  // NOLINT
109
#endif // bytesex
110
111
// ntoh* and hton* are the same thing for any size and bytesex,
112
// since the function is an involution, i.e., its own inverse.
113
#if !defined(__APPLE__)
114
// This one is safe to take as it's an extension
115
#define htonll(x) ghtonll(x)
116
#define ntohll(x) htonll(x)
117
#endif
118
119
// Utilities to convert numbers between the current hosts's native byte
120
// order and little-endian byte order
121
//
122
// Load/Store methods are alignment safe
123
class LittleEndian {
124
public:
125
    // Conversion functions.
126
#ifdef IS_LITTLE_ENDIAN
127
128
0
    static uint16 FromHost16(uint16 x) { return x; }
129
0
    static uint16 ToHost16(uint16 x) { return x; }
130
131
0
    static uint32 FromHost32(uint32 x) { return x; }
132
5.50k
    static uint32 ToHost32(uint32 x) { return x; }
133
134
0
    static uint64 FromHost64(uint64 x) { return x; }
135
23.1k
    static uint64 ToHost64(uint64 x) { return x; }
136
137
0
    static unsigned __int128 FromHost128(unsigned __int128 x) { return x; }
138
0
    static unsigned __int128 ToHost128(unsigned __int128 x) { return x; }
139
140
0
    static wide::UInt256 FromHost256(wide::UInt256 x) { return x; }
141
0
    static wide::UInt256 ToHost256(wide::UInt256 x) { return x; }
142
143
0
    static bool IsLittleEndian() { return true; }
144
145
#elif defined IS_BIG_ENDIAN
146
147
    static uint16 FromHost16(uint16 x) { return bswap_16(x); }
148
    static uint16 ToHost16(uint16 x) { return bswap_16(x); }
149
150
    static uint32 FromHost32(uint32 x) { return bswap_32(x); }
151
    static uint32 ToHost32(uint32 x) { return bswap_32(x); }
152
153
    static uint64 FromHost64(uint64 x) { return gbswap_64(x); }
154
    static uint64 ToHost64(uint64 x) { return gbswap_64(x); }
155
156
    static unsigned __int128 FromHost128(unsigned __int128 x) { return gbswap_128(x); }
157
    static unsigned __int128 ToHost128(unsigned __int128 x) { return gbswap_128(x); }
158
159
    static wide::UInt256 FromHost256(wide::UInt256 x) { return gbswap_256(x); }
160
    static wide::UInt256 ToHost256(wide::UInt256 x) { return gbswap_256(x); }
161
162
    static bool IsLittleEndian() { return false; }
163
164
#endif /* ENDIAN */
165
166
    // Functions to do unaligned loads and stores in little-endian order.
167
0
    static uint16 Load16(const void* p) { return ToHost16(UNALIGNED_LOAD16(p)); }
168
169
0
    static void Store16(void* p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); }
170
171
5.50k
    static uint32 Load32(const void* p) { return ToHost32(UNALIGNED_LOAD32(p)); }
172
173
0
    static void Store32(void* p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); }
174
175
23.1k
    static uint64 Load64(const void* p) { return ToHost64(UNALIGNED_LOAD64(p)); }
176
177
    // Build a uint64 from 1-8 bytes.
178
    // 8 * len least significant bits are loaded from the memory with
179
    // LittleEndian order. The 64 - 8 * len most significant bits are
180
    // set all to 0.
181
    // In latex-friendly words, this function returns:
182
    //     $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned.
183
    //
184
    // This function is equivalent with:
185
    // uint64 val = 0;
186
    // memcpy(&val, p, len);
187
    // return ToHost64(val);
188
    // TODO(user): write a small benchmark and benchmark the speed
189
    // of a memcpy based approach.
190
    //
191
    // For speed reasons this function does not work for len == 0.
192
    // The caller needs to guarantee that 1 <= len <= 8.
193
0
    static uint64 Load64VariableLength(const void* const p, int len) {
194
0
        assert(len >= 1 && len <= 8);
195
0
        const char* const buf = static_cast<const char*>(p);
196
0
        uint64 val = 0;
197
0
        --len;
198
0
        do {
199
0
            val = (val << 8) | buf[len];
200
0
            // (--len >= 0) is about 10 % faster than (len--) in some benchmarks.
201
0
        } while (--len >= 0);
202
0
        // No ToHost64(...) needed. The bytes are accessed in little-endian manner
203
0
        // on every architecture.
204
0
        return val;
205
0
    }
206
207
0
    static void Store64(void* p, uint64 v) { UNALIGNED_STORE64(p, FromHost64(v)); }
208
209
0
    static uint128 Load128(const void* p) {
210
0
        return uint128(ToHost64(UNALIGNED_LOAD64(reinterpret_cast<const uint64*>(p) + 1)),
211
0
                       ToHost64(UNALIGNED_LOAD64(p)));
212
0
    }
213
214
0
    static void Store128(void* p, const uint128 v) {
215
0
        UNALIGNED_STORE64(p, FromHost64(Uint128Low64(v)));
216
0
        UNALIGNED_STORE64(reinterpret_cast<uint64*>(p) + 1, FromHost64(Uint128High64(v)));
217
0
    }
218
219
    // Build a uint128 from 1-16 bytes.
220
    // 8 * len least significant bits are loaded from the memory with
221
    // LittleEndian order. The 128 - 8 * len most significant bits are
222
    // set all to 0.
223
0
    static uint128 Load128VariableLength(const void* p, int len) {
224
0
        if (len <= 8) {
225
0
            return uint128(Load64VariableLength(p, len));
226
0
        } else {
227
0
            return uint128(Load64VariableLength(static_cast<const char*>(p) + 8, len - 8),
228
0
                           Load64(p));
229
0
        }
230
0
    }
231
232
    // Load & Store in machine's word size.
233
0
    static uword_t LoadUnsignedWord(const void* p) {
234
0
        if (sizeof(uword_t) == 8)
235
0
            return Load64(p);
236
0
        else
237
0
            return Load32(p);
238
0
    }
239
240
0
    static void StoreUnsignedWord(void* p, uword_t v) {
241
0
        if (sizeof(v) == 8)
242
0
            Store64(p, v);
243
0
        else
244
0
            Store32(p, v);
245
0
    }
246
};
247
248
// Utilities to convert numbers between the current hosts's native byte
249
// order and big-endian byte order (same as network byte order)
250
//
251
// Load/Store methods are alignment safe
252
class BigEndian {
253
public:
254
#ifdef IS_LITTLE_ENDIAN
255
256
226
    static uint16 FromHost16(uint16 x) { return bswap_16(x); }
257
1
    static uint16 ToHost16(uint16 x) { return bswap_16(x); }
258
259
209
    static uint32 FromHost24(uint32 x) { return bswap_24(x); }
260
0
    static uint32 ToHost24(uint32 x) { return bswap_24(x); }
261
262
352k
    static uint32 FromHost32(uint32 x) { return bswap_32(x); }
263
1
    static uint32 ToHost32(uint32 x) { return bswap_32(x); }
264
265
55.9k
    static uint64 FromHost64(uint64 x) { return gbswap_64(x); }
266
1.19k
    static uint64 ToHost64(uint64 x) { return gbswap_64(x); }
267
268
207
    static unsigned __int128 FromHost128(unsigned __int128 x) { return gbswap_128(x); }
269
1
    static unsigned __int128 ToHost128(unsigned __int128 x) { return gbswap_128(x); }
270
271
0
    static wide::UInt256 FromHost256(wide::UInt256 x) { return gbswap_256(x); }
272
1
    static wide::UInt256 ToHost256(wide::UInt256 x) { return gbswap_256(x); }
273
274
0
    static bool IsLittleEndian() { return true; }
275
276
#elif defined IS_BIG_ENDIAN
277
278
    static uint16 FromHost16(uint16 x) { return x; }
279
    static uint16 ToHost16(uint16 x) { return x; }
280
281
    static uint32 FromHost24(uint32 x) { return x; }
282
    static uint32 ToHost24(uint32 x) { return x; }
283
284
    static uint32 FromHost32(uint32 x) { return x; }
285
    static uint32 ToHost32(uint32 x) { return x; }
286
287
    static uint64 FromHost64(uint64 x) { return x; }
288
    static uint64 ToHost64(uint64 x) { return x; }
289
290
    static uint128 FromHost128(uint128 x) { return x; }
291
    static uint128 ToHost128(uint128 x) { return x; }
292
293
    static wide::UInt256 FromHost256(wide::UInt256 x) { return x; }
294
    static wide::UInt256 ToHost256(wide::UInt256 x) { return x; }
295
296
    static bool IsLittleEndian() { return false; }
297
298
#endif /* ENDIAN */
299
    // Functions to do unaligned loads and stores in little-endian order.
300
0
    static uint16 Load16(const void* p) { return ToHost16(UNALIGNED_LOAD16(p)); }
301
302
0
    static void Store16(void* p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); }
303
304
0
    static uint32 Load32(const void* p) { return ToHost32(UNALIGNED_LOAD32(p)); }
305
306
0
    static void Store32(void* p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); }
307
308
0
    static uint64 Load64(const void* p) { return ToHost64(UNALIGNED_LOAD64(p)); }
309
310
    // Build a uint64 from 1-8 bytes.
311
    // 8 * len least significant bits are loaded from the memory with
312
    // BigEndian order. The 64 - 8 * len most significant bits are
313
    // set all to 0.
314
    // In latex-friendly words, this function returns:
315
    //     $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned.
316
    //
317
    // This function is equivalent with:
318
    // uint64 val = 0;
319
    // memcpy(&val, p, len);
320
    // return ToHost64(val);
321
    // TODO(user): write a small benchmark and benchmark the speed
322
    // of a memcpy based approach.
323
    //
324
    // For speed reasons this function does not work for len == 0.
325
    // The caller needs to guarantee that 1 <= len <= 8.
326
0
    static uint64 Load64VariableLength(const void* const p, int len) {
327
0
        assert(len >= 1 && len <= 8);
328
0
        uint64 val = Load64(p);
329
0
        uint64 mask = 0;
330
0
        --len;
331
0
        do {
332
0
            mask = (mask << 8) | 0xff;
333
0
            // (--len >= 0) is about 10 % faster than (len--) in some benchmarks.
334
0
        } while (--len >= 0);
335
0
        return val & mask;
336
0
    }
337
338
0
    static void Store64(void* p, uint64 v) { UNALIGNED_STORE64(p, FromHost64(v)); }
339
340
0
    static uint128 Load128(const void* p) {
341
0
        return uint128(ToHost64(UNALIGNED_LOAD64(p)),
342
0
                       ToHost64(UNALIGNED_LOAD64(reinterpret_cast<const uint64*>(p) + 1)));
343
0
    }
344
345
0
    static void Store128(void* p, const uint128 v) {
346
0
        UNALIGNED_STORE64(p, FromHost64(Uint128High64(v)));
347
0
        UNALIGNED_STORE64(reinterpret_cast<uint64*>(p) + 1, FromHost64(Uint128Low64(v)));
348
0
    }
349
350
    // Build a uint128 from 1-16 bytes.
351
    // 8 * len least significant bits are loaded from the memory with
352
    // BigEndian order. The 128 - 8 * len most significant bits are
353
    // set all to 0.
354
0
    static uint128 Load128VariableLength(const void* p, int len) {
355
0
        if (len <= 8) {
356
0
            return uint128(Load64VariableLength(static_cast<const char*>(p) + 8, len));
357
0
        } else {
358
0
            return uint128(Load64VariableLength(p, len - 8),
359
0
                           Load64(static_cast<const char*>(p) + 8));
360
0
        }
361
0
    }
362
363
    // Load & Store in machine's word size.
364
0
    static uword_t LoadUnsignedWord(const void* p) {
365
0
        if (sizeof(uword_t) == 8)
366
0
            return Load64(p);
367
0
        else
368
0
            return Load32(p);
369
0
    }
370
371
0
    static void StoreUnsignedWord(void* p, uword_t v) {
372
0
        if (sizeof(uword_t) == 8)
373
0
            Store64(p, v);
374
0
        else
375
0
            Store32(p, v);
376
0
    }
377
}; // BigEndian
378
379
// Network byte order is big-endian
380
typedef BigEndian NetworkByteOrder;