-
Notifications
You must be signed in to change notification settings - Fork 27
/
Extensions.ValueTypes.cs
175 lines (156 loc) · 6.75 KB
/
Extensions.ValueTypes.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
namespace Unosquare.Swan
{
using System;
using System.Reflection;
using System.Linq;
using System.Runtime.InteropServices;
using Attributes;
/// <summary>
/// Provides various extension methods for value types and structs.
/// </summary>
public static class ValueTypeExtensions
{
/// <summary>
/// Clamps the specified value between the minimum and the maximum.
/// </summary>
/// <typeparam name="T">The type of value to clamp.</typeparam>
/// <param name="value">The value.</param>
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
public static T Clamp<T>(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;
}
/// <summary>
/// Clamps the specified value between the minimum and the maximum.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
public static int Clamp(this int value, int min, int max)
=> value < min ? min : (value > max ? max : value);
/// <summary>
/// Determines whether the specified value is between a minimum and a maximum value.
/// </summary>
/// <typeparam name="T">The type of value to check.</typeparam>
/// <param name="value">The value.</param>
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param>
/// <returns>
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
/// </returns>
public static bool IsBetween<T>(this T value, T min, T max)
where T : struct, IComparable
{
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
}
/// <summary>
/// Converts an array of bytes into the given struct type.
/// </summary>
/// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="data">The data.</param>
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
public static T ToStruct<T>(this byte[] data)
where T : struct
{
return ToStruct<T>(data, 0, data.Length);
}
/// <summary>
/// Converts an array of bytes into the given struct type.
/// </summary>
/// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="data">The data.</param>
/// <param name="offset">The offset.</param>
/// <param name="length">The length.</param>
/// <returns>
/// A managed object containing the data pointed to by the ptr parameter.
/// </returns>
/// <exception cref="ArgumentNullException">data.</exception>
public static T ToStruct<T>(this byte[] data, in int offset, in 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<T>(buffer), GCHandleType.Pinned);
try
{
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
}
/// <summary>
/// Converts a struct to an array of bytes.
/// </summary>
/// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="obj">The object.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] ToBytes<T>(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<T>(data);
}
finally
{
handle.Free();
}
}
/// <summary>
/// Swaps the endianness of an unsigned long to an unsigned integer.
/// </summary>
/// <param name="longBytes">The bytes contained in a long.</param>
/// <returns>
/// A 32-bit unsigned integer equivalent to the ulong
/// contained in longBytes.
/// </returns>
public static uint SwapEndianness(this ulong longBytes)
{
return (uint)(((longBytes & 0x000000ff) << 24) +
((longBytes & 0x0000ff00) << 8) +
((longBytes & 0x00ff0000) >> 8) +
((longBytes & 0xff000000) >> 24));
}
private static byte[] GetStructBytes<T>(byte[] data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
#if !NETSTANDARD1_3 && !UWP
var fields = typeof(T).GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
#else
var fields = typeof(T).GetTypeInfo().DeclaredFields;
#endif
StructEndiannessAttribute endian = null;
if (typeof(T).IsDefined(typeof(StructEndiannessAttribute), false))
{
endian = typeof(T).GetCustomAttributes(typeof(StructEndiannessAttribute), false)[0] as StructEndiannessAttribute;
}
foreach (var field in fields)
{
if (endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false))
continue;
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
var length = Marshal.SizeOf(field.FieldType);
endian = endian ?? field.GetCustomAttributes(typeof(StructEndiannessAttribute), false).ToArray()[0] as StructEndiannessAttribute;
if (endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian))
{
Array.Reverse(data, offset, length);
}
}
return data;
}
}
}