//****************************************************************************************************** // EndianOrderTest.cs - Gbtc // // Copyright © 2010, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 02/13/2014 - J. Ritchie Carroll // Generated original version of source code. // //****************************************************************************************************** // Uncomment only for hard coded testing of BigEndian architecture code //#define ForceBigEndianTesting using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GSF.Core.Tests { [TestClass] public class EndianOrderTest { #region [ Byte Swap/Copy Only Endian Order Testing Classes ] // These classes represent a brute-force approach to Endian order handling // that are used as a base-line for testing other optimized approaches /// /// Represents a big-endian byte order interoperability class. /// private class BigEndianOrderTester : EndianOrderTester { #region [ Constructors ] /// /// Constructs a new instance of the class. /// public BigEndianOrderTester() : base(Endianness.BigEndian) { } #endregion #region [ Static ] private static BigEndianOrderTester s_endianOrder; /// /// Returns the default instance of the class. /// public static BigEndianOrderTester Default { get { if ((object)s_endianOrder == null) s_endianOrder = new BigEndianOrderTester(); return s_endianOrder; } } #endregion } /// /// Represents a little-endian byte order interoperability class. /// private class LittleEndianOrderTester : EndianOrderTester { #region [ Constructors ] /// /// Constructs a new instance of the class. /// public LittleEndianOrderTester() : base(Endianness.LittleEndian) { } #endregion #region [ Static ] private static LittleEndianOrderTester s_endianOrder; /// /// Returns the default instance of the class. /// public static LittleEndianOrderTester Default { get { if ((object)s_endianOrder == null) s_endianOrder = new LittleEndianOrderTester(); return s_endianOrder; } } #endregion } /// /// Represents a native-endian byte order interoperability class. /// private class NativeEndianOrderTester : EndianOrderTester { #region [ Constructors ] /// /// Constructs a new instance of the class. /// public NativeEndianOrderTester() : base(BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian) { } #endregion #region [ Static ] private static NativeEndianOrderTester s_endianOrder; /// /// Returns the default instance of the class. /// public static NativeEndianOrderTester Default { get { if ((object)s_endianOrder == null) s_endianOrder = new NativeEndianOrderTester(); return s_endianOrder; } } #endregion } /// /// Represents an endian byte order interoperability class. /// /// /// Intel systems use little-endian byte order, other systems, such as Unix, use big-endian byte ordering. /// Little-endian ordering means bits are ordered such that the bit whose in-memory representation is right-most is the most-significant-bit in a byte. /// Big-endian ordering means bits are ordered such that the bit whose in-memory representation is left-most is the most-significant-bit in a byte. /// private class EndianOrderTester { #region [ Members ] // Delegates private delegate void CopyBufferFunction(byte[] sourceBuffer, int sourceIndex, byte[] destinationBuffer, int destinationIndex, int length); private delegate byte[] CoerceByteOrderFunction(byte[] buffer); // Fields private readonly Endianness m_targetEndianness; private readonly CopyBufferFunction m_copyBuffer; private readonly CoerceByteOrderFunction m_coerceByteOrder; #endregion #region [ Constructors ] /// /// Constructs a new instance of the class. /// /// Endianness parameter. protected EndianOrderTester(Endianness targetEndianness) { m_targetEndianness = targetEndianness; // We perform this logic only once for speed in conversions - we can do this because neither // the target nor the OS endian order will change during the lifecycle of this class... if (targetEndianness == Endianness.BigEndian) { #if ForceBigEndianTesting if (!BitConverter.IsLittleEndian) // <- Hard coded test for alternate architecture #else if (BitConverter.IsLittleEndian) #endif { // If OS is little endian and we want big endian, we swap the bytes m_copyBuffer = SwapCopy; m_coerceByteOrder = ReverseBuffer; } else { // If OS is big endian and we want big endian, we just copy the bytes m_copyBuffer = BlockCopy; m_coerceByteOrder = PassThroughBuffer; } } else { #if ForceBigEndianTesting if (!BitConverter.IsLittleEndian) // <- Hard coded test for alternate architecture #else if (BitConverter.IsLittleEndian) #endif { // If OS is little endian and we want little endian, we just copy the bytes m_copyBuffer = BlockCopy; m_coerceByteOrder = PassThroughBuffer; } else { // If OS is big endian and we want little endian, we swap the bytes m_copyBuffer = SwapCopy; m_coerceByteOrder = ReverseBuffer; } } } #endregion #region [ Properties ] /// /// Returns the target endian-order of this representation. /// public Endianness TargetEndianness { get { return m_targetEndianness; } } #endregion #region [ Methods ] private void BlockCopy(byte[] sourceBuffer, int sourceIndex, byte[] destinationBuffer, int destinationIndex, int length) { Buffer.BlockCopy(sourceBuffer, sourceIndex, destinationBuffer, destinationIndex, length); } // This function behaves just like Array.Copy but takes a little-endian source array and copies it in big-endian order, // or if the source array is big-endian it will copy it in little-endian order private void SwapCopy(byte[] sourceBuffer, int sourceIndex, byte[] destinationBuffer, int destinationIndex, int length) { int offset = destinationIndex + length - 1; for (int x = sourceIndex; x <= sourceIndex + length - 1; x++) { destinationBuffer[offset - (x - sourceIndex)] = sourceBuffer[x]; } } private byte[] PassThroughBuffer(byte[] buffer) { return buffer; } private byte[] ReverseBuffer(byte[] buffer) { Array.Reverse(buffer); return buffer; } /// /// Returns a value converted from one byte at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// true if the byte at startIndex in value is nonzero; otherwise, false. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. [SuppressMessage("Microsoft.Performance", "CA1822")] public bool ToBoolean(byte[] value, int startIndex) { return BitConverter.ToBoolean(value, startIndex); } /// /// Returns a Unicode character converted from two bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A character formed by two bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public char ToChar(byte[] value, int startIndex) { byte[] buffer = new byte[2]; m_copyBuffer(value, startIndex, buffer, 0, 2); return BitConverter.ToChar(buffer, 0); } /// /// Returns a double-precision floating point number converted from eight bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A double-precision floating point number formed by eight bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public double ToDouble(byte[] value, int startIndex) { byte[] buffer = new byte[8]; m_copyBuffer(value, startIndex, buffer, 0, 8); return BitConverter.ToDouble(buffer, 0); } /// /// Returns a 16-bit signed integer converted from two bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 16-bit signed integer formed by two bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public short ToInt16(byte[] value, int startIndex) { byte[] buffer = new byte[2]; m_copyBuffer(value, startIndex, buffer, 0, 2); return BitConverter.ToInt16(buffer, 0); } /// /// Returns a 24-bit signed integer converted from three bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 24-bit signed integer formed by three bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public Int24 ToInt24(byte[] value, int startIndex) { byte[] buffer = new byte[3]; m_copyBuffer(value, startIndex, buffer, 0, 3); return Int24.GetValue(buffer, 0); } /// /// Returns a 32-bit signed integer converted from four bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 32-bit signed integer formed by four bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public int ToInt32(byte[] value, int startIndex) { byte[] buffer = new byte[4]; m_copyBuffer(value, startIndex, buffer, 0, 4); return BitConverter.ToInt32(buffer, 0); } /// /// Returns a 64-bit signed integer converted from eight bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 64-bit signed integer formed by eight bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public long ToInt64(byte[] value, int startIndex) { byte[] buffer = new byte[8]; m_copyBuffer(value, startIndex, buffer, 0, 8); return BitConverter.ToInt64(buffer, 0); } /// /// Returns a single-precision floating point number converted from four bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A single-precision floating point number formed by four bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public float ToSingle(byte[] value, int startIndex) { byte[] buffer = new byte[4]; m_copyBuffer(value, startIndex, buffer, 0, 4); return BitConverter.ToSingle(buffer, 0); } /// /// Returns a 16-bit unsigned integer converted from two bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public ushort ToUInt16(byte[] value, int startIndex) { byte[] buffer = new byte[2]; m_copyBuffer(value, startIndex, buffer, 0, 2); return BitConverter.ToUInt16(buffer, 0); } /// /// Returns a 24-bit unsigned integer converted from three bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 24-bit unsigned integer formed by three bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public UInt24 ToUInt24(byte[] value, int startIndex) { byte[] buffer = new byte[3]; m_copyBuffer(value, startIndex, buffer, 0, 3); return UInt24.GetValue(buffer, 0); } /// /// Returns a 32-bit unsigned integer converted from four bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public uint ToUInt32(byte[] value, int startIndex) { byte[] buffer = new byte[4]; m_copyBuffer(value, startIndex, buffer, 0, 4); return BitConverter.ToUInt32(buffer, 0); } /// /// Returns a 64-bit unsigned integer converted from eight bytes, accounting for target endian-order, at a specified position in a byte array. /// /// An array of bytes (i.e., buffer containing binary image of value). /// The starting position within value. /// A 64-bit unsigned integer formed by eight bytes beginning at startIndex. /// value is null. /// startIndex is less than zero or greater than the length of value minus 1. public ulong ToUInt64(byte[] value, int startIndex) { byte[] buffer = new byte[8]; m_copyBuffer(value, startIndex, buffer, 0, 8); return BitConverter.ToUInt64(buffer, 0); } /// /// Returns the specified value as an array of bytes in the target endian-order. /// /// The value to convert. /// An array of bytes with length 1. /// Native value type to get bytes for. /// type is not primitive. /// Cannot get bytes for type. public byte[] GetBytes(T value) where T : struct, IConvertible { if (!typeof(T).IsPrimitive) throw new ArgumentException("Value type is not primitive", "value"); IConvertible nativeValue = (IConvertible)value; switch (nativeValue.GetTypeCode()) { case TypeCode.Char: return GetBytes(nativeValue.ToChar(null)); case TypeCode.Boolean: return GetBytes(nativeValue.ToBoolean(null)); case TypeCode.Int16: return GetBytes(nativeValue.ToInt16(null)); case TypeCode.UInt16: return GetBytes(nativeValue.ToUInt16(null)); case TypeCode.Int32: return GetBytes(nativeValue.ToInt32(null)); case TypeCode.UInt32: return GetBytes(nativeValue.ToUInt32(null)); case TypeCode.Int64: return GetBytes(nativeValue.ToInt64(null)); case TypeCode.UInt64: return GetBytes(nativeValue.ToUInt64(null)); case TypeCode.Single: return GetBytes(nativeValue.ToSingle(null)); case TypeCode.Double: return GetBytes(nativeValue.ToDouble(null)); default: throw new InvalidOperationException("Cannot get bytes for value type " + nativeValue.GetTypeCode()); } } /// /// Returns the specified value as an array of bytes in the target endian-order. /// /// The value to convert. /// An array of bytes with length 1. [SuppressMessage("Microsoft.Performance", "CA1822")] public byte[] GetBytes(bool value) { // No need to reverse buffer for one byte: return BitConverter.GetBytes(value); } /// /// Returns the specified Unicode character value as an array of bytes in the target endian-order. /// /// The Unicode character value to convert. /// An array of bytes with length 2. public byte[] GetBytes(char value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified double-precision floating point value as an array of bytes in the target endian-order. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(double value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified 16-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 2. public byte[] GetBytes(short value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified 24-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 3. public byte[] GetBytes(Int24 value) { return m_coerceByteOrder(Int24.GetBytes(value)); } /// /// Returns the specified 32-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(int value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified 64-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(long value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified single-precision floating point value as an array of bytes in the target endian-order. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(float value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified 16-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 2. public byte[] GetBytes(ushort value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified 24-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 3. public byte[] GetBytes(UInt24 value) { return m_coerceByteOrder(UInt24.GetBytes(value)); } /// /// Returns the specified 32-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(uint value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Returns the specified 64-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(ulong value) { return m_coerceByteOrder(BitConverter.GetBytes(value)); } /// /// Copies the specified primitive type value as an array of bytes in the target endian-order to the destination array. /// /// The value to convert and copy. /// The destination buffer. /// The byte offset into . /// Native value type to get bytes for. /// type is not primitive. /// Cannot get bytes for type. /// Length of bytes copied into array based on size of . public int CopyBytes(T value, byte[] destinationArray, int destinationIndex) where T : struct, IConvertible { if (!typeof(T).IsPrimitive) throw new ArgumentException("Value type is not primitive", "value"); IConvertible nativeValue = (IConvertible)value; switch (nativeValue.GetTypeCode()) { case TypeCode.Char: return CopyBytes(nativeValue.ToChar(null), destinationArray, destinationIndex); case TypeCode.Boolean: return CopyBytes(nativeValue.ToBoolean(null), destinationArray, destinationIndex); case TypeCode.Int16: return CopyBytes(nativeValue.ToInt16(null), destinationArray, destinationIndex); case TypeCode.UInt16: return CopyBytes(nativeValue.ToUInt16(null), destinationArray, destinationIndex); case TypeCode.Int32: return CopyBytes(nativeValue.ToInt32(null), destinationArray, destinationIndex); case TypeCode.UInt32: return CopyBytes(nativeValue.ToUInt32(null), destinationArray, destinationIndex); case TypeCode.Int64: return CopyBytes(nativeValue.ToInt64(null), destinationArray, destinationIndex); case TypeCode.UInt64: return CopyBytes(nativeValue.ToUInt64(null), destinationArray, destinationIndex); case TypeCode.Single: return CopyBytes(nativeValue.ToSingle(null), destinationArray, destinationIndex); case TypeCode.Double: return CopyBytes(nativeValue.ToDouble(null), destinationArray, destinationIndex); default: throw new InvalidOperationException("Cannot copy bytes for value type " + nativeValue.GetTypeCode()); } } /// /// Copies the specified value as an array of 1 byte in the target endian-order to the destination array. /// /// The value to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(bool value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 1); return 1; } /// /// Copies the specified Unicode character value as an array of 2 bytes in the target endian-order to the destination array. /// /// The Unicode character value to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(char value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 2); return 2; } /// /// Copies the specified double-precision floating point value as an array of 8 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(double value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 8); return 8; } /// /// Copies the specified 16-bit signed integer value as an array of 2 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(short value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 2); return 2; } /// /// Copies the specified 24-bit signed integer value as an array of 3 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(Int24 value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(Int24.GetBytes(value), 0, destinationArray, destinationIndex, 3); return 3; } /// /// Copies the specified 32-bit signed integer value as an array of 4 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(int value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 4); return 4; } /// /// Copies the specified 64-bit signed integer value as an array of 8 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(long value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 8); return 8; } /// /// Copies the specified single-precision floating point value as an array of 4 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(float value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 4); return 4; } /// /// Copies the specified 16-bit unsigned integer value as an array of 2 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(ushort value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 2); return 2; } /// /// Copies the specified 24-bit unsigned integer value as an array of 3 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(UInt24 value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(UInt24.GetBytes(value), 0, destinationArray, destinationIndex, 3); return 3; } /// /// Copies the specified 32-bit unsigned integer value as an array of 4 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(uint value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 4); return 4; } /// /// Copies the specified 64-bit unsigned integer value as an array of 8 bytes in the target endian-order to the destination array. /// /// The number to convert and copy. /// The destination buffer. /// The byte offset into . /// Length of bytes copied into array based on size of . public int CopyBytes(ulong value, byte[] destinationArray, int destinationIndex) { m_copyBuffer(BitConverter.GetBytes(value), 0, destinationArray, destinationIndex, 8); return 8; } #endregion } #endregion #region [ Static Endian Testing Classes ] public class StaticLittleEndianTester { public static StaticLittleEndianTester Default = new StaticLittleEndianTester(); public Endianness TargetEndianness = Endianness.LittleEndian; public byte[] GetBytes(T value) where T : struct, IConvertible { return LittleEndian.GetBytes(value); } public byte[] GetBytes(Int24 value) { return LittleEndian.GetBytes(value); } public byte[] GetBytes(UInt24 value) { return LittleEndian.GetBytes(value); } public int CopyBytes(T value, byte[] destinationArray, int destinationIndex) where T : struct, IConvertible { return LittleEndian.CopyBytes(value, destinationArray, destinationIndex); } public int CopyBytes(Int24 value, byte[] destinationArray, int destinationIndex) { return LittleEndian.CopyBytes(value, destinationArray, destinationIndex); } public int CopyBytes(UInt24 value, byte[] destinationArray, int destinationIndex) { return LittleEndian.CopyBytes(value, destinationArray, destinationIndex); } public bool ToBoolean(byte[] value, int startIndex) { return LittleEndian.ToBoolean(value, startIndex); } public char ToChar(byte[] value, int startIndex) { return LittleEndian.ToChar(value, startIndex); } public double ToDouble(byte[] value, int startIndex) { return LittleEndian.ToDouble(value, startIndex); } public short ToInt16(byte[] value, int startIndex) { return LittleEndian.ToInt16(value, startIndex); } public Int24 ToInt24(byte[] value, int startIndex) { return LittleEndian.ToInt24(value, startIndex); } public int ToInt32(byte[] value, int startIndex) { return LittleEndian.ToInt32(value, startIndex); } public long ToInt64(byte[] value, int startIndex) { return LittleEndian.ToInt64(value, startIndex); } public float ToSingle(byte[] value, int startIndex) { return LittleEndian.ToSingle(value, startIndex); } public ushort ToUInt16(byte[] value, int startIndex) { return LittleEndian.ToUInt16(value, startIndex); } public UInt24 ToUInt24(byte[] value, int startIndex) { return LittleEndian.ToUInt24(value, startIndex); } public uint ToUInt32(byte[] value, int startIndex) { return LittleEndian.ToUInt32(value, startIndex); } public ulong ToUInt64(byte[] value, int startIndex) { return LittleEndian.ToUInt64(value, startIndex); } } public class StaticBigEndianTester { public static StaticBigEndianTester Default = new StaticBigEndianTester(); public Endianness TargetEndianness = Endianness.BigEndian; public byte[] GetBytes(T value) where T : struct, IConvertible { return BigEndian.GetBytes(value); } public byte[] GetBytes(Int24 value) { return BigEndian.GetBytes(value); } public byte[] GetBytes(UInt24 value) { return BigEndian.GetBytes(value); } public int CopyBytes(T value, byte[] destinationArray, int destinationIndex) where T : struct, IConvertible { return BigEndian.CopyBytes(value, destinationArray, destinationIndex); } public int CopyBytes(Int24 value, byte[] destinationArray, int destinationIndex) { return BigEndian.CopyBytes(value, destinationArray, destinationIndex); } public int CopyBytes(UInt24 value, byte[] destinationArray, int destinationIndex) { return BigEndian.CopyBytes(value, destinationArray, destinationIndex); } public bool ToBoolean(byte[] value, int startIndex) { return BigEndian.ToBoolean(value, startIndex); } public char ToChar(byte[] value, int startIndex) { return BigEndian.ToChar(value, startIndex); } public double ToDouble(byte[] value, int startIndex) { return BigEndian.ToDouble(value, startIndex); } public short ToInt16(byte[] value, int startIndex) { return BigEndian.ToInt16(value, startIndex); } public Int24 ToInt24(byte[] value, int startIndex) { return BigEndian.ToInt24(value, startIndex); } public int ToInt32(byte[] value, int startIndex) { return BigEndian.ToInt32(value, startIndex); } public long ToInt64(byte[] value, int startIndex) { return BigEndian.ToInt64(value, startIndex); } public float ToSingle(byte[] value, int startIndex) { return BigEndian.ToSingle(value, startIndex); } public ushort ToUInt16(byte[] value, int startIndex) { return BigEndian.ToUInt16(value, startIndex); } public UInt24 ToUInt24(byte[] value, int startIndex) { return BigEndian.ToUInt24(value, startIndex); } public uint ToUInt32(byte[] value, int startIndex) { return BigEndian.ToUInt32(value, startIndex); } public ulong ToUInt64(byte[] value, int startIndex) { return BigEndian.ToUInt64(value, startIndex); } } #endregion [TestMethod] public void TestDataTypeEncodings() { bool[] boolValues = { true, false }; char[] charValues = { char.MinValue, (char)1, (char)(char.MaxValue / 2), char.MaxValue }; short[] shortValues = { short.MinValue, short.MinValue / (short)2, (short)-1, (short)0, (short)1, short.MaxValue / (short)2, short.MaxValue }; ushort[] ushortValues = { ushort.MinValue, (ushort)1, ushort.MaxValue / (ushort)2, ushort.MaxValue }; Int24[] int24Values = { Int24.MinValue, Int24.MinValue / (Int24)2, (Int24)(-1), (Int24)0, (Int24)1, Int24.MaxValue / (Int24)2, Int24.MaxValue }; UInt24[] uint24Values = { UInt24.MinValue, (UInt24)1, UInt24.MaxValue / (UInt24)2, UInt24.MaxValue }; int[] intValues = { int.MinValue, int.MinValue / 2, -1, 0, 1, int.MaxValue / 2, int.MaxValue }; uint[] uintValues = { uint.MinValue, 1U, uint.MaxValue / 2U, uint.MaxValue }; long[] longValues = { long.MinValue, long.MinValue / 2L, -1L, 0L, 1L, long.MaxValue / 2L, long.MaxValue }; ulong[] ulongValues = { ulong.MinValue, 1UL, ulong.MaxValue / 2UL, ulong.MaxValue }; float[] floatValues = { float.MinValue, float.MinValue / 2.0F, -1.0F, 0.0F, 1.0F, float.MaxValue / 2.0F, float.MaxValue, float.NaN, float.NegativeInfinity, float.PositiveInfinity }; double[] doubleValues = { double.MinValue, double.MinValue / 2.0D, -1.0D, 0.0D, 1.0D, double.MaxValue / 2.0D, double.MaxValue, double.NaN, double.NegativeInfinity, double.PositiveInfinity }; #if ForceBigEndianTesting // Swap all values to big-endian order - note that since endian order tester class is now operating in reverse, we use little-endian for this byte[] valueBytes; IConvertible convertibleValue; EndianOrderTester swapper = LittleEndianOrderTester.Default; for (int i = 0; i < boolValues.Length; i++) { valueBytes = BitConverter.GetBytes(boolValues[i]); boolValues[i] = swapper.ToBoolean(valueBytes, 0); } for (int i = 0; i < charValues.Length; i++) { valueBytes = BitConverter.GetBytes(charValues[i]); charValues[i] = swapper.ToChar(valueBytes, 0); } for (int i = 0; i < shortValues.Length; i++) { valueBytes = BitConverter.GetBytes(shortValues[i]); shortValues[i] = swapper.ToInt16(valueBytes, 0); } for (int i = 0; i < ushortValues.Length; i++) { valueBytes = BitConverter.GetBytes(ushortValues[i]); ushortValues[i] = swapper.ToUInt16(valueBytes, 0); } for (int i = 0; i < int24Values.Length; i++) { valueBytes = Int24.GetBytes(int24Values[i]); int24Values[i] = swapper.ToInt24(valueBytes, 0); } for (int i = 0; i < uint24Values.Length; i++) { valueBytes = UInt24.GetBytes(uint24Values[i]); uint24Values[i] = swapper.ToUInt24(valueBytes, 0); } for (int i = 0; i < intValues.Length; i++) { valueBytes = BitConverter.GetBytes(intValues[i]); intValues[i] = swapper.ToInt32(valueBytes, 0); } for (int i = 0; i < uintValues.Length; i++) { valueBytes = BitConverter.GetBytes(uintValues[i]); uintValues[i] = swapper.ToUInt32(valueBytes, 0); } for (int i = 0; i < longValues.Length; i++) { valueBytes = BitConverter.GetBytes(longValues[i]); longValues[i] = swapper.ToInt64(valueBytes, 0); } for (int i = 0; i < ulongValues.Length; i++) { valueBytes = BitConverter.GetBytes(ulongValues[i]); ulongValues[i] = swapper.ToUInt64(valueBytes, 0); } for (int i = 0; i < floatValues.Length; i++) { valueBytes = BitConverter.GetBytes(floatValues[i]); floatValues[i] = swapper.ToSingle(valueBytes, 0); } for (int i = 0; i < doubleValues.Length; i++) { valueBytes = BitConverter.GetBytes(doubleValues[i]); doubleValues[i] = swapper.ToDouble(valueBytes, 0); } #else TestValues(boolValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(bool)); TestValues(charValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(char)); TestValues(shortValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(short)); TestValues(ushortValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(ushort)); TestValues(int24Values, NativeEndianOrder.Default, NativeEndianOrderTester.Default, 3); TestValues(uint24Values, NativeEndianOrder.Default, NativeEndianOrderTester.Default, 3); TestValues(intValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(int)); TestValues(uintValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(uint)); TestValues(longValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(long)); TestValues(ulongValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(ulong)); TestValues(floatValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(float)); TestValues(doubleValues, NativeEndianOrder.Default, NativeEndianOrderTester.Default, sizeof(double)); #endif TestValues(boolValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(bool)); TestValues(charValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(char)); TestValues(shortValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(short)); TestValues(ushortValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(ushort)); TestValues(int24Values, LittleEndianOrder.Default, LittleEndianOrderTester.Default, 3); TestValues(uint24Values, LittleEndianOrder.Default, LittleEndianOrderTester.Default, 3); TestValues(intValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(int)); TestValues(uintValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(uint)); TestValues(longValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(long)); TestValues(ulongValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(ulong)); TestValues(floatValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(float)); TestValues(doubleValues, LittleEndianOrder.Default, LittleEndianOrderTester.Default, sizeof(double)); TestValues(boolValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(bool)); TestValues(charValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(char)); TestValues(shortValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(short)); TestValues(ushortValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(ushort)); TestValues(int24Values, BigEndianOrder.Default, BigEndianOrderTester.Default, 3); TestValues(uint24Values, BigEndianOrder.Default, BigEndianOrderTester.Default, 3); TestValues(intValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(int)); TestValues(uintValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(uint)); TestValues(longValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(long)); TestValues(ulongValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(ulong)); TestValues(floatValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(float)); TestValues(doubleValues, BigEndianOrder.Default, BigEndianOrderTester.Default, sizeof(double)); TestValues(boolValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(bool)); TestValues(charValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(char)); TestValues(shortValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(short)); TestValues(ushortValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(ushort)); TestValues(int24Values, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, 3); TestValues(uint24Values, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, 3); TestValues(intValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(int)); TestValues(uintValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(uint)); TestValues(longValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(long)); TestValues(ulongValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(ulong)); TestValues(floatValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(float)); TestValues(doubleValues, StaticLittleEndianTester.Default, LittleEndianOrderTester.Default, sizeof(double)); TestValues(boolValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(bool)); TestValues(charValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(char)); TestValues(shortValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(short)); TestValues(ushortValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(ushort)); TestValues(int24Values, StaticBigEndianTester.Default, BigEndianOrderTester.Default, 3); TestValues(uint24Values, StaticBigEndianTester.Default, BigEndianOrderTester.Default, 3); TestValues(intValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(int)); TestValues(uintValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(uint)); TestValues(longValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(long)); TestValues(ulongValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(ulong)); TestValues(floatValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(float)); TestValues(doubleValues, StaticBigEndianTester.Default, BigEndianOrderTester.Default, sizeof(double)); } private void TestValues(IEnumerable values, dynamic eOrder, EndianOrderTester eOrderTester, int typeSize) where T : struct, IConvertible { byte[] valueBytes, valueTestBytes, copiedBytes = new byte[typeSize], copiedTestBytes = new byte[typeSize]; IConvertible convertibleValue; int length, testLength; Assert.IsTrue(eOrder.TargetEndianness == eOrderTester.TargetEndianness); foreach (T value in values) { convertibleValue = value as IConvertible; Assert.IsNotNull(convertibleValue); // Test "GetBytes" implementation if (typeof(T) == typeof(Int24)) { Int24 int24Value = (Int24)convertibleValue.ToInt32(null); valueBytes = eOrder.GetBytes(int24Value); valueTestBytes = eOrderTester.GetBytes(int24Value); Assert.IsTrue(valueBytes.CompareTo(valueTestBytes) == 0); } else if (typeof(T) == typeof(UInt24)) { UInt24 uint24Value = (UInt24)convertibleValue.ToUInt32(null); valueBytes = eOrder.GetBytes(uint24Value); valueTestBytes = eOrderTester.GetBytes(uint24Value); Assert.IsTrue(valueBytes.CompareTo(valueTestBytes) == 0); } else { valueBytes = eOrder.GetBytes(value); valueTestBytes = eOrderTester.GetBytes(value); Assert.IsTrue(valueBytes.CompareTo(valueTestBytes) == 0); } // Test "CopyBytes" implementation if (typeof(T) == typeof(Int24)) { Int24 int24Value = (Int24)convertibleValue.ToInt32(null); length = eOrder.CopyBytes(int24Value, copiedBytes, 0); testLength = eOrderTester.CopyBytes(int24Value, copiedTestBytes, 0); Assert.IsTrue(copiedBytes.CompareTo(copiedTestBytes) == 0); Assert.IsTrue(length == testLength && length == typeSize); } else if (typeof(T) == typeof(UInt24)) { UInt24 uint24Value = (UInt24)convertibleValue.ToUInt32(null); length = eOrder.CopyBytes(uint24Value, copiedBytes, 0); testLength = eOrderTester.CopyBytes(uint24Value, copiedTestBytes, 0); Assert.IsTrue(copiedBytes.CompareTo(copiedTestBytes) == 0); Assert.IsTrue(length == testLength && length == typeSize); } else { length = eOrder.CopyBytes(value, copiedBytes, 0); testLength = eOrderTester.CopyBytes(value, copiedTestBytes, 0); Assert.IsTrue(copiedBytes.CompareTo(copiedTestBytes) == 0); Assert.IsTrue(length == testLength && length == typeSize); } // Validate that CopiedBytes for value matches GetBytes Assert.IsTrue(copiedBytes.CompareTo(valueBytes) == 0); // Test "To" implementation switch (typeSize) { case 1: Assert.IsTrue(convertibleValue.ToBoolean(null) == eOrder.ToBoolean(copiedBytes, 0)); Assert.IsTrue(eOrder.ToBoolean(copiedBytes, 0) == eOrderTester.ToBoolean(copiedBytes, 0)); break; case 2: if (typeof(T) == typeof(char)) { Assert.IsTrue(convertibleValue.ToChar(null) == eOrder.ToChar(copiedBytes, 0)); Assert.IsTrue(eOrder.ToChar(copiedBytes, 0) == eOrderTester.ToChar(copiedBytes, 0)); } else if (typeof(T) == typeof(short)) { Assert.IsTrue(convertibleValue.ToInt16(null) == eOrder.ToInt16(copiedBytes, 0)); Assert.IsTrue(eOrder.ToInt16(copiedBytes, 0) == eOrderTester.ToInt16(copiedBytes, 0)); } else { Assert.IsTrue(convertibleValue.ToUInt16(null) == eOrder.ToUInt16(copiedBytes, 0)); Assert.IsTrue(eOrder.ToUInt16(copiedBytes, 0) == eOrderTester.ToUInt16(copiedBytes, 0)); } break; case 3: if (typeof(T) == typeof(Int24)) { Assert.IsTrue(convertibleValue.ToInt32(null) == eOrder.ToInt24(copiedBytes, 0)); Assert.IsTrue(eOrder.ToInt24(copiedBytes, 0) == eOrderTester.ToInt24(copiedBytes, 0)); } else { Assert.IsTrue(convertibleValue.ToUInt32(null) == eOrder.ToUInt24(copiedBytes, 0)); Assert.IsTrue(eOrder.ToUInt24(copiedBytes, 0) == eOrderTester.ToUInt24(copiedBytes, 0)); } break; case 4: if (typeof(T) == typeof(int)) { Assert.IsTrue(convertibleValue.ToInt32(null) == eOrder.ToInt32(copiedBytes, 0)); Assert.IsTrue(eOrder.ToInt32(copiedBytes, 0) == eOrderTester.ToInt32(copiedBytes, 0)); } else if (typeof(T) == typeof(uint)) { Assert.IsTrue(convertibleValue.ToUInt32(null) == eOrder.ToUInt32(copiedBytes, 0)); Assert.IsTrue(eOrder.ToUInt32(copiedBytes, 0) == eOrderTester.ToUInt32(copiedBytes, 0)); } else { float singleValue = convertibleValue.ToSingle(null); if (float.IsNaN(singleValue)) { Assert.IsTrue(float.IsNaN(eOrder.ToSingle(copiedBytes, 0))); Assert.IsTrue(float.IsNaN(eOrderTester.ToSingle(copiedBytes, 0))); } else if (float.IsNegativeInfinity(singleValue)) { Assert.IsTrue(float.IsNegativeInfinity(eOrder.ToSingle(copiedBytes, 0))); Assert.IsTrue(float.IsNegativeInfinity(eOrderTester.ToSingle(copiedBytes, 0))); } else if (float.IsPositiveInfinity(singleValue)) { Assert.IsTrue(float.IsPositiveInfinity(eOrder.ToSingle(copiedBytes, 0))); Assert.IsTrue(float.IsPositiveInfinity(eOrderTester.ToSingle(copiedBytes, 0))); } else { Assert.IsTrue(singleValue == eOrder.ToSingle(copiedBytes, 0)); Assert.IsTrue(eOrder.ToSingle(copiedBytes, 0) == eOrderTester.ToSingle(copiedBytes, 0)); } } break; case 8: if (typeof(T) == typeof(long)) { Assert.IsTrue(convertibleValue.ToInt64(null) == eOrder.ToInt64(copiedBytes, 0)); Assert.IsTrue(eOrder.ToInt64(copiedBytes, 0) == eOrderTester.ToInt64(copiedBytes, 0)); } else if (typeof(T) == typeof(ulong)) { Assert.IsTrue(convertibleValue.ToUInt64(null) == eOrder.ToUInt64(copiedBytes, 0)); Assert.IsTrue(eOrder.ToUInt64(copiedBytes, 0) == eOrderTester.ToUInt64(copiedBytes, 0)); } else { double doubleValue = convertibleValue.ToDouble(null); if (double.IsNaN(doubleValue)) { Assert.IsTrue(double.IsNaN(eOrder.ToDouble(copiedBytes, 0))); Assert.IsTrue(double.IsNaN(eOrderTester.ToDouble(copiedBytes, 0))); } else if (double.IsNegativeInfinity(doubleValue)) { Assert.IsTrue(double.IsNegativeInfinity(eOrder.ToDouble(copiedBytes, 0))); Assert.IsTrue(double.IsNegativeInfinity(eOrderTester.ToDouble(copiedBytes, 0))); } else if (double.IsPositiveInfinity(doubleValue)) { Assert.IsTrue(double.IsPositiveInfinity(eOrder.ToDouble(copiedBytes, 0))); Assert.IsTrue(double.IsPositiveInfinity(eOrderTester.ToDouble(copiedBytes, 0))); } else { Assert.IsTrue(doubleValue == eOrder.ToDouble(copiedBytes, 0)); Assert.IsTrue(eOrder.ToDouble(copiedBytes, 0) == eOrderTester.ToDouble(copiedBytes, 0)); } } break; } } } } }