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