Skip to content

Commit 60ba38c

Browse files
committed
move unsafe value conversions to their own experimental public class
1 parent e4d9055 commit 60ba38c

2 files changed

Lines changed: 79 additions & 37 deletions

File tree

sources/Input/Input/Implementations/EnumInfo.cs

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Reflection;
5-
using System.Runtime.CompilerServices;
65

76
namespace Silk.NET.Input;
87

@@ -143,6 +142,7 @@ static unsafe EnumInfo()
143142
MaxValue = AllValuesOrdered[^1];
144143
}
145144

145+
#pragma warning disable ST0006 ST0007 ST0008 ST0009
146146
/// <summary>
147147
/// Gets the ordered index of the unnamed enum value provided. This index is calculated by:
148148
/// (the number of named members in this enum type) + (the raw value of the number if unnamed)
@@ -154,7 +154,7 @@ static unsafe EnumInfo()
154154
public static int ValueIndexOf(T value)
155155
{
156156
// happy path - it's a named value we've already computed
157-
if(_numericallyDistinctIndices.TryGetValue(value, out var index))
157+
if (_numericallyDistinctIndices.TryGetValue(value, out var index))
158158
{
159159
return index;
160160
}
@@ -166,46 +166,17 @@ public static int ValueIndexOf(T value)
166166
return -1;
167167
}
168168

169-
var rawValue = Convert<T, int>(value);
169+
var rawValue = value.Convert<T, int>();
170170

171171
// todo - don't rely on joystickButton's unknown - find the MinValue
172-
if (rawValue <= 0 || rawValue >= Convert<ulong, int>(_allEnumValuesRaw[0]))
172+
if (rawValue <= 0 || rawValue >= _allEnumValuesRaw[0].Convert<ulong, int>())
173173
{
174174
return -1;
175175
}
176176

177-
return _allValuesOrdered.Length + rawValue;
177+
return _allValuesOrdered.Length + rawValue;
178178
}
179179

180-
/// <summary>
181-
/// Returns the numerical value of the enum value provided in a type-safe way
182-
/// </summary>
183-
/// <param name="value"></param>
184-
/// <typeparam name="TFrom"></typeparam>
185-
/// <typeparam name="TTo"></typeparam>
186-
/// <returns></returns>
187-
private static unsafe TTo Convert<TFrom, TTo>(TFrom value) where TTo : unmanaged where TFrom : unmanaged
188-
{
189-
if (sizeof(T) == sizeof(TTo))
190-
{
191-
return Unsafe.Read<TTo>(&value);
192-
}
193-
194-
var minSize = Math.Min(sizeof(TTo), sizeof(T));
195-
196-
var originalValuePtr = (byte*)&value;
197-
198-
var valuePtr = &originalValuePtr[Math.Abs(minSize - sizeof(T))]; // does this assume little-endianness?
199-
var numberPtr = stackalloc byte[sizeof(TTo)];
200-
201-
// ensure block is initialized (as it isnt guaranteed?) so any missing bytes of the output will stay 0
202-
// if type TNumber is a larger size than type T
203-
Unsafe.InitBlock(numberPtr, 0, (uint)sizeof(TTo));
204-
205-
var copyToPtr = &numberPtr[Math.Abs(minSize - sizeof(TTo))];
206-
Buffer.MemoryCopy(valuePtr, copyToPtr, sizeof(TTo), minSize);
207-
return *(TTo*)numberPtr;
208-
}
209180

210181
private static T[] OrderedValues<TNumber>(bool byNumericValue)
211182
where TNumber : unmanaged, IComparable<TNumber>
@@ -215,19 +186,21 @@ private static T[] OrderedValues<TNumber>(bool byNumericValue)
215186

216187
if (byNumericValue)
217188
{
218-
allValues = allValues.DistinctBy(Convert<T, TNumber>).ToArray();
189+
allValues = allValues.DistinctBy(UnsafeNumericValueExtensions.Convert<T, TNumber>).ToArray();
219190
}
220191

221192
// sort by increasing order
222193
allValues.AsSpan().StableSort((a, b) => {
223-
var aNumber = Convert<T, TNumber>(a);
224-
var bNumber = Convert<T, TNumber>(b);
194+
var aNumber = a.Convert<T, TNumber>();
195+
var bNumber = b.Convert<T, TNumber>();
225196
return aNumber.CompareTo(bNumber);
226197
});
227198

228199
return allValues;
229200
}
230201

202+
#pragma warning restore ST0006 ST0007 ST0008 ST0009
203+
231204
private static bool IsIgnored(T value)
232205
{
233206
var attr = value.GetType().GetField(value.ToString())?.GetCustomAttribute<OrderedIndexIgnoreAttribute>();
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Runtime.CompilerServices;
6+
7+
namespace Silk.NET.Input;
8+
9+
/// <summary>
10+
/// Some unsafe methods for comparing numeric values of simple numeric types.
11+
/// </summary>
12+
[Experimental(diagnosticId: "ST0006", UrlFormat = "https://dotnet.github.io/Silk.NET/docs/v3/silk.net/diagnostics/{0}")]
13+
public static class UnsafeNumericValueExtensions
14+
{
15+
/// <param name="value"></param>
16+
/// <typeparam name="T"></typeparam>
17+
extension<T>(T value) where T : unmanaged
18+
{
19+
/// <summary>
20+
/// Compares two values of the same type for equality.
21+
/// Type must be no larger than 8 bytes.
22+
/// </summary>
23+
/// <param name="other"></param>
24+
/// <returns></returns>
25+
[Experimental(diagnosticId: "ST0007", UrlFormat = "https://dotnet.github.io/Silk.NET/docs/v3/silk.net/diagnostics/{0}")]
26+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
27+
public bool Equals(T other) => value.Convert<T, ulong>() == other.Convert<T, ulong>();
28+
29+
/// <summary>
30+
/// Compares two values of differing types for numeric equality
31+
/// Types must be no larger than 8 bytes.
32+
/// </summary>
33+
/// <param name="right"></param>
34+
/// <typeparam name="T2"></typeparam>
35+
/// <returns></returns>
36+
[Experimental(diagnosticId: "ST0008", UrlFormat = "https://dotnet.github.io/Silk.NET/docs/v3/silk.net/diagnostics/{0}")]
37+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
38+
public bool Equals<T2>(T2 right) where T2 : unmanaged => value.Convert<T, ulong>() == right.Convert<T2, ulong>();
39+
40+
/// <summary>
41+
/// Returns the numerical value of the enum value provided in a type-safe way
42+
/// </summary>
43+
/// <typeparam name="TTo"></typeparam>
44+
/// <returns></returns>
45+
[Experimental(diagnosticId: "ST0009", UrlFormat = "https://dotnet.github.io/Silk.NET/docs/v3/silk.net/diagnostics/{0}")]
46+
internal unsafe TTo Convert<TTo>() where TTo : unmanaged
47+
{
48+
if (sizeof(T) == sizeof(TTo))
49+
{
50+
return Unsafe.Read<TTo>(&value);
51+
}
52+
53+
var minSize = Math.Min(sizeof(TTo), sizeof(T));
54+
55+
var originalValuePtr = (byte*)&value;
56+
57+
var valuePtr = &originalValuePtr[Math.Abs(minSize - sizeof(T))]; // todo: does this assume little-endianness?
58+
var numberPtr = stackalloc byte[sizeof(TTo)];
59+
60+
// ensure block is initialized (as it isnt guaranteed?) so any missing bytes of the output will stay 0
61+
// if type TNumber is a larger size than type T
62+
Unsafe.InitBlock(numberPtr, 0, (uint)sizeof(TTo));
63+
64+
var copyToPtr = &numberPtr[Math.Abs(minSize - sizeof(TTo))];
65+
Buffer.MemoryCopy(valuePtr, copyToPtr, sizeof(TTo), minSize);
66+
return *(TTo*)numberPtr;
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)