WIP: Some test for string marshalling (based on TerraFX)

This commit is contained in:
Amer Koleci
2022-09-05 09:37:11 +02:00
parent 3737620e92
commit cc6b1a8b39
5 changed files with 451 additions and 2 deletions

View File

@@ -2,9 +2,66 @@
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information. // Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
using static Win32.Graphics.Dxgi.Apis; using static Win32.Graphics.Dxgi.Apis;
using static Win32.StringUtilities;
namespace Win32.Graphics.Dxgi; namespace Win32.Graphics.Dxgi;
public unsafe partial struct AdapterDescription
{
/// <include file='../Generated/DXGI.xml' path='doc/member[@name="DXGI_ADAPTER_DESC::Description"]/*' />
public readonly string DescriptionStr
{
get
{
fixed (ushort* ptr = Description)
{
return GetString(ptr, 128) ?? string.Empty;
}
}
}
}
public unsafe partial struct AdapterDescription1
{
/// <include file='../Generated/DXGI.xml' path='doc/member[@name="DXGI_ADAPTER_DESC1::Description"]/*' />
public readonly string DescriptionStr
{
get
{
fixed (ushort* ptr = Description)
{
return GetString(ptr, 128) ?? string.Empty;
}
}
}
}
public unsafe partial struct AdapterDescription2
{
public readonly ReadOnlySpan<ushort> DescriptionSpan
{
get
{
fixed (ushort* ptr = Description)
{
return GetUtf16Span(ptr, 128);
}
}
}
/// <include file='../Generated/DXGI.xml' path='doc/member[@name="DXGI_ADAPTER_DESC2::Description"]/*' />
public readonly string DescriptionStr
{
get
{
fixed (ushort* ptr = Description)
{
return GetString(ptr, 128) ?? string.Empty;
}
}
}
}
public unsafe partial struct IDXGIFactory5 public unsafe partial struct IDXGIFactory5
{ {
public TFeature CheckFeatureSupport<TFeature>(Feature feature) public TFeature CheckFeatureSupport<TFeature>(Feature feature)

View File

@@ -23,6 +23,17 @@ internal static class MemoryMarshal
return ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(span); return ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(span);
} }
/// <summary>
/// Returns a reference to the 0th element of <paramref name="array"/>. If the array is empty, returns a reference
/// to where the 0th element would have been stored. Such a reference may be used for pinning but must never be dereferenced.
/// </summary>
/// <exception cref="NullReferenceException"><paramref name="array"/> is <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetArrayDataReference<T>(T[] array)
{
return ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(array.AsSpan());
}
/// <summary> /// <summary>
/// Creates a new <see cref="Span{T}"/> from a given reference. /// Creates a new <see cref="Span{T}"/> from a given reference.
/// </summary> /// </summary>
@@ -34,6 +45,18 @@ internal static class MemoryMarshal
{ {
return new(Unsafe.AsPointer(ref value), length); return new(Unsafe.AsPointer(ref value), length);
} }
/// <summary>
/// Creates a new <see cref="ReadOnlySpan{T}"/> from a given reference.
/// </summary>
/// <typeparam name="T">The type of reference to wrap.</typeparam>
/// <param name="value">The target reference.</param>
/// <param name="length">The length of the <see cref="Span{T}"/> to create.</param>
/// <returns>A new <see cref="Span{T}"/> wrapping <paramref name="value"/>.</returns>
public static unsafe ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T value, int length)
{
return new(Unsafe.AsPointer(ref value), length);
}
} }
#endif #endif

View File

@@ -0,0 +1,176 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
// Copyright © Tanner Gooding and Contributors. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
using System.Runtime.CompilerServices;
using System.Text;
using static Win32.UnsafeUtilities;
namespace Win32;
/// <summary>
/// Provides a set of methods to supplement for <see cref="string" />.
/// </summary>
public static unsafe class StringUtilities
{
public static string? GetString(sbyte* pointer, int maxLength = -1)
{
return GetUtf8Span(pointer, maxLength).GetString();
}
public static string? GetString(ushort* pointer, int maxLength = -1)
{
return GetUtf16Span(pointer, maxLength).GetString();
}
/// <summary>Gets a null-terminated sequence of UTF8 characters for a string.</summary>
/// <param name="source">The string for which to get the null-terminated UTF8 character sequence.</param>
/// <returns>A null-terminated UTF8 character sequence created from <paramref name="source" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<sbyte> GetUtf8Span(this string? source)
{
ReadOnlySpan<byte> result;
if (source is not null)
{
int maxLength = Encoding.UTF8.GetMaxByteCount(source.Length);
#if NET6_0_OR_GREATER
var bytes = new byte[maxLength + 1];
var length = Encoding.UTF8.GetBytes(source, bytes);
result = bytes.AsSpan(0, length);
#else
byte* bytes = stackalloc byte[maxLength + 1];
fixed (char* namePtr = source)
{
Encoding.UTF8.GetBytes(namePtr, source.Length, bytes, maxLength);
}
bytes[maxLength] = 0;
result = new(bytes, source.Length);
#endif
}
else
{
result = null;
}
return result.As<byte, sbyte>();
}
/// <summary>Gets a span for a null-terminated UTF8 character sequence.</summary>
/// <param name="source">The pointer to a null-terminated UTF8 character sequence.</param>
/// <param name="maxLength">The maxmimum length of <paramref name="source" /> or <c>-1</c> if the maximum length is unknown.</param>
/// <returns>A span that starts at <paramref name="source" /> and extends to <paramref name="maxLength" /> or the first null character, whichever comes first.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<sbyte> GetUtf8Span(sbyte* source, int maxLength = -1)
=> (source != null) ? GetUtf8Span(in source[0], maxLength) : null;
/// <summary>Gets a span for a null-terminated UTF8 character sequence.</summary>
/// <param name="source">The reference to a null-terminated UTF8 character sequence.</param>
/// <param name="maxLength">The maxmimum length of <paramref name="source" /> or <c>-1</c> if the maximum length is unknown.</param>
/// <returns>A span that starts at <paramref name="source" /> and extends to <paramref name="maxLength" /> or the first null character, whichever comes first.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<sbyte> GetUtf8Span(in sbyte source, int maxLength = -1)
{
ReadOnlySpan<sbyte> result;
if (!IsNullRef(in source))
{
if (maxLength < 0)
{
maxLength = int.MaxValue;
}
result = CreateReadOnlySpan(in source, maxLength);
var length = result.IndexOf((sbyte)'\0');
if (length != -1)
{
result = result.Slice(0, length);
}
}
else
{
result = null;
}
return result;
}
/// <summary>Gets a null-terminated sequence of UTF16 characters for a string.</summary>
/// <param name="source">The string for which to get the null-terminated UTF16 character sequence.</param>
/// <returns>A null-terminated UTF16 character sequence created from <paramref name="source" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<ushort> GetUtf16Span(this string? source) => source.AsSpan().As<char, ushort>();
/// <summary>Marshals a null-terminated UTF16 string to a <see cref="ReadOnlySpan{UInt16}" />.</summary>
/// <param name="source">The pointer to a null-terminated UTF16 string.</param>
/// <param name="maxLength">The maxmimum length of <paramref name="source" /> or <c>-1</c> if the maximum length is unknown.</param>
/// <returns>A <see cref="ReadOnlySpan{UInt16}" /> that starts at <paramref name="source" /> and extends to <paramref name="maxLength" /> or the first null character, whichever comes first.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<ushort> GetUtf16Span(ushort* source, int maxLength = -1)
=> (source != null) ? GetUtf16Span(in source[0], maxLength) : null;
/// <summary>Marshals a null-terminated UTF16 string to a <see cref="ReadOnlySpan{UInt16}" />.</summary>
/// <param name="source">The reference to a null-terminated UTF16 string.</param>
/// <param name="maxLength">The maxmimum length of <paramref name="source" /> or <c>-1</c> if the maximum length is unknown.</param>
/// <returns>A <see cref="ReadOnlySpan{UInt16}" /> that starts at <paramref name="source" /> and extends to <paramref name="maxLength" /> or the first null character, whichever comes first.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<ushort> GetUtf16Span(in ushort source, int maxLength = -1)
{
ReadOnlySpan<ushort> result;
if (!IsNullRef(in source))
{
if (maxLength < 0)
{
maxLength = int.MaxValue;
}
result = CreateReadOnlySpan(in source, maxLength);
var length = result.IndexOf('\0');
if (length != -1)
{
result = result.Slice(0, length);
}
}
else
{
result = null;
}
return result;
}
/// <summary>Gets a string for a given span.</summary>
/// <param name="span">The span for which to create the string.</param>
/// <returns>A string created from <paramref name="span" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string? GetString(this ReadOnlySpan<ushort> span)
{
if (span.GetPointer() == null)
return null;
#if NET6_0_OR_GREATER
return new string(span.As<ushort, char>());
#else
return new string(span.As<ushort, char>().GetPointer(), 0, span.Length);
#endif
}
/// <summary>Gets a string for a given span.</summary>
/// <param name="span">The span for which to create the string.</param>
/// <returns>A string created from <paramref name="span" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string? GetString(this ReadOnlySpan<sbyte> span)
{
if (span.GetPointer() == null)
return null;
#if NET6_0_OR_GREATER
return Encoding.UTF8.GetString(span.As<sbyte, byte>());
#else
return Encoding.UTF8.GetString(span.As<sbyte, byte>().GetPointer(), span.Length);
#endif
}
}

View File

@@ -0,0 +1,171 @@
// Copyright © Tanner Gooding and Contributors. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
#if !NET6_0_OR_GREATER
using MemoryMarshal = Win32.MemoryMarshal;
#endif
namespace Win32;
/// <summary>Provides a set of methods to supplement or replace <see cref="Unsafe" /> and <see cref="MemoryMarshal" />.</summary>
public static unsafe class UnsafeUtilities
{
/// <inheritdoc cref="Unsafe.As{T}(object)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NET6_0_OR_GREATER
[return: NotNullIfNotNull("o")]
#endif
public static T? As<T>(this object? o)
where T : class?
{
//Assert(o is null or T);
return Unsafe.As<T>(o);
}
/// <inheritdoc cref="Unsafe.As{TFrom, TTo}(ref TFrom)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TTo As<TFrom, TTo>(ref TFrom source)
=> ref Unsafe.As<TFrom, TTo>(ref source);
/// <inheritdoc cref="Unsafe.As{TFrom, TTo}(ref TFrom)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<TTo> As<TFrom, TTo>(this Span<TFrom> span)
where TFrom : unmanaged
where TTo : unmanaged
{
//Assert(AssertionsEnabled && (SizeOf<TFrom>() == SizeOf<TTo>()));
return CreateSpan(ref As<TFrom, TTo>(ref span.GetReference()), span.Length);
}
/// <inheritdoc cref="Unsafe.As{TFrom, TTo}(ref TFrom)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<TTo> As<TFrom, TTo>(this ReadOnlySpan<TFrom> span)
where TFrom : unmanaged
where TTo : unmanaged
{
//Assert(AssertionsEnabled && (SizeOf<TFrom>() == SizeOf<TTo>()));
return CreateReadOnlySpan(in AsReadOnly<TFrom, TTo>(in span.GetReference()), span.Length);
}
/// <summary>Reinterprets the given native integer as a reference.</summary>
/// <typeparam name="T">The type of the reference.</typeparam>
/// <param name="source">The native integer to reinterpret.</param>
/// <returns>A reference to a value of type <typeparamref name="T" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(nint source) => ref Unsafe.AsRef<T>((void*)source);
/// <summary>Reinterprets the given native unsigned integer as a reference.</summary>
/// <typeparam name="T">The type of the reference.</typeparam>
/// <param name="source">The native unsigned integer to reinterpret.</param>
/// <returns>A reference to a value of type <typeparamref name="T" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(nuint source) => ref Unsafe.AsRef<T>((void*)source);
/// <inheritdoc cref="Unsafe.AsRef{T}(in T)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(in T source) => ref Unsafe.AsRef(in source);
/// <inheritdoc cref="Unsafe.AsRef{T}(in T)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TTo AsRef<TFrom, TTo>(in TFrom source)
{
ref var mutable = ref AsRef(in source);
return ref As<TFrom, TTo>(ref mutable);
}
/// <inheritdoc cref="Unsafe.AsPointer{T}(ref T)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* AsPointer<T>(ref T source)
where T : unmanaged => (T*)Unsafe.AsPointer(ref source);
/// <inheritdoc cref="Unsafe.As{TFrom, TTo}(ref TFrom)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly TTo AsReadOnly<TFrom, TTo>(in TFrom source)
=> ref Unsafe.As<TFrom, TTo>(ref AsRef(in source));
/// <inheritdoc cref="Unsafe.AsPointer{T}(ref T)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* AsReadOnlyPointer<T>(in T source)
where T : unmanaged => AsPointer(ref AsRef(in source));
/// <inheritdoc cref="Unsafe.AsRef{T}(void*)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(void* source) => ref Unsafe.AsRef<T>(source);
/// <inheritdoc cref="MemoryMarshal.GetArrayDataReference{T}(T[])" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference<T>(this T[] array) => ref MemoryMarshal.GetArrayDataReference(array);
/// <inheritdoc cref="MemoryMarshal.GetArrayDataReference{T}(T[])" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference<T>(this T[] array, int index) => ref Unsafe.Add(ref array.GetReference(), index);
/// <inheritdoc cref="MemoryMarshal.GetArrayDataReference{T}(T[])" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference<T>(this T[] array, nuint index) => ref Unsafe.Add(ref array.GetReference(), index);
/// <inheritdoc cref="MemoryMarshal.GetReference{T}(Span{T})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference<T>(this Span<T> span) => ref MemoryMarshal.GetReference(span);
/// <inheritdoc cref="MemoryMarshal.GetReference{T}(Span{T})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference<T>(this Span<T> span, int index) => ref Unsafe.Add(ref MemoryMarshal.GetReference(span), index);
/// <inheritdoc cref="MemoryMarshal.GetReference{T}(Span{T})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference<T>(this Span<T> span, nuint index) => ref Unsafe.Add(ref MemoryMarshal.GetReference(span), index);
/// <inheritdoc cref="MemoryMarshal.GetReference{T}(ReadOnlySpan{T})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T GetReference<T>(this ReadOnlySpan<T> span) => ref MemoryMarshal.GetReference(span);
/// <inheritdoc cref="MemoryMarshal.GetReference{T}(ReadOnlySpan{T})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T GetReference<T>(this ReadOnlySpan<T> span, int index) => ref Unsafe.Add(ref MemoryMarshal.GetReference(span), index);
/// <inheritdoc cref="MemoryMarshal.GetReference{T}(ReadOnlySpan{T})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T GetReference<T>(this ReadOnlySpan<T> span, nuint index) => ref Unsafe.Add(ref MemoryMarshal.GetReference(span), index);
/// <summary>Determines if a given reference to a value of type <typeparamref name="T" /> is not a null reference.</summary>
/// <typeparam name="T">The type of the reference</typeparam>
/// <param name="source">The reference to check.</param>
/// <returns><c>true</c> if <paramref name="source" /> is not a null reference; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNotNullRef<T>(in T source) => !IsNullRef(in source);
/// <inheritdoc cref="Unsafe.IsNullRef{T}(ref T)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNullRef<T>(in T source) => Unsafe.IsNullRef(ref AsRef(in source));
/// <inheritdoc cref="Unsafe.NullRef{T}" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T NullRef<T>() => ref Unsafe.NullRef<T>();
/// <inheritdoc cref="MemoryMarshal.CreateSpan{T}(ref T, int)" />
public static Span<T> CreateSpan<T>(ref T reference, int length) => MemoryMarshal.CreateSpan(ref reference, length);
/// <inheritdoc cref="MemoryMarshal.CreateReadOnlySpan{T}(ref T, int)" />
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length) => MemoryMarshal.CreateReadOnlySpan(ref AsRef(in reference), length);
/// <summary>Returns a pointer to the element of the span at index zero.</summary>
/// <typeparam name="T">The type of items in <paramref name="span" />.</typeparam>
/// <param name="span">The span from which the pointer is retrieved.</param>
/// <returns>A pointer to the item at index zero of <paramref name="span" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* GetPointer<T>(this Span<T> span)
where T : unmanaged => AsPointer(ref span.GetReference());
/// <summary>Returns a pointer to the element of the span at index zero.</summary>
/// <typeparam name="T">The type of items in <paramref name="span" />.</typeparam>
/// <param name="span">The span from which the pointer is retrieved.</param>
/// <returns>A pointer to the item at index zero of <paramref name="span" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* GetPointer<T>(this ReadOnlySpan<T> span)
where T : unmanaged => AsPointer(ref AsRef(in span.GetReference()));
}

View File

@@ -12,6 +12,9 @@ public static unsafe class Program
{ {
public static void Main() public static void Main()
{ {
string test = StringUtilities.GetString(new sbyte[] { (sbyte)'A', (sbyte)'B', (sbyte)'C' });
test = StringUtilities.GetString(new ushort[] { 'A', 'B', 'C' });
using ComPtr<IDXGIFactory1> factory = default; using ComPtr<IDXGIFactory1> factory = default;
HResult hr = CreateDXGIFactory1(__uuidof<IDXGIFactory4>(), (void**)&factory); HResult hr = CreateDXGIFactory1(__uuidof<IDXGIFactory4>(), (void**)&factory);
@@ -24,15 +27,34 @@ public static unsafe class Program
} }
using ComPtr<IDXGIAdapter1> adapter = default; using ComPtr<IDXGIAdapter1> adapter = default;
using ComPtr<IDXGIFactory6> factory6 = default;
if (factory.CopyTo(&factory6).Success)
{
for (uint adapterIndex = 0;
factory6.Get()->EnumAdapterByGpuPreference(
adapterIndex,
GpuPreference.HighPerformance,
__uuidof<IDXGIAdapter1>(),
(void**)adapter.ReleaseAndGetAddressOf()).Success;
adapterIndex++)
{
AdapterDescription1 desc = default;
adapter.Get()->GetDesc1(&desc);
string name = desc.DescriptionStr;
}
}
for (uint adapterIndex = 0; for (uint adapterIndex = 0;
factory.Get()->EnumAdapters1(adapterIndex, adapter.ReleaseAndGetAddressOf()).Success; factory.Get()->EnumAdapters1(adapterIndex, adapter.ReleaseAndGetAddressOf()).Success;
adapterIndex++) adapterIndex++)
{ {
AdapterDescription1 desc = default; AdapterDescription1 desc = default;
adapter.Get()->GetDesc1(&desc); adapter.Get()->GetDesc1(&desc);
string name = desc.DescriptionStr;
} }
} }
[DllImport("dxgi", ExactSpelling = true)] [DllImport("dxgi", ExactSpelling = true)]