1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | #ifndef __DBG_PORTABLE_INCLUDED |
6 | #define __DBG_PORTABLE_INCLUDED |
7 | |
8 | // |
9 | // This header defines the template class Portable<T> which is designed to wrap primitive types in such a way |
10 | // that their physical representation is in a canonical format that can be safely transferred between hosts on |
11 | // different platforms. |
12 | // |
13 | // This is achieved by storing the wrapped datum in little-endian format (since most of our platforms are |
14 | // little-endian this makes the most sense from a performance perspective). On little-endian platforms the |
15 | // wrapper code will become a no-op and get optimized away by the compiler. On big-endian platforms |
16 | // assignments to a Portable<T> value will reverse the order of the bytes in the T value and reverse them back |
17 | // again on a read. |
18 | // |
19 | // Portable<T> is typically used to wrap the fields of structures sent directly over a network channel. In |
20 | // this fashion many of the values that would otherwise require manual endian-ness fixups are now marshalled |
21 | // and unmarshalled transparent right at the network transition. |
22 | // |
23 | // Care must be taken to identify any code that takes the address of a Portable<T>, since this is not |
24 | // generally safe (it could expose naive code to the network encoded form of the datum). In such situations |
25 | // the code is normally re-written to create a temporary instance of T on the stack, initialized to the |
26 | // correct host value by reading from the Portable<T> field. The address of this variable can now be taken |
27 | // safely (assuming its value is required only for some lexically scoped operation). Once the value is no |
28 | // longer being used, and if there is a possibility that the value may have been updated, the new value can be |
29 | // copied back into the Portable<T> field. |
30 | // |
31 | // Note that this header uses very basic data types only as it is included from both Win32/PAL code and native |
32 | // Mac code. |
33 | // |
34 | |
35 | #if BIGENDIAN || __BIG_ENDIAN__ |
36 | #define DBG_BYTE_SWAP_REQUIRED |
37 | #endif |
38 | |
39 | #if defined(_ASSERTE) |
40 | #define _PASSERT(_expr) _ASSERTE(_expr) |
41 | #elif defined(assert) |
42 | #define _PASSERT(_expr) assert(_expr) |
43 | #else |
44 | #define _PASSERT(_expr) |
45 | #endif |
46 | |
47 | // Lowest level helper used to reverse the order of a sequence of bytes, either as an in-place operation or as |
48 | // part of a copy. |
49 | inline void ByteSwapPrimitive(const void *pSrc, void *pDst, unsigned int cbSize) |
50 | { |
51 | _PASSERT(cbSize == 2 || cbSize == 4 || cbSize == 8); |
52 | |
53 | unsigned char *pbSrc = (unsigned char*)pSrc; |
54 | unsigned char *pbDst = (unsigned char*)pDst; |
55 | |
56 | for (unsigned int i = 0; i < (cbSize / 2); i++) |
57 | { |
58 | unsigned int j = cbSize - i - 1; |
59 | unsigned char bTemp = pbSrc[i]; |
60 | pbDst[i] = pbSrc[j]; |
61 | pbDst[j] = bTemp; |
62 | } |
63 | } |
64 | |
65 | template <typename T> |
66 | class Portable |
67 | { |
68 | T m_data; |
69 | |
70 | public: |
71 | // No constructors -- this will be used in unions. |
72 | |
73 | // Convert data to portable format on assignment. |
74 | T operator = (T value) |
75 | { |
76 | _PASSERT(sizeof(value) <= sizeof(double)); |
77 | #ifdef DBG_BYTE_SWAP_REQUIRED |
78 | m_data = ByteSwap(value); |
79 | #else // DBG_BYTE_SWAP_REQUIRED |
80 | m_data = value; |
81 | #endif // DBG_BYTE_SWAP_REQUIRED |
82 | return value; |
83 | } |
84 | |
85 | // Return data in native format on access. |
86 | operator T () const |
87 | { |
88 | #ifdef DBG_BYTE_SWAP_REQUIRED |
89 | return ByteSwap(m_data); |
90 | #else // DBG_BYTE_SWAP_REQUIRED |
91 | return m_data; |
92 | #endif // DBG_BYTE_SWAP_REQUIRED |
93 | } |
94 | |
95 | bool operator == (T other) const |
96 | { |
97 | #ifdef DBG_BYTE_SWAP_REQUIRED |
98 | return ByteSwap(m_data) == other; |
99 | #else // DBG_BYTE_SWAP_REQUIRED |
100 | return m_data == other; |
101 | #endif // DBG_BYTE_SWAP_REQUIRED |
102 | } |
103 | |
104 | bool operator != (T other) const |
105 | { |
106 | #ifdef DBG_BYTE_SWAP_REQUIRED |
107 | return ByteSwap(m_data) != other; |
108 | #else // DBG_BYTE_SWAP_REQUIRED |
109 | return m_data != other; |
110 | #endif // DBG_BYTE_SWAP_REQUIRED |
111 | } |
112 | |
113 | T Unwrap() |
114 | { |
115 | #ifdef DBG_BYTE_SWAP_REQUIRED |
116 | return ByteSwap(m_data); |
117 | #else // DBG_BYTE_SWAP_REQUIRED |
118 | return m_data; |
119 | #endif // DBG_BYTE_SWAP_REQUIRED |
120 | } |
121 | |
122 | private: |
123 | #ifdef DBG_BYTE_SWAP_REQUIRED |
124 | // Big endian helper routine to swap the order of bytes of an arbitrary sized type |
125 | // (though obviously this type must be an integral primitive for this to make any |
126 | // sense). |
127 | static T ByteSwap(T inval) |
128 | { |
129 | if (sizeof(T) > 1) |
130 | { |
131 | T outval; |
132 | ByteSwapPrimitive(&inval, &outval, sizeof(T)); |
133 | return outval; |
134 | } |
135 | else |
136 | return inval; |
137 | } |
138 | #endif // DBG_BYTE_SWAP_REQUIRED |
139 | }; |
140 | |
141 | #endif // !__DBG_PORTABLE_INCLUDED |
142 | |