diff options
Diffstat (limited to 'src/inc/dbgportable.h')
-rw-r--r-- | src/inc/dbgportable.h | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/inc/dbgportable.h b/src/inc/dbgportable.h new file mode 100644 index 0000000000..b58700905d --- /dev/null +++ b/src/inc/dbgportable.h @@ -0,0 +1,142 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#ifndef __DBG_PORTABLE_INCLUDED +#define __DBG_PORTABLE_INCLUDED + +// +// This header defines the template class Portable<T> which is designed to wrap primitive types in such a way +// that their physical representation is in a canonical format that can be safely transferred between hosts on +// different platforms. +// +// This is achieved by storing the wrapped datum in little-endian format (since most of our platforms are +// little-endian this makes the most sense from a performance perspective). On little-endian platforms the +// wrapper code will become a no-op and get optimized away by the compiler. On big-endian platforms +// assignments to a Portable<T> value will reverse the order of the bytes in the T value and reverse them back +// again on a read. +// +// Portable<T> is typically used to wrap the fields of structures sent directly over a network channel. In +// this fashion many of the values that would otherwise require manual endian-ness fixups are now marshalled +// and unmarshalled transparent right at the network transition. +// +// Care must be taken to identify any code that takes the address of a Portable<T>, since this is not +// generally safe (it could expose naive code to the network encoded form of the datum). In such situations +// the code is normally re-written to create a temporary instance of T on the stack, initialized to the +// correct host value by reading from the Portable<T> field. The address of this variable can now be taken +// safely (assuming its value is required only for some lexically scoped operation). Once the value is no +// longer being used, and if there is a possibility that the value may have been updated, the new value can be +// copied back into the Portable<T> field. +// +// Note that this header uses very basic data types only as it is included from both Win32/PAL code and native +// Mac code. +// + +#if BIGENDIAN || __BIG_ENDIAN__ +#define DBG_BYTE_SWAP_REQUIRED +#endif + +#if defined(_ASSERTE) +#define _PASSERT(_expr) _ASSERTE(_expr) +#elif defined(assert) +#define _PASSERT(_expr) assert(_expr) +#else +#define _PASSERT(_expr) +#endif + +// Lowest level helper used to reverse the order of a sequence of bytes, either as an in-place operation or as +// part of a copy. +inline void ByteSwapPrimitive(const void *pSrc, void *pDst, unsigned int cbSize) +{ + _PASSERT(cbSize == 2 || cbSize == 4 || cbSize == 8); + + unsigned char *pbSrc = (unsigned char*)pSrc; + unsigned char *pbDst = (unsigned char*)pDst; + + for (unsigned int i = 0; i < (cbSize / 2); i++) + { + unsigned int j = cbSize - i - 1; + unsigned char bTemp = pbSrc[i]; + pbDst[i] = pbSrc[j]; + pbDst[j] = bTemp; + } +} + +template <typename T> +class Portable +{ + T m_data; + +public: + // No constructors -- this will be used in unions. + + // Convert data to portable format on assignment. + T operator = (T value) + { + _PASSERT(sizeof(value) <= sizeof(double)); +#ifdef DBG_BYTE_SWAP_REQUIRED + m_data = ByteSwap(value); +#else // DBG_BYTE_SWAP_REQUIRED + m_data = value; +#endif // DBG_BYTE_SWAP_REQUIRED + return value; + } + + // Return data in native format on access. + operator T () const + { +#ifdef DBG_BYTE_SWAP_REQUIRED + return ByteSwap(m_data); +#else // DBG_BYTE_SWAP_REQUIRED + return m_data; +#endif // DBG_BYTE_SWAP_REQUIRED + } + + bool operator == (T other) const + { +#ifdef DBG_BYTE_SWAP_REQUIRED + return ByteSwap(m_data) == other; +#else // DBG_BYTE_SWAP_REQUIRED + return m_data == other; +#endif // DBG_BYTE_SWAP_REQUIRED + } + + bool operator != (T other) const + { +#ifdef DBG_BYTE_SWAP_REQUIRED + return ByteSwap(m_data) != other; +#else // DBG_BYTE_SWAP_REQUIRED + return m_data != other; +#endif // DBG_BYTE_SWAP_REQUIRED + } + + T Unwrap() + { +#ifdef DBG_BYTE_SWAP_REQUIRED + return ByteSwap(m_data); +#else // DBG_BYTE_SWAP_REQUIRED + return m_data; +#endif // DBG_BYTE_SWAP_REQUIRED + } + +private: +#ifdef DBG_BYTE_SWAP_REQUIRED + // Big endian helper routine to swap the order of bytes of an arbitrary sized type + // (though obviously this type must be an integral primitive for this to make any + // sense). + static T ByteSwap(T inval) + { + if (sizeof(T) > 1) + { + T outval; + ByteSwapPrimitive(&inval, &outval, sizeof(T)); + return outval; + } + else + return inval; + } +#endif // DBG_BYTE_SWAP_REQUIRED +}; + +#endif // !__DBG_PORTABLE_INCLUDED |