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.
49inline 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
65template <typename T>
66class Portable
67{
68 T m_data;
69
70public:
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
122private:
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