mirror of https://github.com/ppy/SDL3-CS.git
Add `Utf8String`, sourcegen and tests
This commit is contained in:
parent
5b9bbd518e
commit
72a002ead0
|
|
@ -11,9 +11,9 @@ namespace SDL3.SourceGeneration
|
|||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Change <c>const char*</c> function parameters to <c>ReadOnlySpan<byte></c>.
|
||||
/// Change <c>const char*</c> function parameters to <c>SDLUtf8String</c>.
|
||||
/// </summary>
|
||||
ChangeParamsToReadOnlySpan = 1 << 0,
|
||||
ChangeParamsToUtf8String = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Change <c>char *</c> or <c>const char *</c> return type to <see cref="string"/>.
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ using System;
|
|||
{
|
||||
if (param.IsTypeConstCharPtr())
|
||||
{
|
||||
Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToReadOnlySpan));
|
||||
yield return param.WithType(SyntaxFactory.ParseTypeName("ReadOnlySpan<byte>"))
|
||||
Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToUtf8String));
|
||||
yield return param.WithType(SyntaxFactory.ParseTypeName(Helper.Utf8StringStructName))
|
||||
.WithAttributeLists(SyntaxFactory.List<AttributeListSyntax>());
|
||||
}
|
||||
else
|
||||
|
|
@ -102,7 +102,7 @@ using System;
|
|||
|
||||
foreach (var param in gm.NativeMethod.ParameterList.Parameters.Where(p => p.IsTypeConstCharPtr()).Reverse())
|
||||
{
|
||||
Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToReadOnlySpan));
|
||||
Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToUtf8String));
|
||||
|
||||
expr = SyntaxFactory.FixedStatement(
|
||||
SyntaxFactory.VariableDeclaration(
|
||||
|
|
@ -112,7 +112,10 @@ using System;
|
|||
param.Identifier.ValueText + pointer_suffix)
|
||||
.WithInitializer(
|
||||
SyntaxFactory.EqualsValueClause(
|
||||
SyntaxFactory.IdentifierName(param.Identifier))))),
|
||||
SyntaxFactory.MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
SyntaxFactory.IdentifierName(param.Identifier),
|
||||
SyntaxFactory.IdentifierName(Helper.Utf8StringReadOnlySpanFieldName)))))),
|
||||
expr);
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +164,7 @@ using System;
|
|||
{
|
||||
if (param.IsTypeConstCharPtr())
|
||||
{
|
||||
Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToReadOnlySpan));
|
||||
Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToUtf8String));
|
||||
yield return SyntaxFactory.Argument(SyntaxFactory.IdentifierName(param.Identifier.ValueText + pointer_suffix));
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ namespace SDL3.SourceGeneration
|
|||
/// </remarks>
|
||||
public const string UnsafePrefix = "Unsafe_";
|
||||
|
||||
public const string Utf8StringStructName = "Utf8String";
|
||||
|
||||
public const string Utf8StringReadOnlySpanFieldName = "Raw";
|
||||
|
||||
public static bool IsVoid(this TypeSyntax type) => type is PredefinedTypeSyntax predefined
|
||||
&& predefined.Keyword.IsKind(SyntaxKind.VoidKeyword);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace SDL3.SourceGeneration
|
|||
foreach (var parameter in method.ParameterList.Parameters)
|
||||
{
|
||||
if (parameter.IsTypeConstCharPtr())
|
||||
changes |= Changes.ChangeParamsToReadOnlySpan;
|
||||
changes |= Changes.ChangeParamsToUtf8String;
|
||||
}
|
||||
|
||||
if (changes != Changes.None)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ namespace SDL3.Tests
|
|||
Debug.Assert(SDL_GetHint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4) == "null byte ");
|
||||
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, "1"u8);
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, "1");
|
||||
|
||||
using (var window = new MyWindow())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using SDL;
|
||||
|
||||
namespace SDL3.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestUtf8String
|
||||
{
|
||||
[Test]
|
||||
public void TestNullImplicitConversion()
|
||||
{
|
||||
checkNull(null);
|
||||
checkNull(default);
|
||||
}
|
||||
|
||||
[TestCase(null, -1)]
|
||||
[TestCase("", 1)]
|
||||
[TestCase("\0", 1)]
|
||||
[TestCase("test\0", 5)]
|
||||
[TestCase("test\0test", 10)]
|
||||
[TestCase("test\0test\0", 10)]
|
||||
public static void TestString(string? str, int expectedLength)
|
||||
{
|
||||
if (str == null)
|
||||
checkNull(str);
|
||||
else
|
||||
check(str, expectedLength);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestNullSpan()
|
||||
{
|
||||
ReadOnlySpan<byte> span = null;
|
||||
checkNull(span);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestDefaultSpan()
|
||||
{
|
||||
ReadOnlySpan<byte> span = default;
|
||||
checkNull(span);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestNewSpan()
|
||||
{
|
||||
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>();
|
||||
checkNull(span);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestReadOnlySpan()
|
||||
{
|
||||
check(""u8, 1);
|
||||
check("\0"u8, 1);
|
||||
check("test"u8, 5);
|
||||
check("test\0"u8, 5);
|
||||
check("test\0test"u8, 10);
|
||||
check("test\0test\0"u8, 10);
|
||||
}
|
||||
|
||||
private static unsafe void checkNull(Utf8String s)
|
||||
{
|
||||
Assert.That(s.Raw == null, "s.Raw == null");
|
||||
Assert.That(s.Raw.Length, Is.EqualTo(0));
|
||||
|
||||
fixed (byte* ptr = s.Raw)
|
||||
{
|
||||
Assert.That(ptr == null, "ptr == null");
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void check(Utf8String s, int expectedLength)
|
||||
{
|
||||
Assert.That(s.Raw.Length, Is.EqualTo(expectedLength));
|
||||
|
||||
fixed (byte* ptr = s.Raw)
|
||||
{
|
||||
Assert.That(ptr != null, "ptr != null");
|
||||
Assert.That(ptr[s.Raw.Length - 1], Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
|
||||
namespace SDL
|
||||
{
|
||||
public partial struct SDL_MessageBoxButtonData
|
||||
|
|
@ -18,7 +16,7 @@ namespace SDL
|
|||
public static partial class SDL3
|
||||
{
|
||||
// public static int SDL_ShowSimpleMessageBox([NativeTypeName("Uint32")] uint flags, [NativeTypeName("const char *")] byte* title, [NativeTypeName("const char *")] byte* message, SDL_Window* window);
|
||||
public static unsafe int SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, ReadOnlySpan<byte> title, ReadOnlySpan<byte> message, SDL_Window* window)
|
||||
public static unsafe int SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, Utf8String title, Utf8String message, SDL_Window* window)
|
||||
=> SDL_ShowSimpleMessageBox((uint)flags, title, message, window);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
|
||||
namespace SDL
|
||||
{
|
||||
public partial struct SDL_RendererInfo
|
||||
|
|
@ -12,7 +10,7 @@ namespace SDL
|
|||
|
||||
public static partial class SDL3
|
||||
{
|
||||
public static unsafe SDL_Renderer* SDL_CreateRenderer(SDL_Window* window, ReadOnlySpan<byte> name, SDL_RendererFlags flags)
|
||||
public static unsafe SDL_Renderer* SDL_CreateRenderer(SDL_Window* window, Utf8String name, SDL_RendererFlags flags)
|
||||
=> SDL_CreateRenderer(window, name, (uint)flags);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SDL
|
||||
{
|
||||
/// <summary>
|
||||
/// Null pointer or a null-byte terminated UTF8 string suitable for use in native methods.
|
||||
/// </summary>
|
||||
/// <remarks>Should only be instantiated through implicit conversions.</remarks>
|
||||
public readonly ref struct Utf8String
|
||||
{
|
||||
public readonly ReadOnlySpan<byte> Raw;
|
||||
|
||||
private Utf8String(ReadOnlySpan<byte> raw)
|
||||
{
|
||||
Raw = raw;
|
||||
}
|
||||
|
||||
public static implicit operator Utf8String(string? str)
|
||||
{
|
||||
if (str == null)
|
||||
return new Utf8String(null);
|
||||
|
||||
return new Utf8String(Encoding.UTF8.GetBytes(ensureTrailingNull(str)));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static string ensureTrailingNull(string str) => str.EndsWith('\0') ? str : str + '\0';
|
||||
|
||||
public static implicit operator Utf8String(ReadOnlySpan<byte> raw)
|
||||
{
|
||||
if (raw == null)
|
||||
return new Utf8String(null);
|
||||
|
||||
if (raw.Length == 0)
|
||||
return new Utf8String(new ReadOnlySpan<byte>([0]));
|
||||
|
||||
if (raw[^1] != 0)
|
||||
{
|
||||
byte[] copy = new byte[raw.Length + 1];
|
||||
raw.CopyTo(copy);
|
||||
raw = copy;
|
||||
}
|
||||
|
||||
return new Utf8String(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue