using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan {
///
/// 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 => value.CompareTo(min) < 0 ? min : 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 Int32 Clamp(this Int32 value, Int32 min, Int32 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 Boolean IsBetween(this T value, T min, T max)
where T : struct, IComparable => 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 => 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, Int32 offset, Int32 length)
where T : struct {
if(data == null) {
throw new ArgumentNullException(nameof(data));
}
Byte[] buffer = new Byte[length];
Array.Copy(data, offset, buffer, 0, buffer.Length);
GCHandle 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 {
Byte[] data = new Byte[Marshal.SizeOf(obj)];
GCHandle 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 UInt32 SwapEndianness(this UInt64 longBytes)
=> (UInt32)(((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
FieldInfo[] fields = typeof(T).GetTypeInfo()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
#else
var fields = typeof(T).GetTypeInfo().DeclaredFields;
#endif
StructEndiannessAttribute endian = Runtime.AttributeCache.RetrieveOne();
foreach(FieldInfo field in fields) {
if(endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false)) {
continue;
}
Int32 offset = Marshal.OffsetOf(field.Name).ToInt32();
Int32 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;
}
}
}