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