| 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 | |