annotate osx/include/capnp/endian.h @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 0994c39f1e94
children
rev   line source
cannam@62 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
cannam@62 2 // Licensed under the MIT License:
cannam@62 3 //
cannam@62 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
cannam@62 5 // of this software and associated documentation files (the "Software"), to deal
cannam@62 6 // in the Software without restriction, including without limitation the rights
cannam@62 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
cannam@62 8 // copies of the Software, and to permit persons to whom the Software is
cannam@62 9 // furnished to do so, subject to the following conditions:
cannam@62 10 //
cannam@62 11 // The above copyright notice and this permission notice shall be included in
cannam@62 12 // all copies or substantial portions of the Software.
cannam@62 13 //
cannam@62 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
cannam@62 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
cannam@62 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
cannam@62 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
cannam@62 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
cannam@62 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
cannam@62 20 // THE SOFTWARE.
cannam@62 21
cannam@62 22 #ifndef CAPNP_ENDIAN_H_
cannam@62 23 #define CAPNP_ENDIAN_H_
cannam@62 24
cannam@62 25 #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
cannam@62 26 #pragma GCC system_header
cannam@62 27 #endif
cannam@62 28
cannam@62 29 #include "common.h"
cannam@62 30 #include <inttypes.h>
cannam@62 31 #include <string.h> // memcpy
cannam@62 32
cannam@62 33 namespace capnp {
cannam@62 34 namespace _ { // private
cannam@62 35
cannam@62 36 // WireValue
cannam@62 37 //
cannam@62 38 // Wraps a primitive value as it appears on the wire. Namely, values are little-endian on the
cannam@62 39 // wire, because little-endian is the most common endianness in modern CPUs.
cannam@62 40 //
cannam@62 41 // Note: In general, code that depends cares about byte ordering is bad. See:
cannam@62 42 // http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
cannam@62 43 // Cap'n Proto is special because it is essentially doing compiler-like things, fussing over
cannam@62 44 // allocation and layout of memory, in order to squeeze out every last drop of performance.
cannam@62 45
cannam@62 46 #if _MSC_VER
cannam@62 47 // Assume Windows is little-endian.
cannam@62 48 //
cannam@62 49 // TODO(msvc): This is ugly. Maybe refactor later checks to be based on CAPNP_BYTE_ORDER or
cannam@62 50 // CAPNP_SWAP_BYTES or something, and define that in turn based on _MSC_VER or the GCC
cannam@62 51 // intrinsics.
cannam@62 52
cannam@62 53 #ifndef __ORDER_BIG_ENDIAN__
cannam@62 54 #define __ORDER_BIG_ENDIAN__ 4321
cannam@62 55 #endif
cannam@62 56 #ifndef __ORDER_LITTLE_ENDIAN__
cannam@62 57 #define __ORDER_LITTLE_ENDIAN__ 1234
cannam@62 58 #endif
cannam@62 59 #ifndef __BYTE_ORDER__
cannam@62 60 #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
cannam@62 61 #endif
cannam@62 62 #endif
cannam@62 63
cannam@62 64 #if CAPNP_REVERSE_ENDIAN
cannam@62 65 #define CAPNP_WIRE_BYTE_ORDER __ORDER_BIG_ENDIAN__
cannam@62 66 #define CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER __ORDER_LITTLE_ENDIAN__
cannam@62 67 #else
cannam@62 68 #define CAPNP_WIRE_BYTE_ORDER __ORDER_LITTLE_ENDIAN__
cannam@62 69 #define CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER __ORDER_BIG_ENDIAN__
cannam@62 70 #endif
cannam@62 71
cannam@62 72 #if defined(__BYTE_ORDER__) && \
cannam@62 73 __BYTE_ORDER__ == CAPNP_WIRE_BYTE_ORDER && \
cannam@62 74 !CAPNP_DISABLE_ENDIAN_DETECTION
cannam@62 75 // CPU is little-endian. We can just read/write the memory directly.
cannam@62 76
cannam@62 77 template <typename T>
cannam@62 78 class DirectWireValue {
cannam@62 79 public:
cannam@62 80 KJ_ALWAYS_INLINE(T get() const) { return value; }
cannam@62 81 KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
cannam@62 82
cannam@62 83 private:
cannam@62 84 T value;
cannam@62 85 };
cannam@62 86
cannam@62 87 template <typename T>
cannam@62 88 using WireValue = DirectWireValue<T>;
cannam@62 89 // To prevent ODR problems when endian-test, endian-reverse-test, and endian-fallback-test are
cannam@62 90 // linked together, we define each implementation with a different name and define an alias to the
cannam@62 91 // one we want to use.
cannam@62 92
cannam@62 93 #elif defined(__BYTE_ORDER__) && \
cannam@62 94 __BYTE_ORDER__ == CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER && \
cannam@62 95 defined(__GNUC__) && !CAPNP_DISABLE_ENDIAN_DETECTION
cannam@62 96 // Big-endian, but GCC's __builtin_bswap() is available.
cannam@62 97
cannam@62 98 // TODO(perf): Use dedicated instructions to read little-endian data on big-endian CPUs that have
cannam@62 99 // them.
cannam@62 100
cannam@62 101 // TODO(perf): Verify that this code optimizes reasonably. In particular, ensure that the
cannam@62 102 // compiler optimizes away the memcpy()s and keeps everything in registers.
cannam@62 103
cannam@62 104 template <typename T, size_t size = sizeof(T)>
cannam@62 105 class SwappingWireValue;
cannam@62 106
cannam@62 107 template <typename T>
cannam@62 108 class SwappingWireValue<T, 1> {
cannam@62 109 public:
cannam@62 110 KJ_ALWAYS_INLINE(T get() const) { return value; }
cannam@62 111 KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
cannam@62 112
cannam@62 113 private:
cannam@62 114 T value;
cannam@62 115 };
cannam@62 116
cannam@62 117 template <typename T>
cannam@62 118 class SwappingWireValue<T, 2> {
cannam@62 119 public:
cannam@62 120 KJ_ALWAYS_INLINE(T get() const) {
cannam@62 121 // Not all platforms have __builtin_bswap16() for some reason. In particular, it is missing
cannam@62 122 // on gcc-4.7.3-cygwin32 (but present on gcc-4.8.1-cygwin64).
cannam@62 123 uint16_t swapped = (value << 8) | (value >> 8);
cannam@62 124 T result;
cannam@62 125 memcpy(&result, &swapped, sizeof(T));
cannam@62 126 return result;
cannam@62 127 }
cannam@62 128 KJ_ALWAYS_INLINE(void set(T newValue)) {
cannam@62 129 uint16_t raw;
cannam@62 130 memcpy(&raw, &newValue, sizeof(T));
cannam@62 131 // Not all platforms have __builtin_bswap16() for some reason. In particular, it is missing
cannam@62 132 // on gcc-4.7.3-cygwin32 (but present on gcc-4.8.1-cygwin64).
cannam@62 133 value = (raw << 8) | (raw >> 8);
cannam@62 134 }
cannam@62 135
cannam@62 136 private:
cannam@62 137 uint16_t value;
cannam@62 138 };
cannam@62 139
cannam@62 140 template <typename T>
cannam@62 141 class SwappingWireValue<T, 4> {
cannam@62 142 public:
cannam@62 143 KJ_ALWAYS_INLINE(T get() const) {
cannam@62 144 uint32_t swapped = __builtin_bswap32(value);
cannam@62 145 T result;
cannam@62 146 memcpy(&result, &swapped, sizeof(T));
cannam@62 147 return result;
cannam@62 148 }
cannam@62 149 KJ_ALWAYS_INLINE(void set(T newValue)) {
cannam@62 150 uint32_t raw;
cannam@62 151 memcpy(&raw, &newValue, sizeof(T));
cannam@62 152 value = __builtin_bswap32(raw);
cannam@62 153 }
cannam@62 154
cannam@62 155 private:
cannam@62 156 uint32_t value;
cannam@62 157 };
cannam@62 158
cannam@62 159 template <typename T>
cannam@62 160 class SwappingWireValue<T, 8> {
cannam@62 161 public:
cannam@62 162 KJ_ALWAYS_INLINE(T get() const) {
cannam@62 163 uint64_t swapped = __builtin_bswap64(value);
cannam@62 164 T result;
cannam@62 165 memcpy(&result, &swapped, sizeof(T));
cannam@62 166 return result;
cannam@62 167 }
cannam@62 168 KJ_ALWAYS_INLINE(void set(T newValue)) {
cannam@62 169 uint64_t raw;
cannam@62 170 memcpy(&raw, &newValue, sizeof(T));
cannam@62 171 value = __builtin_bswap64(raw);
cannam@62 172 }
cannam@62 173
cannam@62 174 private:
cannam@62 175 uint64_t value;
cannam@62 176 };
cannam@62 177
cannam@62 178 template <typename T>
cannam@62 179 using WireValue = SwappingWireValue<T>;
cannam@62 180 // To prevent ODR problems when endian-test, endian-reverse-test, and endian-fallback-test are
cannam@62 181 // linked together, we define each implementation with a different name and define an alias to the
cannam@62 182 // one we want to use.
cannam@62 183
cannam@62 184 #else
cannam@62 185 // Unknown endianness. Fall back to bit shifts.
cannam@62 186
cannam@62 187 #if !CAPNP_DISABLE_ENDIAN_DETECTION
cannam@62 188 #if _MSC_VER
cannam@62 189 #pragma message("Couldn't detect endianness of your platform. Using unoptimized fallback implementation.")
cannam@62 190 #pragma message("Consider changing this code to detect your platform and send us a patch!")
cannam@62 191 #else
cannam@62 192 #warning "Couldn't detect endianness of your platform. Using unoptimized fallback implementation."
cannam@62 193 #warning "Consider changing this code to detect your platform and send us a patch!"
cannam@62 194 #endif
cannam@62 195 #endif // !CAPNP_DISABLE_ENDIAN_DETECTION
cannam@62 196
cannam@62 197 template <typename T, size_t size = sizeof(T)>
cannam@62 198 class ShiftingWireValue;
cannam@62 199
cannam@62 200 template <typename T>
cannam@62 201 class ShiftingWireValue<T, 1> {
cannam@62 202 public:
cannam@62 203 KJ_ALWAYS_INLINE(T get() const) { return value; }
cannam@62 204 KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
cannam@62 205
cannam@62 206 private:
cannam@62 207 T value;
cannam@62 208 };
cannam@62 209
cannam@62 210 template <typename T>
cannam@62 211 class ShiftingWireValue<T, 2> {
cannam@62 212 public:
cannam@62 213 KJ_ALWAYS_INLINE(T get() const) {
cannam@62 214 uint16_t raw = (static_cast<uint16_t>(bytes[0]) ) |
cannam@62 215 (static_cast<uint16_t>(bytes[1]) << 8);
cannam@62 216 T result;
cannam@62 217 memcpy(&result, &raw, sizeof(T));
cannam@62 218 return result;
cannam@62 219 }
cannam@62 220 KJ_ALWAYS_INLINE(void set(T newValue)) {
cannam@62 221 uint16_t raw;
cannam@62 222 memcpy(&raw, &newValue, sizeof(T));
cannam@62 223 bytes[0] = raw;
cannam@62 224 bytes[1] = raw >> 8;
cannam@62 225 }
cannam@62 226
cannam@62 227 private:
cannam@62 228 union {
cannam@62 229 byte bytes[2];
cannam@62 230 uint16_t align;
cannam@62 231 };
cannam@62 232 };
cannam@62 233
cannam@62 234 template <typename T>
cannam@62 235 class ShiftingWireValue<T, 4> {
cannam@62 236 public:
cannam@62 237 KJ_ALWAYS_INLINE(T get() const) {
cannam@62 238 uint32_t raw = (static_cast<uint32_t>(bytes[0]) ) |
cannam@62 239 (static_cast<uint32_t>(bytes[1]) << 8) |
cannam@62 240 (static_cast<uint32_t>(bytes[2]) << 16) |
cannam@62 241 (static_cast<uint32_t>(bytes[3]) << 24);
cannam@62 242 T result;
cannam@62 243 memcpy(&result, &raw, sizeof(T));
cannam@62 244 return result;
cannam@62 245 }
cannam@62 246 KJ_ALWAYS_INLINE(void set(T newValue)) {
cannam@62 247 uint32_t raw;
cannam@62 248 memcpy(&raw, &newValue, sizeof(T));
cannam@62 249 bytes[0] = raw;
cannam@62 250 bytes[1] = raw >> 8;
cannam@62 251 bytes[2] = raw >> 16;
cannam@62 252 bytes[3] = raw >> 24;
cannam@62 253 }
cannam@62 254
cannam@62 255 private:
cannam@62 256 union {
cannam@62 257 byte bytes[4];
cannam@62 258 uint32_t align;
cannam@62 259 };
cannam@62 260 };
cannam@62 261
cannam@62 262 template <typename T>
cannam@62 263 class ShiftingWireValue<T, 8> {
cannam@62 264 public:
cannam@62 265 KJ_ALWAYS_INLINE(T get() const) {
cannam@62 266 uint64_t raw = (static_cast<uint64_t>(bytes[0]) ) |
cannam@62 267 (static_cast<uint64_t>(bytes[1]) << 8) |
cannam@62 268 (static_cast<uint64_t>(bytes[2]) << 16) |
cannam@62 269 (static_cast<uint64_t>(bytes[3]) << 24) |
cannam@62 270 (static_cast<uint64_t>(bytes[4]) << 32) |
cannam@62 271 (static_cast<uint64_t>(bytes[5]) << 40) |
cannam@62 272 (static_cast<uint64_t>(bytes[6]) << 48) |
cannam@62 273 (static_cast<uint64_t>(bytes[7]) << 56);
cannam@62 274 T result;
cannam@62 275 memcpy(&result, &raw, sizeof(T));
cannam@62 276 return result;
cannam@62 277 }
cannam@62 278 KJ_ALWAYS_INLINE(void set(T newValue)) {
cannam@62 279 uint64_t raw;
cannam@62 280 memcpy(&raw, &newValue, sizeof(T));
cannam@62 281 bytes[0] = raw;
cannam@62 282 bytes[1] = raw >> 8;
cannam@62 283 bytes[2] = raw >> 16;
cannam@62 284 bytes[3] = raw >> 24;
cannam@62 285 bytes[4] = raw >> 32;
cannam@62 286 bytes[5] = raw >> 40;
cannam@62 287 bytes[6] = raw >> 48;
cannam@62 288 bytes[7] = raw >> 56;
cannam@62 289 }
cannam@62 290
cannam@62 291 private:
cannam@62 292 union {
cannam@62 293 byte bytes[8];
cannam@62 294 uint64_t align;
cannam@62 295 };
cannam@62 296 };
cannam@62 297
cannam@62 298 template <typename T>
cannam@62 299 using WireValue = ShiftingWireValue<T>;
cannam@62 300 // To prevent ODR problems when endian-test, endian-reverse-test, and endian-fallback-test are
cannam@62 301 // linked together, we define each implementation with a different name and define an alias to the
cannam@62 302 // one we want to use.
cannam@62 303
cannam@62 304 #endif
cannam@62 305
cannam@62 306 } // namespace _ (private)
cannam@62 307 } // namespace capnp
cannam@62 308
cannam@62 309 #endif // CAPNP_ENDIAN_H_