Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cesium.Runtime.Tests/Cesium.Runtime.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks Condition=" $([MSBuild]::IsOsPlatform('Windows')) ">$(TargetFrameworks);net48</TargetFrameworks>
<!-- <TargetFrameworks Condition=" $([MSBuild]::IsOsPlatform('Windows')) ">$(TargetFrameworks);net48</TargetFrameworks> -->
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some thought, I still want us to support .NET Framework on Windows. Let's re-enable that here, and add a fallback implementation for .NET Standard.

<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
71 changes: 71 additions & 0 deletions Cesium.Runtime.Tests/StringFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Text;

namespace Cesium.Runtime.Tests;

public unsafe class StringFunctionTests
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some Unicode tests, shall we?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will do

{
[Theory]
[InlineData("Hello\0", 5)]
[InlineData("Goodbye\0", 7)]
[InlineData("Hello\0Goodbye\0", 5)]
[InlineData(" \0", 18)]
public void StrLen(string input, int expected)
{
// TODO: If you are rich enough to procure a 2-4+ GB RAM runner,
// please update this test to exercise the path where the string
// length exceeds int.MaxLength of bytes.
var bytes = Encoding.UTF8.GetBytes(input);
fixed (byte* str = bytes)
{
var actual = StringFunctions.StrLen(str);

Assert.Equal((nuint)expected, actual);
}
}

[Fact]
public void StrLen_Null()
{
var actual = StringFunctions.StrLen(null);

Assert.Equal((nuint)0, actual);
}

[Theory]
[InlineData("Hello\n", 5)]
[InlineData("Goodbye\n", 7)]
[InlineData("Hello\nGoodbye\n", 5)]
[InlineData(" \n", 18)]
public void StrChr(string input, int expectedOffset)
{
var needle = '\n';
var bytes = Encoding.UTF8.GetBytes(input);
fixed (byte* str = bytes)
{
var ptr = StringFunctions.StrChr(str, '\n');

Assert.Equal((byte)needle, *ptr);
Assert.Equal(expectedOffset, (int)(ptr - str));
}
}

[Fact]
public void StrChr_NotFound()
{
var bytes = Encoding.UTF8.GetBytes("Hello\0");
fixed (byte* str = bytes)
{
var actual = StringFunctions.StrChr(str, '\n');

Assert.True(actual is null);
}
}

[Fact]
public void StrChr_Null()
{
var actual = StringFunctions.StrChr(null, '\0');

Assert.True(actual is null);
}
}
2 changes: 1 addition & 1 deletion Cesium.Runtime/Cesium.Runtime.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
54 changes: 23 additions & 31 deletions Cesium.Runtime/StringFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
#if NETSTANDARD
using System.Text;
#else
using System.Collections.Specialized;
using System.Runtime.InteropServices;
#endif

namespace Cesium.Runtime;

/// <summary>
Expand All @@ -14,26 +7,22 @@ public unsafe static class StringFunctions
{
public static nuint StrLen(byte* str)
{
#if NETSTANDARD
if (str == null)
if (str != null)
{
return 0;
}
int match;
nuint offset = 0;

Encoding encoding = Encoding.UTF8;
int byteLength = 0;
byte* search = str;
while (*search != '\0')
{
byteLength++;
search++;
// TODO: Copy IndexOfNullByte impl. from CoreLib in a distant future
while ((match = new Span<byte>(str + offset, int.MaxValue)
.IndexOf((byte)0)) < 0)
{
offset += int.MaxValue;
}

return offset + (uint)match;
}

int stringLength = encoding.GetCharCount(str, byteLength);
return (uint)stringLength;
#else
return (uint)(Marshal.PtrToStringUTF8((nint)str)?.Length ?? 0);
#endif
return 0;
}
public static byte* StrCpy(byte* dest, byte* src)
{
Expand Down Expand Up @@ -186,19 +175,22 @@ public static int StrNCmp(byte* lhs, byte* rhs, nuint count)
}
public static byte* StrChr(byte* str, int ch)
{
if (str == null)
if (str != null)
{
return null;
}
int match;
byte c = (byte)ch;

while (*str != 0)
{
if (*str == ch)
while ((match = new Span<byte>(str, int.MaxValue)
.IndexOfAny<byte>(c, 0)) < 0)
{
return str;
str += int.MaxValue;
}
str += (nint)(uint)match;

str++;
if (*str == c)
{
return str;
}
}

return null;
Expand Down