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