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