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
32namespace capnp {
33namespace _ { // 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
76template <typename T>
77class DirectWireValue {
78public:
79 KJ_ALWAYS_INLINE(T get() const) { return value; }
80 KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
81
82private:
83 T value;
84};
85
86template <typename T>
87using 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
103template <typename T, size_t size = sizeof(T)>
104class SwappingWireValue;
105
106template <typename T>
107class SwappingWireValue<T, 1> {
108public:
109 KJ_ALWAYS_INLINE(T get() const) { return value; }
110 KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
111
112private:
113 T value;
114};
115
116template <typename T>
117class SwappingWireValue<T, 2> {
118public:
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
135private:
136 uint16_t value;
137};
138
139template <typename T>
140class SwappingWireValue<T, 4> {
141public:
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
154private:
155 uint32_t value;
156};
157
158template <typename T>
159class SwappingWireValue<T, 8> {
160public:
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
173private:
174 uint64_t value;
175};
176
177template <typename T>
178using 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
196template <typename T, size_t size = sizeof(T)>
197class ShiftingWireValue;
198
199template <typename T>
200class ShiftingWireValue<T, 1> {
201public:
202 KJ_ALWAYS_INLINE(T get() const) { return value; }
203 KJ_ALWAYS_INLINE(void set(T newValue)) { value = newValue; }
204
205private:
206 T value;
207};
208
209template <typename T>
210class ShiftingWireValue<T, 2> {
211public:
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
226private:
227 union {
228 byte bytes[2];
229 uint16_t align;
230 };
231};
232
233template <typename T>
234class ShiftingWireValue<T, 4> {
235public:
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
254private:
255 union {
256 byte bytes[4];
257 uint32_t align;
258 };
259};
260
261template <typename T>
262class ShiftingWireValue<T, 8> {
263public:
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
290private:
291 union {
292 byte bytes[8];
293 uint64_t align;
294 };
295};
296
297template <typename T>
298using 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