capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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