Coverage Report

Created: 2024-11-21 14:00

/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/integral_types.h"
36
#include "gutil/port.h"
37
#include "vec/core/wide_integer.h"
38
39
57.2k
inline uint64 gbswap_64(uint64 host_int) {
40
57.2k
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__)
41
    // Adapted from /usr/include/byteswap.h.  Not available on Mac.
42
57.2k
    if (__builtin_constant_p(host_int)) {
43
0
        return __bswap_constant_64(host_int);
44
57.2k
    } else {
45
57.2k
        uint64 result;
46
57.2k
        __asm__("bswap %0" : "=r"(result) : "0"(host_int));
47
57.2k
        return result;
48
57.2k
    }
49
#elif defined(bswap_64)
50
    return bswap_64(host_int);
51
#else
52
    return static_cast<uint64>(bswap_32(static_cast<uint32>(host_int >> 32))) |
53
           (static_cast<uint64>(bswap_32(static_cast<uint32>(host_int))) << 32);
54
#endif // bswap_64
55
57.2k
}
56
57
208
inline unsigned __int128 gbswap_128(unsigned __int128 host_int) {
58
208
    return static_cast<unsigned __int128>(bswap_64(static_cast<uint64>(host_int >> 64))) |
59
208
           (static_cast<unsigned __int128>(bswap_64(static_cast<uint64>(host_int))) << 64);
60
208
}
61
62
1
inline wide::UInt256 gbswap_256(wide::UInt256 host_int) {
63
1
    wide::UInt256 result {gbswap_64(host_int.items[3]), gbswap_64(host_int.items[2]),
64
1
                          gbswap_64(host_int.items[1]), gbswap_64(host_int.items[0])};
65
1
    return result;
66
1
}
67
68
// Swap bytes of a 24-bit value.
69
209
inline uint32_t bswap_24(uint32_t x) {
70
209
    return ((x & 0x0000ffULL) << 16) | ((x & 0x00ff00ULL)) | ((x & 0xff0000ULL) >> 16);
71
209
}
72
73
#ifdef IS_LITTLE_ENDIAN
74
75
// Definitions for ntohl etc. that don't require us to include
76
// netinet/in.h. We wrap bswap_32 and bswap_16 in functions rather
77
// than just #defining them because in debug mode, gcc doesn't
78
// correctly handle the (rather involved) definitions of bswap_32.
79
// gcc guarantees that inline functions are as fast as macros, so
80
// this isn't a performance hit.
81
0
inline uint16 ghtons(uint16 x) {
82
0
    return bswap_16(x);
83
0
}
84
0
inline uint32 ghtonl(uint32 x) {
85
0
    return bswap_32(x);
86
0
}
87
0
inline uint64 ghtonll(uint64 x) {
88
0
    return gbswap_64(x);
89
0
}
90
91
#elif defined IS_BIG_ENDIAN
92
93
// These definitions are simpler on big-endian machines
94
// These are functions instead of macros to avoid self-assignment warnings
95
// on calls such as "i = ghtnol(i);".  This also provides type checking.
96
inline uint16 ghtons(uint16 x) {
97
    return x;
98
}
99
inline uint32 ghtonl(uint32 x) {
100
    return x;
101
}
102
inline uint64 ghtonll(uint64 x) {
103
    return x;
104
}
105
106
#else
107
#error "Unsupported bytesex: Either IS_BIG_ENDIAN or IS_LITTLE_ENDIAN must be defined"  // NOLINT
108
#endif // bytesex
109
110
// ntoh* and hton* are the same thing for any size and bytesex,
111
// since the function is an involution, i.e., its own inverse.
112
#if !defined(__APPLE__)
113
// This one is safe to take as it's an extension
114
#define htonll(x) ghtonll(x)
115
#define ntohll(x) htonll(x)
116
#endif
117
118
// Utilities to convert numbers between the current hosts's native byte
119
// order and little-endian byte order
120
//
121
// Load/Store methods are alignment safe
122
class LittleEndian {
123
public:
124
    // Conversion functions.
125
#ifdef IS_LITTLE_ENDIAN
126
127
0
    static uint16 FromHost16(uint16 x) { return x; }
128
0
    static uint16 ToHost16(uint16 x) { return x; }
129
130
0
    static uint32 FromHost32(uint32 x) { return x; }
131
8
    static uint32 ToHost32(uint32 x) { return x; }
132
133
0
    static uint64 FromHost64(uint64 x) { return x; }
134
0
    static uint64 ToHost64(uint64 x) { return x; }
135
136
0
    static unsigned __int128 FromHost128(unsigned __int128 x) { return x; }
137
0
    static unsigned __int128 ToHost128(unsigned __int128 x) { return x; }
138
139
0
    static wide::UInt256 FromHost256(wide::UInt256 x) { return x; }
140
0
    static wide::UInt256 ToHost256(wide::UInt256 x) { return x; }
141
142
0
    static bool IsLittleEndian() { return true; }
143
144
#elif defined IS_BIG_ENDIAN
145
146
    static uint16 FromHost16(uint16 x) { return bswap_16(x); }
147
    static uint16 ToHost16(uint16 x) { return bswap_16(x); }
148
149
    static uint32 FromHost32(uint32 x) { return bswap_32(x); }
150
    static uint32 ToHost32(uint32 x) { return bswap_32(x); }
151
152
    static uint64 FromHost64(uint64 x) { return gbswap_64(x); }
153
    static uint64 ToHost64(uint64 x) { return gbswap_64(x); }
154
155
    static unsigned __int128 FromHost128(unsigned __int128 x) { return gbswap_128(x); }
156
    static unsigned __int128 ToHost128(unsigned __int128 x) { return gbswap_128(x); }
157
158
    static wide::UInt256 FromHost256(wide::UInt256 x) { return gbswap_256(x); }
159
    static wide::UInt256 ToHost256(wide::UInt256 x) { return gbswap_256(x); }
160
161
    static bool IsLittleEndian() { return false; }
162
163
#endif /* ENDIAN */
164
165
    // Functions to do unaligned loads and stores in little-endian order.
166
0
    static uint16 Load16(const void* p) { return ToHost16(UNALIGNED_LOAD16(p)); }
167
168
0
    static void Store16(void* p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); }
169
170
8
    static uint32 Load32(const void* p) { return ToHost32(UNALIGNED_LOAD32(p)); }
171
172
0
    static void Store32(void* p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); }
173
174
0
    static uint64 Load64(const void* p) { return ToHost64(UNALIGNED_LOAD64(p)); }
175
176
    // Build a uint64 from 1-8 bytes.
177
    // 8 * len least significant bits are loaded from the memory with
178
    // LittleEndian order. The 64 - 8 * len most significant bits are
179
    // set all to 0.
180
    // In latex-friendly words, this function returns:
181
    //     $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned.
182
    //
183
    // This function is equivalent with:
184
    // uint64 val = 0;
185
    // memcpy(&val, p, len);
186
    // return ToHost64(val);
187
    // TODO(user): write a small benchmark and benchmark the speed
188
    // of a memcpy based approach.
189
    //
190
    // For speed reasons this function does not work for len == 0.
191
    // The caller needs to guarantee that 1 <= len <= 8.
192
0
    static uint64 Load64VariableLength(const void* const p, int len) {
193
0
        assert(len >= 1 && len <= 8);
194
0
        const char* const buf = static_cast<const char*>(p);
195
0
        uint64 val = 0;
196
0
        --len;
197
0
        do {
198
0
            val = (val << 8) | buf[len];
199
0
            // (--len >= 0) is about 10 % faster than (len--) in some benchmarks.
200
0
        } while (--len >= 0);
201
0
        // No ToHost64(...) needed. The bytes are accessed in little-endian manner
202
0
        // on every architecture.
203
0
        return val;
204
0
    }
205
206
0
    static void Store64(void* p, uint64 v) { UNALIGNED_STORE64(p, FromHost64(v)); }
207
208
    // Load & Store in machine's word size.
209
0
    static uword_t LoadUnsignedWord(const void* p) {
210
0
        if (sizeof(uword_t) == 8)
211
0
            return Load64(p);
212
0
        else
213
0
            return Load32(p);
214
0
    }
215
216
0
    static void StoreUnsignedWord(void* p, uword_t v) {
217
0
        if (sizeof(v) == 8)
218
0
            Store64(p, v);
219
0
        else
220
0
            Store32(p, v);
221
0
    }
222
};
223
224
// Utilities to convert numbers between the current hosts's native byte
225
// order and big-endian byte order (same as network byte order)
226
//
227
// Load/Store methods are alignment safe
228
class BigEndian {
229
public:
230
#ifdef IS_LITTLE_ENDIAN
231
232
233
    static uint16 FromHost16(uint16 x) { return bswap_16(x); }
233
1
    static uint16 ToHost16(uint16 x) { return bswap_16(x); }
234
235
209
    static uint32 FromHost24(uint32 x) { return bswap_24(x); }
236
0
    static uint32 ToHost24(uint32 x) { return bswap_24(x); }
237
238
868k
    static uint32 FromHost32(uint32 x) { return bswap_32(x); }
239
1
    static uint32 ToHost32(uint32 x) { return bswap_32(x); }
240
241
56.0k
    static uint64 FromHost64(uint64 x) { return gbswap_64(x); }
242
1.19k
    static uint64 ToHost64(uint64 x) { return gbswap_64(x); }
243
244
207
    static unsigned __int128 FromHost128(unsigned __int128 x) { return gbswap_128(x); }
245
1
    static unsigned __int128 ToHost128(unsigned __int128 x) { return gbswap_128(x); }
246
247
0
    static wide::UInt256 FromHost256(wide::UInt256 x) { return gbswap_256(x); }
248
1
    static wide::UInt256 ToHost256(wide::UInt256 x) { return gbswap_256(x); }
249
250
0
    static bool IsLittleEndian() { return true; }
251
252
#elif defined IS_BIG_ENDIAN
253
254
    static uint16 FromHost16(uint16 x) { return x; }
255
    static uint16 ToHost16(uint16 x) { return x; }
256
257
    static uint32 FromHost24(uint32 x) { return x; }
258
    static uint32 ToHost24(uint32 x) { return x; }
259
260
    static uint32 FromHost32(uint32 x) { return x; }
261
    static uint32 ToHost32(uint32 x) { return x; }
262
263
    static uint64 FromHost64(uint64 x) { return x; }
264
    static uint64 ToHost64(uint64 x) { return x; }
265
266
    static wide::UInt256 FromHost256(wide::UInt256 x) { return x; }
267
    static wide::UInt256 ToHost256(wide::UInt256 x) { return x; }
268
269
    static bool IsLittleEndian() { return false; }
270
271
#endif /* ENDIAN */
272
    // Functions to do unaligned loads and stores in little-endian order.
273
0
    static uint16 Load16(const void* p) { return ToHost16(UNALIGNED_LOAD16(p)); }
274
275
0
    static void Store16(void* p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); }
276
277
0
    static uint32 Load32(const void* p) { return ToHost32(UNALIGNED_LOAD32(p)); }
278
279
0
    static void Store32(void* p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); }
280
281
0
    static uint64 Load64(const void* p) { return ToHost64(UNALIGNED_LOAD64(p)); }
282
283
    // Build a uint64 from 1-8 bytes.
284
    // 8 * len least significant bits are loaded from the memory with
285
    // BigEndian order. The 64 - 8 * len most significant bits are
286
    // set all to 0.
287
    // In latex-friendly words, this function returns:
288
    //     $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned.
289
    //
290
    // This function is equivalent with:
291
    // uint64 val = 0;
292
    // memcpy(&val, p, len);
293
    // return ToHost64(val);
294
    // TODO(user): write a small benchmark and benchmark the speed
295
    // of a memcpy based approach.
296
    //
297
    // For speed reasons this function does not work for len == 0.
298
    // The caller needs to guarantee that 1 <= len <= 8.
299
0
    static uint64 Load64VariableLength(const void* const p, int len) {
300
0
        assert(len >= 1 && len <= 8);
301
0
        uint64 val = Load64(p);
302
0
        uint64 mask = 0;
303
0
        --len;
304
0
        do {
305
0
            mask = (mask << 8) | 0xff;
306
0
            // (--len >= 0) is about 10 % faster than (len--) in some benchmarks.
307
0
        } while (--len >= 0);
308
0
        return val & mask;
309
0
    }
310
311
0
    static void Store64(void* p, uint64 v) { UNALIGNED_STORE64(p, FromHost64(v)); }
312
313
    // Load & Store in machine's word size.
314
0
    static uword_t LoadUnsignedWord(const void* p) {
315
0
        if (sizeof(uword_t) == 8)
316
0
            return Load64(p);
317
0
        else
318
0
            return Load32(p);
319
0
    }
320
321
0
    static void StoreUnsignedWord(void* p, uword_t v) {
322
0
        if (sizeof(uword_t) == 8)
323
0
            Store64(p, v);
324
0
        else
325
0
            Store32(p, v);
326
0
    }
327
}; // BigEndian
328
329
// Network byte order is big-endian
330
typedef BigEndian NetworkByteOrder;