namespace Unosquare.Swan { using System; using System.Reflection; using System.Runtime.InteropServices; using Attributes; /// /// Provides various extension methods for value types and structs. /// public static class ValueTypeExtensions { /// /// Clamps the specified value between the minimum and the maximum. /// /// The type of value to clamp. /// The value. /// The minimum. /// The maximum. /// A value that indicates the relative order of the objects being compared. public static T Clamp(this T value, T min, T max) where T : struct, IComparable { if (value.CompareTo(min) < 0) return min; return value.CompareTo(max) > 0 ? max : value; } /// /// Clamps the specified value between the minimum and the maximum. /// /// The value. /// The minimum. /// The maximum. /// A value that indicates the relative order of the objects being compared. public static int Clamp(this int value, int min, int max) => value < min ? min : (value > max ? max : value); /// /// Determines whether the specified value is between a minimum and a maximum value. /// /// The type of value to check. /// The value. /// The minimum. /// The maximum. /// /// true if the specified minimum is between; otherwise, false. /// public static bool IsBetween(this T value, T min, T max) where T : struct, IComparable { return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0; } /// /// Converts an array of bytes into the given struct type. /// /// The type of structure to convert. /// The data. /// a struct type derived from convert an array of bytes ref=ToStruct". public static T ToStruct(this byte[] data) where T : struct { return ToStruct(data, 0, data.Length); } /// /// Converts an array of bytes into the given struct type. /// /// The type of structure to convert. /// The data. /// The offset. /// The length. /// /// A managed object containing the data pointed to by the ptr parameter. /// /// data. public static T ToStruct(this byte[] data, int offset, int length) where T : struct { if (data == null) throw new ArgumentNullException(nameof(data)); var buffer = new byte[length]; Array.Copy(data, offset, buffer, 0, buffer.Length); var handle = GCHandle.Alloc(GetStructBytes(buffer), GCHandleType.Pinned); try { return Marshal.PtrToStructure(handle.AddrOfPinnedObject()); } finally { handle.Free(); } } /// /// Converts a struct to an array of bytes. /// /// The type of structure to convert. /// The object. /// A byte array containing the results of encoding the specified set of characters. public static byte[] ToBytes(this T obj) where T : struct { var data = new byte[Marshal.SizeOf(obj)]; var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false); return GetStructBytes(data); } finally { handle.Free(); } } /// /// Swaps the endianness of an unsigned long to an unsigned integer. /// /// The bytes contained in a long. /// /// A 32-bit unsigned integer equivalent to the ulong /// contained in longBytes. /// public static uint SwapEndianness(this ulong longBytes) => (uint) (((longBytes & 0x000000ff) << 24) + ((longBytes & 0x0000ff00) << 8) + ((longBytes & 0x00ff0000) >> 8) + ((longBytes & 0xff000000) >> 24)); private static byte[] GetStructBytes(byte[] data) { if (data == null) throw new ArgumentNullException(nameof(data)); #if !NETSTANDARD1_3 var fields = typeof(T).GetTypeInfo() .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); #else var fields = typeof(T).GetTypeInfo().DeclaredFields; #endif var endian = Runtime.AttributeCache.RetrieveOne(); foreach (var field in fields) { if (endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false)) continue; var offset = Marshal.OffsetOf(field.Name).ToInt32(); var length = Marshal.SizeOf(field.FieldType); endian = endian ?? Runtime.AttributeCache.RetrieveOne(field); if (endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian || endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian)) { Array.Reverse(data, offset, length); } } return data; } } }