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