From e0a7e3dec910b09e22f97e5e037ece3cf73413c2 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 16 Apr 2024 12:02:17 +0200 Subject: [PATCH 01/14] Fix .editorconfig generated code glob pattern --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index a5f783f..e971c4b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ indent_style = space indent_size = 2 trim_trailing_whitespace = true -[*g.cs] +[*.g.cs] generated_code = true [*.cs] From 5b9bbd518e88d34761763dd8d21a2eadcb5f505e Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 16 Apr 2024 12:47:11 +0200 Subject: [PATCH 02/14] Make SDL3-CS.Tests a proper test project --- SDL3-CS.Tests/SDL3-CS.Tests.csproj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/SDL3-CS.Tests/SDL3-CS.Tests.csproj b/SDL3-CS.Tests/SDL3-CS.Tests.csproj index 0fe8dc8..f38b831 100644 --- a/SDL3-CS.Tests/SDL3-CS.Tests.csproj +++ b/SDL3-CS.Tests/SDL3-CS.Tests.csproj @@ -7,8 +7,15 @@ enable enable true + false + + + + + + From 72a002ead044bebdb26d2afcda5053f93e0449f3 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 16 Apr 2024 12:59:17 +0200 Subject: [PATCH 03/14] Add `Utf8String`, sourcegen and tests --- SDL3-CS.SourceGeneration/Changes.cs | 4 +- .../FriendlyOverloadGenerator.cs | 13 +-- SDL3-CS.SourceGeneration/Helper.cs | 4 + .../UnfriendlyMethodFinder.cs | 2 +- SDL3-CS.Tests/Program.cs | 1 + SDL3-CS.Tests/TestUtf8String.cs | 87 +++++++++++++++++++ SDL3-CS/SDL3/SDL_messagebox.cs | 4 +- SDL3-CS/SDL3/SDL_render.cs | 4 +- SDL3-CS/Utf8String.cs | 52 +++++++++++ 9 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 SDL3-CS.Tests/TestUtf8String.cs create mode 100644 SDL3-CS/Utf8String.cs diff --git a/SDL3-CS.SourceGeneration/Changes.cs b/SDL3-CS.SourceGeneration/Changes.cs index 02a70eb..e564da3 100644 --- a/SDL3-CS.SourceGeneration/Changes.cs +++ b/SDL3-CS.SourceGeneration/Changes.cs @@ -11,9 +11,9 @@ public enum Changes None, /// - /// Change const char* function parameters to ReadOnlySpan<byte>. + /// Change const char* function parameters to SDLUtf8String. /// - ChangeParamsToReadOnlySpan = 1 << 0, + ChangeParamsToUtf8String = 1 << 0, /// /// Change char * or const char * return type to . diff --git a/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs b/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs index 0830bd1..c8a2935 100644 --- a/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs +++ b/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs @@ -83,8 +83,8 @@ private static IEnumerable transformParams(GeneratedMethod gm) { if (param.IsTypeConstCharPtr()) { - Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToReadOnlySpan)); - yield return param.WithType(SyntaxFactory.ParseTypeName("ReadOnlySpan")) + Debug.Assert(gm.RequiredChanges.HasFlag(Changes.ChangeParamsToUtf8String)); + yield return param.WithType(SyntaxFactory.ParseTypeName(Helper.Utf8StringStructName)) .WithAttributeLists(SyntaxFactory.List()); } else @@ -102,7 +102,7 @@ private static StatementSyntax makeMethodBody(GeneratedMethod gm) 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 @@ private static StatementSyntax makeMethodBody(GeneratedMethod gm) 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 @@ private static IEnumerable makeArguments(GeneratedMethod gm) { 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 diff --git a/SDL3-CS.SourceGeneration/Helper.cs b/SDL3-CS.SourceGeneration/Helper.cs index 2eb2dad..65d84f9 100644 --- a/SDL3-CS.SourceGeneration/Helper.cs +++ b/SDL3-CS.SourceGeneration/Helper.cs @@ -16,6 +16,10 @@ public static class Helper /// 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); diff --git a/SDL3-CS.SourceGeneration/UnfriendlyMethodFinder.cs b/SDL3-CS.SourceGeneration/UnfriendlyMethodFinder.cs index 944fa3c..81f40bc 100644 --- a/SDL3-CS.SourceGeneration/UnfriendlyMethodFinder.cs +++ b/SDL3-CS.SourceGeneration/UnfriendlyMethodFinder.cs @@ -42,7 +42,7 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode) foreach (var parameter in method.ParameterList.Parameters) { if (parameter.IsTypeConstCharPtr()) - changes |= Changes.ChangeParamsToReadOnlySpan; + changes |= Changes.ChangeParamsToUtf8String; } if (changes != Changes.None) diff --git a/SDL3-CS.Tests/Program.cs b/SDL3-CS.Tests/Program.cs index 609d1fe..d8db8dc 100644 --- a/SDL3-CS.Tests/Program.cs +++ b/SDL3-CS.Tests/Program.cs @@ -31,6 +31,7 @@ public static void Main() 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()) { diff --git a/SDL3-CS.Tests/TestUtf8String.cs b/SDL3-CS.Tests/TestUtf8String.cs new file mode 100644 index 0000000..f4ddd51 --- /dev/null +++ b/SDL3-CS.Tests/TestUtf8String.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . 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 span = null; + checkNull(span); + } + + [Test] + public static void TestDefaultSpan() + { + ReadOnlySpan span = default; + checkNull(span); + } + + [Test] + public static void TestNewSpan() + { + ReadOnlySpan span = new ReadOnlySpan(); + 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)); + } + } + } +} diff --git a/SDL3-CS/SDL3/SDL_messagebox.cs b/SDL3-CS/SDL3/SDL_messagebox.cs index 764cbbc..e4b79eb 100644 --- a/SDL3-CS/SDL3/SDL_messagebox.cs +++ b/SDL3-CS/SDL3/SDL_messagebox.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . 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 @@ public partial struct SDL_MessageBoxData 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 title, ReadOnlySpan 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); } } diff --git a/SDL3-CS/SDL3/SDL_render.cs b/SDL3-CS/SDL3/SDL_render.cs index 896953b..ed2e04d 100644 --- a/SDL3-CS/SDL3/SDL_render.cs +++ b/SDL3-CS/SDL3/SDL_render.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . 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 @@ public partial struct SDL_RendererInfo public static partial class SDL3 { - public static unsafe SDL_Renderer* SDL_CreateRenderer(SDL_Window* window, ReadOnlySpan 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); } } diff --git a/SDL3-CS/Utf8String.cs b/SDL3-CS/Utf8String.cs new file mode 100644 index 0000000..56b8d74 --- /dev/null +++ b/SDL3-CS/Utf8String.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . 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 +{ + /// + /// Null pointer or a null-byte terminated UTF8 string suitable for use in native methods. + /// + /// Should only be instantiated through implicit conversions. + public readonly ref struct Utf8String + { + public readonly ReadOnlySpan Raw; + + private Utf8String(ReadOnlySpan 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 raw) + { + if (raw == null) + return new Utf8String(null); + + if (raw.Length == 0) + return new Utf8String(new ReadOnlySpan([0])); + + if (raw[^1] != 0) + { + byte[] copy = new byte[raw.Length + 1]; + raw.CopyTo(copy); + raw = copy; + } + + return new Utf8String(raw); + } + } +} From 8e0066d7f7fd96c881f89a35094ba81f56afff72 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 16 Apr 2024 13:01:13 +0200 Subject: [PATCH 04/14] Remove unnecessary `UTF8GetBytes()` helper --- SDL3-CS.Tests/Program.cs | 13 ------------- SDL3-CS/SDL3.cs | 12 ------------ 2 files changed, 25 deletions(-) diff --git a/SDL3-CS.Tests/Program.cs b/SDL3-CS.Tests/Program.cs index d8db8dc..3eeccb8 100644 --- a/SDL3-CS.Tests/Program.cs +++ b/SDL3-CS.Tests/Program.cs @@ -14,19 +14,6 @@ public static void Main() { Console.OutputEncoding = Encoding.UTF8; - unsafe - { - // Encoding.UTF8.GetBytes can churn out null pointers and doesn't guarantee null termination - fixed (byte* badPointer = Encoding.UTF8.GetBytes("")) - Debug.Assert(badPointer == null); - - fixed (byte* pointer = UTF8GetBytes("")) - { - Debug.Assert(pointer != null); - Debug.Assert(pointer[0] == '\0'); - } - } - SDL_SetHint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, "null byte \0 in string"u8); Debug.Assert(SDL_GetHint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4) == "null byte "); diff --git a/SDL3-CS/SDL3.cs b/SDL3-CS/SDL3.cs index bf00f9f..6aed6af 100644 --- a/SDL3-CS/SDL3.cs +++ b/SDL3-CS/SDL3.cs @@ -27,17 +27,5 @@ public static unsafe partial class SDL3 return s; } - - /// - /// UTF8 encodes a managed string to a byte array suitable for use in ReadOnlySpan<byte> parameters of SDL functions. - /// - /// The string to encode. - /// A null-terminated byte array. - public static byte[] UTF8GetBytes(string s) - { - byte[] array = Encoding.UTF8.GetBytes(s + '\0'); - Debug.Assert(array[^1] == '\0'); - return array; - } } } From 8a98ba51033ea20a196774172875d7706a8c1c27 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 16 Apr 2024 13:02:14 +0200 Subject: [PATCH 05/14] Add rider generated files --- .idea/.idea.SDL3-CS.Desktop/.idea/.gitignore | 11 +++++++++++ .idea/.idea.SDL3-CS.Desktop/.idea/.name | 1 + .idea/.idea.SDL3-CS.Desktop/.idea/encodings.xml | 4 ++++ .idea/.idea.SDL3-CS.Desktop/.idea/indexLayout.xml | 8 ++++++++ 4 files changed, 24 insertions(+) create mode 100644 .idea/.idea.SDL3-CS.Desktop/.idea/.gitignore create mode 100644 .idea/.idea.SDL3-CS.Desktop/.idea/.name create mode 100644 .idea/.idea.SDL3-CS.Desktop/.idea/encodings.xml create mode 100644 .idea/.idea.SDL3-CS.Desktop/.idea/indexLayout.xml diff --git a/.idea/.idea.SDL3-CS.Desktop/.idea/.gitignore b/.idea/.idea.SDL3-CS.Desktop/.idea/.gitignore new file mode 100644 index 0000000..090b233 --- /dev/null +++ b/.idea/.idea.SDL3-CS.Desktop/.idea/.gitignore @@ -0,0 +1,11 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/.idea.SDL3-CS.Desktop.iml +/modules.xml +/projectSettingsUpdater.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.SDL3-CS.Desktop/.idea/.name b/.idea/.idea.SDL3-CS.Desktop/.idea/.name new file mode 100644 index 0000000..216c862 --- /dev/null +++ b/.idea/.idea.SDL3-CS.Desktop/.idea/.name @@ -0,0 +1 @@ +SDL3-CS.Desktop \ No newline at end of file diff --git a/.idea/.idea.SDL3-CS.Desktop/.idea/encodings.xml b/.idea/.idea.SDL3-CS.Desktop/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.SDL3-CS.Desktop/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.SDL3-CS.Desktop/.idea/indexLayout.xml b/.idea/.idea.SDL3-CS.Desktop/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.SDL3-CS.Desktop/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From aab02bf6f9c30060f6eb220d5e4cdd3879c7867c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Apr 2024 12:08:38 +0900 Subject: [PATCH 06/14] Fix inspections --- SDL3-CS/SDL3.cs | 2 -- SDL3-CS/SDL3/SDL_log.cs | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/SDL3-CS/SDL3.cs b/SDL3-CS/SDL3.cs index 6aed6af..5b46e55 100644 --- a/SDL3-CS/SDL3.cs +++ b/SDL3-CS/SDL3.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Runtime.InteropServices; -using System.Text; namespace SDL { diff --git a/SDL3-CS/SDL3/SDL_log.cs b/SDL3-CS/SDL3/SDL_log.cs index dd19dc4..70ab926 100644 --- a/SDL3-CS/SDL3/SDL_log.cs +++ b/SDL3-CS/SDL3/SDL_log.cs @@ -1,13 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Runtime.CompilerServices; - namespace SDL { - public static partial class SDL3 + public static partial class SDL3 { - public static void SDL_LogSetPriority(SDL_LogCategory category, SDL_LogPriority priority) => SDL_LogSetPriority((int)category, priority); + public static void SDL_LogSetPriority(SDL_LogCategory category, SDL_LogPriority priority) => SDL_LogSetPriority((int)category, priority); public static SDL_LogPriority SDL_LogGetPriority(SDL_LogCategory category) => SDL_LogGetPriority((int)category); } } From 2f459c7a84a91d44f1249aa8a8daa246183ac631 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 12:10:35 +0200 Subject: [PATCH 07/14] Add missing test It was present in `TestReadOnlySpan` but not for strings. --- SDL3-CS.Tests/TestUtf8String.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/SDL3-CS.Tests/TestUtf8String.cs b/SDL3-CS.Tests/TestUtf8String.cs index f4ddd51..2c4ec5c 100644 --- a/SDL3-CS.Tests/TestUtf8String.cs +++ b/SDL3-CS.Tests/TestUtf8String.cs @@ -19,6 +19,7 @@ public void TestNullImplicitConversion() [TestCase(null, -1)] [TestCase("", 1)] [TestCase("\0", 1)] + [TestCase("test", 5)] [TestCase("test\0", 5)] [TestCase("test\0test", 10)] [TestCase("test\0test\0", 10)] From 6f8564fb9d3149250e92d26fd31332d1cda2cf1e Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 12:12:45 +0200 Subject: [PATCH 08/14] Cover all cases of "creating" `Utf8String`s --- SDL3-CS.Tests/TestUtf8String.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/SDL3-CS.Tests/TestUtf8String.cs b/SDL3-CS.Tests/TestUtf8String.cs index 2c4ec5c..be97d99 100644 --- a/SDL3-CS.Tests/TestUtf8String.cs +++ b/SDL3-CS.Tests/TestUtf8String.cs @@ -14,6 +14,7 @@ public void TestNullImplicitConversion() { checkNull(null); checkNull(default); + checkNull(new Utf8String()); // don't do this in actual code } [TestCase(null, -1)] From ec3ce75780ad529e9364232182f3ea977a21a0b2 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 12:14:05 +0200 Subject: [PATCH 09/14] Change deceptive test name --- SDL3-CS.Tests/TestUtf8String.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDL3-CS.Tests/TestUtf8String.cs b/SDL3-CS.Tests/TestUtf8String.cs index be97d99..93c9afc 100644 --- a/SDL3-CS.Tests/TestUtf8String.cs +++ b/SDL3-CS.Tests/TestUtf8String.cs @@ -10,7 +10,7 @@ namespace SDL3.Tests public class TestUtf8String { [Test] - public void TestNullImplicitConversion() + public void TestNoImplicitConversion() { checkNull(null); checkNull(default); From a6838a3f9fd7a795241fe5adbdcd582f172c2e43 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 13:17:09 +0200 Subject: [PATCH 10/14] Use Utf8String directly in `fixed` statement --- SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs | 5 +---- SDL3-CS.SourceGeneration/Helper.cs | 2 -- SDL3-CS/Utf8String.cs | 2 ++ 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs b/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs index c8a2935..263488c 100644 --- a/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs +++ b/SDL3-CS.SourceGeneration/FriendlyOverloadGenerator.cs @@ -112,10 +112,7 @@ private static StatementSyntax makeMethodBody(GeneratedMethod gm) param.Identifier.ValueText + pointer_suffix) .WithInitializer( SyntaxFactory.EqualsValueClause( - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName(param.Identifier), - SyntaxFactory.IdentifierName(Helper.Utf8StringReadOnlySpanFieldName)))))), + SyntaxFactory.IdentifierName(param.Identifier))))), expr); } diff --git a/SDL3-CS.SourceGeneration/Helper.cs b/SDL3-CS.SourceGeneration/Helper.cs index 65d84f9..42e51d5 100644 --- a/SDL3-CS.SourceGeneration/Helper.cs +++ b/SDL3-CS.SourceGeneration/Helper.cs @@ -18,8 +18,6 @@ public static class Helper 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); diff --git a/SDL3-CS/Utf8String.cs b/SDL3-CS/Utf8String.cs index 56b8d74..253ee87 100644 --- a/SDL3-CS/Utf8String.cs +++ b/SDL3-CS/Utf8String.cs @@ -48,5 +48,7 @@ public static implicit operator Utf8String(ReadOnlySpan raw) return new Utf8String(raw); } + + internal ref readonly byte GetPinnableReference() => ref Raw.GetPinnableReference(); } } From 42b32e36d506fb1a3acabe18c5ead138c8ac07bf Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 13:22:22 +0200 Subject: [PATCH 11/14] Make `Utf8String.Raw` internal --- SDL3-CS/Properties/AssemblyInfo.cs | 6 ++++++ SDL3-CS/Utf8String.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 SDL3-CS/Properties/AssemblyInfo.cs diff --git a/SDL3-CS/Properties/AssemblyInfo.cs b/SDL3-CS/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4fabb01 --- /dev/null +++ b/SDL3-CS/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("SDL3-CS.Tests")] diff --git a/SDL3-CS/Utf8String.cs b/SDL3-CS/Utf8String.cs index 253ee87..d50fc8c 100644 --- a/SDL3-CS/Utf8String.cs +++ b/SDL3-CS/Utf8String.cs @@ -13,7 +13,7 @@ namespace SDL /// Should only be instantiated through implicit conversions. public readonly ref struct Utf8String { - public readonly ReadOnlySpan Raw; + internal readonly ReadOnlySpan Raw; private Utf8String(ReadOnlySpan raw) { From 13b2c6ebbaa572f8aef7e120f4853d1cdcd504cc Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 13:23:32 +0200 Subject: [PATCH 12/14] Update tests --- SDL3-CS.Tests/TestUtf8String.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SDL3-CS.Tests/TestUtf8String.cs b/SDL3-CS.Tests/TestUtf8String.cs index 93c9afc..b879568 100644 --- a/SDL3-CS.Tests/TestUtf8String.cs +++ b/SDL3-CS.Tests/TestUtf8String.cs @@ -69,7 +69,7 @@ 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) + fixed (byte* ptr = s) { Assert.That(ptr == null, "ptr == null"); } @@ -79,7 +79,7 @@ private static unsafe void check(Utf8String s, int expectedLength) { Assert.That(s.Raw.Length, Is.EqualTo(expectedLength)); - fixed (byte* ptr = s.Raw) + fixed (byte* ptr = s) { Assert.That(ptr != null, "ptr != null"); Assert.That(ptr[s.Raw.Length - 1], Is.EqualTo(0)); From 8f5e52d9445de50012e7cf196a882ebd57a99789 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 13:30:41 +0200 Subject: [PATCH 13/14] Update old xmldoc --- SDL3-CS.SourceGeneration/Changes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDL3-CS.SourceGeneration/Changes.cs b/SDL3-CS.SourceGeneration/Changes.cs index e564da3..ce40fa3 100644 --- a/SDL3-CS.SourceGeneration/Changes.cs +++ b/SDL3-CS.SourceGeneration/Changes.cs @@ -11,7 +11,7 @@ public enum Changes None, /// - /// Change const char* function parameters to SDLUtf8String. + /// Change const char* function parameters to . /// ChangeParamsToUtf8String = 1 << 0, From c40e8b150c89d9907192118ee3ab2d080e5665c7 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 17 Apr 2024 13:37:11 +0200 Subject: [PATCH 14/14] Inline `ensureTrailingNull()` --- SDL3-CS/Utf8String.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/SDL3-CS/Utf8String.cs b/SDL3-CS/Utf8String.cs index d50fc8c..832b44f 100644 --- a/SDL3-CS/Utf8String.cs +++ b/SDL3-CS/Utf8String.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Runtime.CompilerServices; using System.Text; namespace SDL @@ -10,7 +9,7 @@ namespace SDL /// /// Null pointer or a null-byte terminated UTF8 string suitable for use in native methods. /// - /// Should only be instantiated through implicit conversions. + /// Should only be instantiated through implicit conversions or with null. public readonly ref struct Utf8String { internal readonly ReadOnlySpan Raw; @@ -25,11 +24,11 @@ public static implicit operator Utf8String(string? str) if (str == null) return new Utf8String(null); - return new Utf8String(Encoding.UTF8.GetBytes(ensureTrailingNull(str))); - } + if (str.EndsWith('\0')) + return new Utf8String(Encoding.UTF8.GetBytes(str)); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string ensureTrailingNull(string str) => str.EndsWith('\0') ? str : str + '\0'; + return new Utf8String(Encoding.UTF8.GetBytes(str + '\0')); + } public static implicit operator Utf8String(ReadOnlySpan raw) {