annotate win64-msvc/include/capnp/endian.h @ 58:eab3b14ddc95

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