From 9ccb481dfd1dffb3a61a0b4c4047b762cc3d1694 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2019 00:36:36 +0000 Subject: [PATCH] [master] Update dependencies from dotnet/coreclr (dotnet/corefx#41280) * Add unit tests for Utf8Span, react to Utf8String changes * Regenerating the reference sources for System.Runtime.Intrinsics.Experimental * Update dependencies from https://github.com/dotnet/coreclr build 20191001.3 - Microsoft.NET.Sdk.IL - 5.0.0-alpha1.19501.3 - Microsoft.NETCore.ILAsm - 5.0.0-alpha1.19501.3 - Microsoft.NETCore.Runtime.CoreCLR - 5.0.0-alpha1.19501.3 * Move corefx alpine build containers to 3.9 Commit migrated from https://github.com/dotnet/corefx/commit/c8c2917540a568869ba761afa6e4dba6edeb433b --- eng/pipelines/libraries/.azure-ci.yml | 2 +- eng/pipelines/libraries/outerloop.yml | 2 +- .../ref/System.Runtime.Intrinsics.Experimental.cs | 248 ++++----- .../src/ApiCompatBaseline.txt | 3 + .../src/MatchingRefApiCompatBaseline.txt | 4 + .../ref/System.Utf8String.Experimental.cs | 224 +++++++- .../ref/System.Utf8String.Experimental.csproj | 4 + .../src/System/IO/Utf8StringStream.cs | 12 +- .../src/System/Net/Http/Utf8StringContent.cs | 2 +- .../System.Utf8String.Experimental.Tests.csproj | 15 +- .../tests/System/BoundedUtf8Span.cs | 37 ++ .../tests/System/RangeEqualityComparer.cs | 38 ++ .../tests/System/Utf8ExtensionsTests.cs | 16 +- .../tests/System/Utf8SpanTests.Comparison.cs | 146 ++++++ .../tests/System/Utf8SpanTests.Conversion.cs | 242 +++++++++ .../tests/System/Utf8SpanTests.Ctor.cs | 135 +++++ .../tests/System/Utf8SpanTests.Enumeration.cs | 82 +++ .../System/Utf8SpanTests.Manipulation.TestData.cs | 130 +++++ .../tests/System/Utf8SpanTests.Manipulation.cs | 329 ++++++++++++ .../System/Utf8SpanTests.Searching.TestData.cs | 576 +++++++++++++++++++++ .../tests/System/Utf8SpanTests.Searching.cs | 416 +++++++++++++++ .../tests/System/Utf8SpanTests.TestData.cs | 178 +++++++ .../tests/System/Utf8SpanTests.cs | 353 +++++++++++++ .../tests/System/Utf8TestUtilities.cs | 137 ++++- 24 files changed, 3124 insertions(+), 207 deletions(-) create mode 100644 src/libraries/System.Runtime.Intrinsics.Experimental/src/ApiCompatBaseline.txt create mode 100644 src/libraries/System.Runtime.Intrinsics.Experimental/src/MatchingRefApiCompatBaseline.txt create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/BoundedUtf8Span.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/RangeEqualityComparer.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Conversion.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Ctor.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Enumeration.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.TestData.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.TestData.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.TestData.cs create mode 100644 src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs diff --git a/eng/pipelines/libraries/.azure-ci.yml b/eng/pipelines/libraries/.azure-ci.yml index e8cbd857..bf34c13 100644 --- a/eng/pipelines/libraries/.azure-ci.yml +++ b/eng/pipelines/libraries/.azure-ci.yml @@ -32,7 +32,7 @@ resources: image: microsoft/dotnet-buildtools-prereqs:centos-6-376e1a3-20174311014331 - container: alpine_36_container - image: microsoft/dotnet-buildtools-prereqs:alpine-3.6-WithNode-f4d3fe3-20181213005010 + image: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.9-WithNode-0fc54a3-20190918214015 - container: alpine_37_arm64_container image: microsoft/dotnet-buildtools-prereqs:ubuntu-16.04-cross-arm64-alpine10fcdcf-20190208200917 diff --git a/eng/pipelines/libraries/outerloop.yml b/eng/pipelines/libraries/outerloop.yml index 2d5ffd6..00c22ed 100644 --- a/eng/pipelines/libraries/outerloop.yml +++ b/eng/pipelines/libraries/outerloop.yml @@ -17,7 +17,7 @@ resources: image: microsoft/dotnet-buildtools-prereqs:centos-6-376e1a3-20174311014331 - container: alpine_36_container - image: microsoft/dotnet-buildtools-prereqs:alpine-3.6-WithNode-f4d3fe3-20181213005010 + image: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.9-WithNode-0fc54a3-20190918214015 - container: alpine_37_arm64_container image: microsoft/dotnet-buildtools-prereqs:ubuntu-16.04-cross-arm64-alpine10fcdcf-20190208200917 diff --git a/src/libraries/System.Runtime.Intrinsics.Experimental/ref/System.Runtime.Intrinsics.Experimental.cs b/src/libraries/System.Runtime.Intrinsics.Experimental/ref/System.Runtime.Intrinsics.Experimental.cs index 4210250..4a2ed82 100644 --- a/src/libraries/System.Runtime.Intrinsics.Experimental/ref/System.Runtime.Intrinsics.Experimental.cs +++ b/src/libraries/System.Runtime.Intrinsics.Experimental/ref/System.Runtime.Intrinsics.Experimental.cs @@ -5,186 +5,112 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ -namespace System.Runtime.Intrinsics.Arm.Arm64 +namespace System.Runtime.Intrinsics.Arm { [System.CLSCompliantAttribute(false)] - public static partial class Aes + public abstract partial class AdvSimd : System.Runtime.Intrinsics.Arm.ArmBase { - public static bool IsSupported { get { throw null; } } + internal AdvSimd() { } + public static new bool IsSupported { get { throw null; } } + public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } + public static System.Runtime.Intrinsics.Vector64 AbsScalar(System.Runtime.Intrinsics.Vector64 value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 AddScalar(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(byte* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(double* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(short* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(int* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(long* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(sbyte* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(float* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(ushort* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(uint* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector128 LoadVector128(ulong* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(byte* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(short* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(int* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(sbyte* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(float* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(ushort* address) { throw null; } + public unsafe static System.Runtime.Intrinsics.Vector64 LoadVector64(uint* address) { throw null; } + public new abstract partial class Arm64 : System.Runtime.Intrinsics.Arm.ArmBase.Arm64 + { + internal Arm64() { } + public static new bool IsSupported { get { throw null; } } + public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + } + } + [System.CLSCompliantAttribute(false)] + public abstract partial class Aes : System.Runtime.Intrinsics.Arm.ArmBase + { + internal Aes() { } + public static new bool IsSupported { get { throw null; } } public static System.Runtime.Intrinsics.Vector128 Decrypt(System.Runtime.Intrinsics.Vector128 value, System.Runtime.Intrinsics.Vector128 roundKey) { throw null; } public static System.Runtime.Intrinsics.Vector128 Encrypt(System.Runtime.Intrinsics.Vector128 value, System.Runtime.Intrinsics.Vector128 roundKey) { throw null; } public static System.Runtime.Intrinsics.Vector128 InverseMixColumns(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static System.Runtime.Intrinsics.Vector128 MixColumns(System.Runtime.Intrinsics.Vector128 value) { throw null; } } [System.CLSCompliantAttribute(false)] - public static partial class Base + public abstract partial class ArmBase { + internal ArmBase() { } public static bool IsSupported { get { throw null; } } - public static int LeadingSignCount(int value) { throw null; } - public static int LeadingSignCount(long value) { throw null; } public static int LeadingZeroCount(int value) { throw null; } - public static int LeadingZeroCount(long value) { throw null; } public static int LeadingZeroCount(uint value) { throw null; } - public static int LeadingZeroCount(ulong value) { throw null; } + public abstract partial class Arm64 + { + internal Arm64() { } + public static bool IsSupported { get { throw null; } } + public static int LeadingSignCount(int value) { throw null; } + public static int LeadingSignCount(long value) { throw null; } + public static int LeadingZeroCount(long value) { throw null; } + public static int LeadingZeroCount(ulong value) { throw null; } + } } [System.CLSCompliantAttribute(false)] - public static partial class Sha1 + public abstract partial class Sha1 : System.Runtime.Intrinsics.Arm.ArmBase { - public static bool IsSupported { get { throw null; } } + internal Sha1() { } + public static new bool IsSupported { get { throw null; } } public static uint FixedRotate(uint hash_e) { throw null; } - public static System.Runtime.Intrinsics.Vector128 HashChoose(System.Runtime.Intrinsics.Vector128 hash_abcd, uint hash_e, System.Runtime.Intrinsics.Vector128 wk) { throw null; } - public static System.Runtime.Intrinsics.Vector128 HashMajority(System.Runtime.Intrinsics.Vector128 hash_abcd, uint hash_e, System.Runtime.Intrinsics.Vector128 wk) { throw null; } - public static System.Runtime.Intrinsics.Vector128 HashParity(System.Runtime.Intrinsics.Vector128 hash_abcd, uint hash_e, System.Runtime.Intrinsics.Vector128 wk) { throw null; } - public static System.Runtime.Intrinsics.Vector128 SchedulePart1(System.Runtime.Intrinsics.Vector128 w0_3, System.Runtime.Intrinsics.Vector128 w4_7, System.Runtime.Intrinsics.Vector128 w8_11) { throw null; } - public static System.Runtime.Intrinsics.Vector128 SchedulePart2(System.Runtime.Intrinsics.Vector128 tw0_3, System.Runtime.Intrinsics.Vector128 w12_15) { throw null; } + public static System.Runtime.Intrinsics.Vector128 HashUpdateChoose(System.Runtime.Intrinsics.Vector128 hash_abcd, uint hash_e, System.Runtime.Intrinsics.Vector128 wk) { throw null; } + public static System.Runtime.Intrinsics.Vector128 HashUpdateMajority(System.Runtime.Intrinsics.Vector128 hash_abcd, uint hash_e, System.Runtime.Intrinsics.Vector128 wk) { throw null; } + public static System.Runtime.Intrinsics.Vector128 HashUpdateParity(System.Runtime.Intrinsics.Vector128 hash_abcd, uint hash_e, System.Runtime.Intrinsics.Vector128 wk) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ScheduleUpdate0(System.Runtime.Intrinsics.Vector128 w0_3, System.Runtime.Intrinsics.Vector128 w4_7, System.Runtime.Intrinsics.Vector128 w8_11) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ScheduleUpdate1(System.Runtime.Intrinsics.Vector128 tw0_3, System.Runtime.Intrinsics.Vector128 w12_15) { throw null; } } [System.CLSCompliantAttribute(false)] - public static partial class Sha256 + public abstract partial class Sha256 : System.Runtime.Intrinsics.Arm.ArmBase { - public static bool IsSupported { get { throw null; } } - public static System.Runtime.Intrinsics.Vector128 HashLower(System.Runtime.Intrinsics.Vector128 hash_abcd, System.Runtime.Intrinsics.Vector128 hash_efgh, System.Runtime.Intrinsics.Vector128 wk) { throw null; } - public static System.Runtime.Intrinsics.Vector128 HashUpper(System.Runtime.Intrinsics.Vector128 hash_efgh, System.Runtime.Intrinsics.Vector128 hash_abcd, System.Runtime.Intrinsics.Vector128 wk) { throw null; } - public static System.Runtime.Intrinsics.Vector128 SchedulePart1(System.Runtime.Intrinsics.Vector128 w0_3, System.Runtime.Intrinsics.Vector128 w4_7) { throw null; } - public static System.Runtime.Intrinsics.Vector128 SchedulePart2(System.Runtime.Intrinsics.Vector128 w0_3, System.Runtime.Intrinsics.Vector128 w8_11, System.Runtime.Intrinsics.Vector128 w12_15) { throw null; } - } - [System.CLSCompliantAttribute(false)] - public static partial class Simd - { - public static bool IsSupported { get { throw null; } } - public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 AndNot(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 AndNot(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 And(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 And(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 BitwiseSelect(System.Runtime.Intrinsics.Vector128 sel, System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 BitwiseSelect(System.Runtime.Intrinsics.Vector64 sel, System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareEqualZero(System.Runtime.Intrinsics.Vector128 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareEqualZero(System.Runtime.Intrinsics.Vector64 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareEqual(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareEqual(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareGreaterThanOrEqualZero(System.Runtime.Intrinsics.Vector128 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareGreaterThanOrEqualZero(System.Runtime.Intrinsics.Vector64 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareGreaterThanOrEqual(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareGreaterThanOrEqual(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareGreaterThanZero(System.Runtime.Intrinsics.Vector128 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareGreaterThanZero(System.Runtime.Intrinsics.Vector64 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareGreaterThan(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareGreaterThan(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareLessThanOrEqualZero(System.Runtime.Intrinsics.Vector128 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareLessThanOrEqualZero(System.Runtime.Intrinsics.Vector64 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareLessThanZero(System.Runtime.Intrinsics.Vector128 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareLessThanZero(System.Runtime.Intrinsics.Vector64 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 CompareTest(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 CompareTest(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 Divide(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Divide(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Divide(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static T Extract(System.Runtime.Intrinsics.Vector128 vector, byte index) where T : struct { throw null; } - public static T Extract(System.Runtime.Intrinsics.Vector64 vector, byte index) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 Insert(System.Runtime.Intrinsics.Vector128 vector, byte index, T data) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 Insert(System.Runtime.Intrinsics.Vector64 vector, byte index, T data) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingSignCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingSignCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingSignCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingSignCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingSignCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingSignCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingZeroCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingZeroCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingZeroCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingZeroCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingZeroCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 LeadingZeroCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingZeroCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingZeroCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingZeroCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingZeroCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingZeroCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 LeadingZeroCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Min(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Min(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Multiply(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Multiply(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Negate(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Negate(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Negate(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Negate(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Not(System.Runtime.Intrinsics.Vector128 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 Not(System.Runtime.Intrinsics.Vector64 value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 OrNot(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 OrNot(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 Or(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 Or(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 PopCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 PopCount(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 PopCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 PopCount(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 SetAllVector128(T value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 SetAllVector64(T value) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 Sqrt(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Sqrt(System.Runtime.Intrinsics.Vector128 value) { throw null; } - public static System.Runtime.Intrinsics.Vector64 Sqrt(System.Runtime.Intrinsics.Vector64 value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 Subtract(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 Subtract(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector128 Xor(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) where T : struct { throw null; } - public static System.Runtime.Intrinsics.Vector64 Xor(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) where T : struct { throw null; } + internal Sha256() { } + public static new bool IsSupported { get { throw null; } } + public static System.Runtime.Intrinsics.Vector128 HashUpdate1(System.Runtime.Intrinsics.Vector128 hash_abcd, System.Runtime.Intrinsics.Vector128 hash_efgh, System.Runtime.Intrinsics.Vector128 wk) { throw null; } + public static System.Runtime.Intrinsics.Vector128 HashUpdate2(System.Runtime.Intrinsics.Vector128 hash_efgh, System.Runtime.Intrinsics.Vector128 hash_abcd, System.Runtime.Intrinsics.Vector128 wk) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ScheduleUpdate0(System.Runtime.Intrinsics.Vector128 w0_3, System.Runtime.Intrinsics.Vector128 w4_7) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ScheduleUpdate1(System.Runtime.Intrinsics.Vector128 w0_3, System.Runtime.Intrinsics.Vector128 w8_11, System.Runtime.Intrinsics.Vector128 w12_15) { throw null; } } } diff --git a/src/libraries/System.Runtime.Intrinsics.Experimental/src/ApiCompatBaseline.txt b/src/libraries/System.Runtime.Intrinsics.Experimental/src/ApiCompatBaseline.txt new file mode 100644 index 0000000..55dbd30 --- /dev/null +++ b/src/libraries/System.Runtime.Intrinsics.Experimental/src/ApiCompatBaseline.txt @@ -0,0 +1,3 @@ +Compat issues with assembly System.Runtime.Intrinsics.Experimental: +CannotSealType : Type 'System.Runtime.Intrinsics.Arm.AdvSimd.Arm64' is effectively (has a private constructor) sealed in the reference but not sealed in the implementation. +Total Issues: 1 diff --git a/src/libraries/System.Runtime.Intrinsics.Experimental/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.Runtime.Intrinsics.Experimental/src/MatchingRefApiCompatBaseline.txt new file mode 100644 index 0000000..16c4416 --- /dev/null +++ b/src/libraries/System.Runtime.Intrinsics.Experimental/src/MatchingRefApiCompatBaseline.txt @@ -0,0 +1,4 @@ +Compat issues with assembly System.Runtime.Intrinsics.Experimental: +CannotSealType : Type 'System.Runtime.Intrinsics.Arm.AdvSimd.Arm64' is effectively (has a private constructor) sealed in the reference but not sealed in the implementation. +MembersMustExist : Member 'System.Runtime.Intrinsics.Arm.AdvSimd.Arm64..ctor()' does not exist in the reference but it does exist in the implementation. +Total Issues: 2 diff --git a/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.cs b/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.cs index c48f100..75f2850 100644 --- a/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.cs +++ b/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.cs @@ -12,37 +12,37 @@ namespace System private readonly int _dummyPrimitive; public int CompareTo(System.Char8 other) { throw null; } public bool Equals(System.Char8 other) { throw null; } - public override bool Equals(object obj) { throw null; } + public override bool Equals(object? obj) { throw null; } public override int GetHashCode() { throw null; } public static bool operator ==(System.Char8 left, System.Char8 right) { throw null; } - public static explicit operator System.Char8 (char value) { throw null; } - public static explicit operator char (System.Char8 value) { throw null; } + public static explicit operator System.Char8(char value) { throw null; } + public static explicit operator char(System.Char8 value) { throw null; } [System.CLSCompliantAttribute(false)] - public static explicit operator sbyte (System.Char8 value) { throw null; } - public static explicit operator System.Char8 (short value) { throw null; } - public static explicit operator System.Char8 (int value) { throw null; } - public static explicit operator System.Char8 (long value) { throw null; } + public static explicit operator sbyte(System.Char8 value) { throw null; } + public static explicit operator System.Char8(short value) { throw null; } + public static explicit operator System.Char8(int value) { throw null; } + public static explicit operator System.Char8(long value) { throw null; } [System.CLSCompliantAttribute(false)] - public static explicit operator System.Char8 (sbyte value) { throw null; } + public static explicit operator System.Char8(sbyte value) { throw null; } [System.CLSCompliantAttribute(false)] - public static explicit operator System.Char8 (ushort value) { throw null; } + public static explicit operator System.Char8(ushort value) { throw null; } [System.CLSCompliantAttribute(false)] - public static explicit operator System.Char8 (uint value) { throw null; } + public static explicit operator System.Char8(uint value) { throw null; } [System.CLSCompliantAttribute(false)] - public static explicit operator System.Char8 (ulong value) { throw null; } + public static explicit operator System.Char8(ulong value) { throw null; } public static bool operator >(System.Char8 left, System.Char8 right) { throw null; } public static bool operator >=(System.Char8 left, System.Char8 right) { throw null; } - public static implicit operator System.Char8 (byte value) { throw null; } - public static implicit operator byte (System.Char8 value) { throw null; } - public static implicit operator short (System.Char8 value) { throw null; } - public static implicit operator int (System.Char8 value) { throw null; } - public static implicit operator long (System.Char8 value) { throw null; } + public static implicit operator System.Char8(byte value) { throw null; } + public static implicit operator byte(System.Char8 value) { throw null; } + public static implicit operator short(System.Char8 value) { throw null; } + public static implicit operator int(System.Char8 value) { throw null; } + public static implicit operator long(System.Char8 value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator ushort (System.Char8 value) { throw null; } + public static implicit operator ushort(System.Char8 value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator uint (System.Char8 value) { throw null; } + public static implicit operator uint(System.Char8 value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator ulong (System.Char8 value) { throw null; } + public static implicit operator ulong(System.Char8 value) { throw null; } public static bool operator !=(System.Char8 left, System.Char8 right) { throw null; } public static bool operator <(System.Char8 left, System.Char8 right) { throw null; } public static bool operator <=(System.Char8 left, System.Char8 right) { throw null; } @@ -64,9 +64,10 @@ namespace System public static System.ReadOnlyMemory AsMemoryBytes(this System.Utf8String text, int start) { throw null; } public static System.ReadOnlyMemory AsMemoryBytes(this System.Utf8String text, int start, int length) { throw null; } public static System.ReadOnlyMemory AsMemoryBytes(this System.Utf8String text, System.Range range) { throw null; } - public static System.ReadOnlySpan AsSpan(this System.Utf8String text) { throw null; } - public static System.ReadOnlySpan AsSpan(this System.Utf8String text, int start) { throw null; } - public static System.ReadOnlySpan AsSpan(this System.Utf8String text, int start, int length) { throw null; } + public static System.Text.Utf8Span AsSpan(this System.Utf8String? text) { throw null; } + public static System.Text.Utf8Span AsSpan(this System.Utf8String? text, int start) { throw null; } + public static System.Text.Utf8Span AsSpan(this System.Utf8String? text, int start, int length) { throw null; } + public static System.Utf8String ToUtf8String(this System.Text.Rune rune) { throw null; } } public sealed partial class Utf8String : System.IEquatable { @@ -82,11 +83,14 @@ namespace System public Utf8String(string value) { } public System.Char8 this[int index] { get { throw null; } } public int Length { get { throw null; } } + public static bool AreEquivalent(System.Utf8String? utf8Text, string? utf16Text) { throw null; } + public static bool AreEquivalent(System.Text.Utf8Span utf8Text, System.ReadOnlySpan utf16Text) { throw null; } + public static bool AreEquivalent(System.ReadOnlySpan utf8Text, System.ReadOnlySpan utf16Text) { throw null; } public bool Contains(char value) { throw null; } public bool Contains(System.Text.Rune value) { throw null; } public bool EndsWith(char value) { throw null; } public bool EndsWith(System.Text.Rune value) { throw null; } - public override bool Equals(object obj) { throw null; } + public override bool Equals(object? obj) { throw null; } public bool Equals(System.Utf8String value) { throw null; } public static bool Equals(System.Utf8String left, System.Utf8String right) { throw null; } public override int GetHashCode() { throw null; } @@ -96,8 +100,9 @@ namespace System public int IndexOf(System.Text.Rune value) { throw null; } public static bool IsNullOrEmpty(System.Utf8String value) { throw null; } public static bool operator ==(System.Utf8String left, System.Utf8String right) { throw null; } - public static explicit operator System.ReadOnlySpan (System.Utf8String value) { throw null; } - public static implicit operator System.ReadOnlySpan (System.Utf8String value) { throw null; } + public static explicit operator System.ReadOnlySpan(System.Utf8String value) { throw null; } + public static implicit operator System.ReadOnlySpan(System.Utf8String value) { throw null; } + public static implicit operator System.Text.Utf8Span(System.Utf8String value) { throw null; } public static bool operator !=(System.Utf8String left, System.Utf8String right) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public System.Utf8String Slice(int startIndex, int length) { throw null; } @@ -109,15 +114,180 @@ namespace System public byte[] ToByteArray(int startIndex, int length) { throw null; } public override string ToString() { throw null; } } + [System.FlagsAttribute] + public enum Utf8StringSplitOptions + { + None = 0, + RemoveEmptyEntries = 1, + TrimEntries = 2 + } } namespace System.Net.Http { public sealed partial class Utf8StringContent : System.Net.Http.HttpContent { public Utf8StringContent(System.Utf8String content) { } - public Utf8StringContent(System.Utf8String content, string mediaType) { } + public Utf8StringContent(System.Utf8String content, string? mediaType) { } protected override System.Threading.Tasks.Task CreateContentReadStreamAsync() { throw null; } - protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context) { throw null; } + protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context) { throw null; } protected override bool TryComputeLength(out long length) { throw null; } } } +namespace System.Text +{ + public readonly ref partial struct Utf8Span + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public Utf8Span(System.Utf8String? value) { throw null; } + public System.ReadOnlySpan Bytes { get { throw null; } } + public CharEnumerable Chars { get { throw null; } } + public static System.Text.Utf8Span Empty { get { throw null; } } + public bool IsEmpty { get { throw null; } } + public RuneEnumerable Runes { get { throw null; } } + public int CompareTo(System.Text.Utf8Span other) { throw null; } + public int CompareTo(System.Text.Utf8Span other, System.StringComparison comparison) { throw null; } + public bool Contains(char value) { throw null; } + public bool Contains(char value, System.StringComparison comparison) { throw null; } + public bool Contains(System.Text.Rune value) { throw null; } + public bool Contains(System.Text.Rune value, System.StringComparison comparison) { throw null; } + public bool Contains(System.Text.Utf8Span value) { throw null; } + public bool Contains(System.Text.Utf8Span value, System.StringComparison comparison) { throw null; } + public bool EndsWith(char value) { throw null; } + public bool EndsWith(char value, System.StringComparison comparison) { throw null; } + public bool EndsWith(System.Text.Rune value) { throw null; } + public bool EndsWith(System.Text.Rune value, System.StringComparison comparison) { throw null; } + public bool EndsWith(System.Text.Utf8Span value) { throw null; } + public bool EndsWith(System.Text.Utf8Span value, System.StringComparison comparison) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("Equals(object) on Utf8Span will always throw an exception. Use Equals(Utf8Span) or == instead.")] + public override bool Equals(object? obj) { throw null; } + public bool Equals(System.Text.Utf8Span other) { throw null; } + public bool Equals(System.Text.Utf8Span other, System.StringComparison comparison) { throw null; } + public static bool Equals(System.Text.Utf8Span left, System.Text.Utf8Span right) { throw null; } + public static bool Equals(System.Text.Utf8Span left, System.Text.Utf8Span right, System.StringComparison comparison) { throw null; } + public override int GetHashCode() { throw null; } + public int GetHashCode(System.StringComparison comparison) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public ref readonly byte GetPinnableReference() { throw null; } + public bool IsAscii() { throw null; } + public bool IsEmptyOrWhiteSpace() { throw null; } + public bool IsNormalized(System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; } + public System.Utf8String Normalize(System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; } + public int Normalize(System.Span destination, System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; } + public static bool operator !=(System.Text.Utf8Span left, System.Text.Utf8Span right) { throw null; } + public static bool operator ==(System.Text.Utf8Span left, System.Text.Utf8Span right) { throw null; } + public System.Text.Utf8Span this[System.Range range] { get { throw null; } } + public SplitResult Split(char separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; } + public SplitResult Split(System.Text.Rune separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; } + public SplitResult Split(System.Text.Utf8Span separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; } + public SplitOnResult SplitOn(char separator) { throw null; } + public SplitOnResult SplitOn(char separator, System.StringComparison comparisonType) { throw null; } + public SplitOnResult SplitOn(System.Text.Rune separator) { throw null; } + public SplitOnResult SplitOn(System.Text.Rune separator, System.StringComparison comparisonType) { throw null; } + public SplitOnResult SplitOn(System.Text.Utf8Span separator) { throw null; } + public SplitOnResult SplitOn(System.Text.Utf8Span separator, System.StringComparison comparisonType) { throw null; } + public SplitOnResult SplitOnLast(char separator) { throw null; } + public SplitOnResult SplitOnLast(char separator, System.StringComparison comparisonType) { throw null; } + public SplitOnResult SplitOnLast(System.Text.Rune separator) { throw null; } + public SplitOnResult SplitOnLast(System.Text.Rune separator, System.StringComparison comparisonType) { throw null; } + public SplitOnResult SplitOnLast(System.Text.Utf8Span separator) { throw null; } + public SplitOnResult SplitOnLast(System.Text.Utf8Span separator, System.StringComparison comparisonType) { throw null; } + public bool StartsWith(char value) { throw null; } + public bool StartsWith(char value, System.StringComparison comparison) { throw null; } + public bool StartsWith(System.Text.Rune value) { throw null; } + public bool StartsWith(System.Text.Rune value, System.StringComparison comparison) { throw null; } + public bool StartsWith(System.Text.Utf8Span value) { throw null; } + public bool StartsWith(System.Text.Utf8Span value, System.StringComparison comparison) { throw null; } + public System.Text.Utf8Span Trim() { throw null; } + public System.Text.Utf8Span TrimEnd() { throw null; } + public System.Text.Utf8Span TrimStart() { throw null; } + public char[] ToCharArray() { throw null; } + public int ToChars(System.Span destination) { throw null; } + public System.Utf8String ToLower(System.Globalization.CultureInfo culture) { throw null; } + public int ToLower(System.Span destination, System.Globalization.CultureInfo culture) { throw null; } + public System.Utf8String ToLowerInvariant() { throw null; } + public int ToLowerInvariant(System.Span destination) { throw null; } + public override string ToString() { throw null; } + public System.Utf8String ToUpper(System.Globalization.CultureInfo culture) { throw null; } + public int ToUpper(System.Span destination, System.Globalization.CultureInfo culture) { throw null; } + public System.Utf8String ToUpperInvariant() { throw null; } + public int ToUpperInvariant(System.Span destination) { throw null; } + public System.Utf8String ToUtf8String() { throw null; } + public bool TryFind(char value, out System.Range range) { throw null; } + public bool TryFind(char value, System.StringComparison comparisonType, out System.Range range) { throw null; } + public bool TryFind(System.Text.Rune value, out System.Range range) { throw null; } + public bool TryFind(System.Text.Rune value, System.StringComparison comparisonType, out System.Range range) { throw null; } + public bool TryFind(System.Text.Utf8Span value, out System.Range range) { throw null; } + public bool TryFind(System.Text.Utf8Span value, System.StringComparison comparisonType, out System.Range range) { throw null; } + public bool TryFindLast(char value, out System.Range range) { throw null; } + public bool TryFindLast(char value, System.StringComparison comparisonType, out System.Range range) { throw null; } + public bool TryFindLast(System.Text.Rune value, out System.Range range) { throw null; } + public bool TryFindLast(System.Text.Rune value, System.StringComparison comparisonType, out System.Range range) { throw null; } + public bool TryFindLast(System.Text.Utf8Span value, out System.Range range) { throw null; } + public bool TryFindLast(System.Text.Utf8Span value, System.StringComparison comparisonType, out System.Range range) { throw null; } + public static System.Text.Utf8Span UnsafeCreateWithoutValidation(System.ReadOnlySpan buffer) { throw null; } + public readonly ref struct CharEnumerable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public Enumerator GetEnumerator() { throw null; } + public ref struct Enumerator + { + private object _dummy; + private int _dummyPrimitive; + public char Current { get { throw null; } } + public bool MoveNext() { throw null; } + } + } + public readonly ref struct RuneEnumerable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public Enumerator GetEnumerator() { throw null; } + public ref struct Enumerator + { + private object _dummy; + private int _dummyPrimitive; + public System.Text.Rune Current { get { throw null; } } + public bool MoveNext() { throw null; } + } + } + public readonly ref struct SplitResult + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5, out System.Text.Utf8Span item6) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5, out System.Text.Utf8Span item6, out System.Text.Utf8Span item7) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5, out System.Text.Utf8Span item6, out System.Text.Utf8Span item7, out System.Text.Utf8Span item8) { throw null; } + public Enumerator GetEnumerator() { throw null; } + public ref struct Enumerator + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public System.Text.Utf8Span Current { get { throw null; } } + public bool MoveNext() { throw null; } + } + } + public readonly ref struct SplitOnResult + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public Utf8Span After { get { throw null; } } + public Utf8Span Before { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void Deconstruct(out System.Text.Utf8Span before, out System.Text.Utf8Span after) { throw null; } + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.csproj b/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.csproj index 5233d28..dfb8a35 100644 --- a/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.csproj +++ b/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.csproj @@ -1,7 +1,11 @@  true + {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0} + + $(NoWarn);0809;0618 netcoreapp-Debug;netcoreapp-Release + enable diff --git a/src/libraries/System.Utf8String.Experimental/src/System/IO/Utf8StringStream.cs b/src/libraries/System.Utf8String.Experimental/src/System/IO/Utf8StringStream.cs index c2ffa23..5a339db 100644 --- a/src/libraries/System.Utf8String.Experimental/src/System/IO/Utf8StringStream.cs +++ b/src/libraries/System.Utf8String.Experimental/src/System/IO/Utf8StringStream.cs @@ -25,14 +25,14 @@ namespace System.IO public override bool CanWrite => false; - public override long Length => _content.Length; + public override long Length => _content.AsBytes().Length; public override long Position { get => _position; set { - if ((ulong)value > (uint)_content.Length) + if ((ulong)value > (uint)_content.AsBytes().Length) { throw new ArgumentOutOfRangeException(nameof(value)); } @@ -84,13 +84,13 @@ namespace System.IO public override int ReadByte() { int position = _position; - if ((uint)position >= (uint)_content.Length) + if ((uint)position >= (uint)_content.AsBytes().Length) { return -1; } _position++; - return _content[position]; + return _content.AsBytes()[position]; } public override long Seek(long offset, SeekOrigin origin) @@ -103,13 +103,13 @@ namespace System.IO offset += _position; break; case SeekOrigin.End: - offset += _content.Length; + offset += _content.AsBytes().Length; break; default: throw new ArgumentOutOfRangeException(nameof(origin)); } - if ((ulong)offset > (uint)_content.Length) + if ((ulong)offset > (uint)_content.AsBytes().Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } diff --git a/src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.cs b/src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.cs index 18b36ee..2c350f0 100644 --- a/src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.cs +++ b/src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.cs @@ -48,7 +48,7 @@ namespace System.Net.Http protected override bool TryComputeLength(out long length) { - length = _content.Length; + length = _content.AsBytes().Length; return true; } } diff --git a/src/libraries/System.Utf8String.Experimental/tests/System.Utf8String.Experimental.Tests.csproj b/src/libraries/System.Utf8String.Experimental/tests/System.Utf8String.Experimental.Tests.csproj index b6fb150..deb16c3 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System.Utf8String.Experimental.Tests.csproj +++ b/src/libraries/System.Utf8String.Experimental/tests/System.Utf8String.Experimental.Tests.csproj @@ -4,6 +4,7 @@ true netcoreapp-Debug;netcoreapp-Release true + true System @@ -16,11 +17,23 @@ + + + + + + + + + + + + - + \ No newline at end of file diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/BoundedUtf8Span.cs b/src/libraries/System.Utf8String.Experimental/tests/System/BoundedUtf8Span.cs new file mode 100644 index 0000000..d8a1c46 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/BoundedUtf8Span.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Text; + +using static System.Tests.Utf8TestUtilities; + +namespace System.Tests +{ + /// + /// Allows creating instances that wrap . + /// Useful for ensuring an API under test doesn't read past the end of the span. + /// + public sealed class BoundedUtf8Span : IDisposable + { + private readonly BoundedMemory _boundedMemory; + + public BoundedUtf8Span(ReadOnlySpan utf16Data, PoisonPagePlacement placement = PoisonPagePlacement.After) + : this(u8(utf16Data.ToString()).AsBytes(), placement) + { + } + + public BoundedUtf8Span(ReadOnlySpan utf8Data, PoisonPagePlacement placement = PoisonPagePlacement.After) + { + _boundedMemory = BoundedMemory.AllocateFromExistingData(utf8Data, placement); + } + + public Utf8Span Span => Utf8Span.UnsafeCreateWithoutValidation(_boundedMemory.Span); + + public void Dispose() + { + _boundedMemory.Dispose(); + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/RangeEqualityComparer.cs b/src/libraries/System.Utf8String.Experimental/tests/System/RangeEqualityComparer.cs new file mode 100644 index 0000000..8cc3938 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/RangeEqualityComparer.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Xunit; + +namespace System.Tests +{ + /// + /// Given a fixed length, compares two instances for equality. + /// + public sealed class RangeEqualityComparer : IEqualityComparer + { + private int _length; + + public RangeEqualityComparer(int length) + { + Assert.True(length >= 0); + + _length = length; + } + + public bool Equals(Range x, Range y) + { + (int offsetX, int lengthX) = x.GetOffsetAndLength(_length); + (int offsetY, int lengthY) = y.GetOffsetAndLength(_length); + + return offsetX == offsetY && lengthX == lengthY; + } + + public int GetHashCode(Range obj) + { + (int offset, int length) = obj.GetOffsetAndLength(_length); + return HashCode.Combine(offset, length); + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8ExtensionsTests.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8ExtensionsTests.cs index a3c2182..604b02e 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8ExtensionsTests.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8ExtensionsTests.cs @@ -165,19 +165,20 @@ namespace System.Tests [Fact] public void AsSpan_FromUtf8String() { - Assert.True(default(ReadOnlySpan) == ((Utf8String)null).AsSpan()); + Assert.True(((Utf8String)null).AsSpan().Bytes == default); // referential equality check Utf8String theString = u8("Hello"); - Assert.True(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in theString.GetPinnableReference())), 5) == theString.AsSpan()); + + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in theString.GetPinnableReference()), ref Unsafe.AsRef(in theString.AsSpan().GetPinnableReference()))); + Assert.Equal(5, theString.AsSpan().Bytes.Length); } [Fact] public void AsSpan_FromUtf8String_WithStart() { - Assert.True(default(ReadOnlySpan) == ((Utf8String)null).AsSpan(0)); + Assert.True(((Utf8String)null).AsSpan(0).Bytes == default); // referential equality check Assert.True(u8("Hello").AsSpan(5).IsEmpty); - - SpanAssert.Equal(new Char8[] { (Char8)'e', (Char8)'l', (Char8)'l', (Char8)'o' }, u8("Hello").AsSpan(1)); + Assert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l', (byte)'o' }, u8("Hello").AsSpan(1).Bytes.ToArray()); } [Fact] @@ -191,10 +192,9 @@ namespace System.Tests [Fact] public void AsSpan_FromUtf8String_WithStartAndLength() { - Assert.True(default(ReadOnlySpan) == ((Utf8String)null).AsSpan(0, 0)); + Assert.True(((Utf8String)null).AsSpan(0, 0).Bytes == default); // referential equality check Assert.True(u8("Hello").AsSpan(5, 0).IsEmpty); - - SpanAssert.Equal(new Char8[] { (Char8)'e', (Char8)'l', (Char8)'l' }, u8("Hello").AsSpan(1, 3)); + Assert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l' }, u8("Hello").AsSpan(1, 3).Bytes.ToArray()); } [Fact] diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs new file mode 100644 index 0000000..058ff7f --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using System.Tests; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +using static System.Tests.Utf8TestUtilities; + +namespace System.Text.Tests +{ + public partial class Utf8SpanTests + { + [Fact] + public static void Equals_Object_ThrowsNotSupported() + { + Utf8Span span = Utf8Span.Empty; + + Assert.Throws(() => Utf8Span.Empty.Equals((object)null)); + Assert.Throws(() => Utf8Span.Empty.Equals(new object())); + } + + [Fact] + public static void Equals_Ordinal() + { + // First make sure referential equality passes + + Utf8Span span1 = u8("Hello!"); + Utf8Span span2 = span1; + AssertEqualOrdinal(span1, span2); + + // Now make sure deep equality passes + + span2 = Utf8Span.UnsafeCreateWithoutValidation(Encoding.UTF8.GetBytes("Hello!")); + AssertEqualOrdinal(span1, span2); + + // Now mutate one of the inputs and make sure they're inequal + + span2 = u8("Bello!"); + AssertNotEqualOrdinal(span1, span2); + + // Finally, make sure null / null and null / empty are treated as the same + + AssertEqualOrdinal(Utf8Span.Empty, Utf8Span.Empty); + AssertEqualOrdinal(Utf8Span.Empty, u8("")); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData(null, null, StringComparison.OrdinalIgnoreCase, null, true)] + [InlineData("encyclopaedia", "encyclopædia", StringComparison.OrdinalIgnoreCase, null, false)] + [InlineData("encyclopaedia", "encyclopædia", StringComparison.InvariantCulture, null, true)] + [InlineData("encyclopaedia", "ENCYCLOPÆDIA", StringComparison.InvariantCulture, null, false)] + [InlineData("encyclopaedia", "encyclopædia", StringComparison.InvariantCultureIgnoreCase, null, true)] + [InlineData("encyclopaedia", "ENCYCLOPÆDIA", StringComparison.InvariantCultureIgnoreCase, null, true)] + [InlineData("Weiß", "WEISS", StringComparison.OrdinalIgnoreCase, null, false)] + [InlineData("Weiß", "WEISS", StringComparison.InvariantCulture, null, false)] + [InlineData("Weiß", "WEISS", StringComparison.InvariantCultureIgnoreCase, null, true)] + [InlineData("Weiß", "WEISS", StringComparison.CurrentCulture, "de-DE", false)] + [InlineData("Weiß", "WEISS", StringComparison.CurrentCultureIgnoreCase, "de-DE", true)] + [InlineData("γένεσις", "ΓΈΝΕΣΙΣ", StringComparison.InvariantCultureIgnoreCase, null, true)] + [InlineData("ıI", "iI", StringComparison.CurrentCulture, "tr-TR", false)] + [InlineData("ıI", "iI", StringComparison.CurrentCultureIgnoreCase, "tr-TR", false)] + [InlineData("İI", "iI", StringComparison.CurrentCultureIgnoreCase, "tr-TR", true)] + public static void Equals_NonOrdinal(string str1, string str2, StringComparison comparison, string culture, bool shouldCompareAsEqual) + { + Func action = (str1, str2, comparison, culture, shouldCompareAsEqual) => + { + if (culture != null) + { + CultureInfo.CurrentCulture = new CultureInfo(culture); + } + + using BoundedUtf8Span boundedSpan1 = new BoundedUtf8Span(str1); + using BoundedUtf8Span boundedSpan2 = new BoundedUtf8Span(str2); + + Utf8Span span1 = boundedSpan1.Span; + Utf8Span span2 = boundedSpan2.Span; + + StringComparison comparisonType = Enum.Parse(comparison); + bool expected = bool.Parse(shouldCompareAsEqual); + + Assert.Equal(expected, span1.Equals(span2, comparisonType)); + Assert.Equal(expected, span2.Equals(span1, comparisonType)); + Assert.Equal(expected, Utf8Span.Equals(span1, span2, comparisonType)); + Assert.Equal(expected, Utf8Span.Equals(span2, span1, comparisonType)); + + return RemoteExecutor.SuccessExitCode; + }; + + if (culture != null) + { + // need to apply a culture to the current thread + RemoteExecutor.Invoke(action, str1, str2, comparison.ToString(), culture, shouldCompareAsEqual.ToString()).Dispose(); + } + else + { + action(str1, str2, comparison.ToString(), culture, shouldCompareAsEqual.ToString()); + } + } + + private static void AssertEqualOrdinal(Utf8Span span1, Utf8Span span2) + { + Assert.True(span1.Equals(span2)); + Assert.True(span2.Equals(span1)); + + Assert.True(span1.Equals(span2, StringComparison.Ordinal)); + Assert.True(span2.Equals(span1, StringComparison.Ordinal)); + + Assert.True(Utf8Span.Equals(span1, span2)); + Assert.True(Utf8Span.Equals(span2, span1)); + + Assert.True(Utf8Span.Equals(span1, span2, StringComparison.Ordinal)); + Assert.True(Utf8Span.Equals(span2, span1, StringComparison.Ordinal)); + + Assert.True(span1 == span2); + Assert.True(span2 == span1); + + Assert.False(span1 != span2); + Assert.False(span2 != span1); + } + + private static void AssertNotEqualOrdinal(Utf8Span span1, Utf8Span span2) + { + Assert.False(span1.Equals(span2)); + Assert.False(span2.Equals(span1)); + + Assert.False(span1.Equals(span2, StringComparison.Ordinal)); + Assert.False(span2.Equals(span1, StringComparison.Ordinal)); + + Assert.False(Utf8Span.Equals(span1, span2)); + Assert.False(Utf8Span.Equals(span2, span1)); + + Assert.False(Utf8Span.Equals(span1, span2, StringComparison.Ordinal)); + Assert.False(Utf8Span.Equals(span2, span1, StringComparison.Ordinal)); + + Assert.False(span1 == span2); + Assert.False(span2 == span1); + + Assert.True(span1 != span2); + Assert.True(span2 != span1); + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Conversion.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Conversion.cs new file mode 100644 index 0000000..01721a4 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Conversion.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Globalization; +using System.Tests; +using Xunit; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public partial class Utf8SpanTests + { + [Theory] + [MemberData(nameof(NormalizationData))] + public static void Normalize(string utf16Source, string utf16Expected, NormalizationForm normalizationForm) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(utf16Source); + Utf8Span utf8Source = boundedSpan.Span; + + // Quick IsNormalized tests + + Assert.Equal(utf16Source == utf16Expected, utf8Source.IsNormalized(normalizationForm)); + + // Normalize and return new Utf8String instances + + ustring utf8Normalized = utf8Source.Normalize(normalizationForm); + Assert.True(ustring.AreEquivalent(utf8Normalized, utf16Expected)); + + // Normalize to byte arrays which are too small, expect -1 (failure) + + Assert.Equal(-1, utf8Source.Normalize(new byte[utf8Normalized.GetByteLength() - 1], normalizationForm)); + + // Normalize to byte arrays which are the correct length, expect success, + // then compare buffer contents for ordinal equality. + + foreach (int bufferLength in new int[] { utf8Normalized.GetByteLength() /* just right */, utf8Normalized.GetByteLength() + 1 /* with extra room */}) + { + byte[] dest = new byte[bufferLength]; + Assert.Equal(utf8Normalized.GetByteLength(), utf8Source.Normalize(dest, normalizationForm)); + Utf8Span normalizedSpan = Utf8Span.UnsafeCreateWithoutValidation(dest[..utf8Normalized.GetByteLength()]); + Assert.True(utf8Normalized.AsSpan() == normalizedSpan); // ordinal equality + Assert.True(normalizedSpan.IsNormalized(normalizationForm)); + } + } + + [Theory] + [MemberData(nameof(CaseConversionData))] + public static void ToLower(string testData) + { + static void RunTest(string testData, string expected, CultureInfo culture) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(testData); + Utf8Span inputSpan = boundedSpan.Span; + + // First try the allocating APIs + + ustring expectedUtf8 = u8(expected) ?? ustring.Empty; + ustring actualUtf8; + + if (culture is null) + { + actualUtf8 = inputSpan.ToLowerInvariant(); + } + else + { + actualUtf8 = inputSpan.ToLower(culture); + } + + Assert.Equal(expectedUtf8, actualUtf8); + + // Next, try the non-allocating APIs with too small a buffer + + if (expectedUtf8.GetByteLength() > 0) + { + byte[] bufferTooSmall = new byte[expectedUtf8.GetByteLength() - 1]; + + if (culture is null) + { + Assert.Equal(-1, inputSpan.ToLowerInvariant(bufferTooSmall)); + } + else + { + Assert.Equal(-1, inputSpan.ToLower(bufferTooSmall, culture)); + } + } + + // Then the non-allocating APIs with a properly sized buffer + + foreach (int bufferSize in new[] { expectedUtf8.GetByteLength(), expectedUtf8.GetByteLength() + 1 }) + { + byte[] buffer = new byte[expectedUtf8.GetByteLength()]; + + if (culture is null) + { + Assert.Equal(expectedUtf8.GetByteLength(), inputSpan.ToLowerInvariant(buffer)); + } + else + { + Assert.Equal(expectedUtf8.GetByteLength(), inputSpan.ToLower(buffer, culture)); + } + + Assert.True(expectedUtf8.AsBytes().SequenceEqual(buffer)); + } + } + + RunTest(testData, testData?.ToLowerInvariant(), null); + RunTest(testData, testData?.ToLower(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + RunTest(testData, testData?.ToLower(CultureInfo.GetCultureInfo("en-US")), CultureInfo.GetCultureInfo("en-US")); + RunTest(testData, testData?.ToLower(CultureInfo.GetCultureInfo("tr-TR")), CultureInfo.GetCultureInfo("tr-TR")); + } + + [Theory] + [MemberData(nameof(CaseConversionData))] + public static void ToUpper(string testData) + { + static void RunTest(string testData, string expected, CultureInfo culture) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(testData); + Utf8Span inputSpan = boundedSpan.Span; + + // First try the allocating APIs + + ustring expectedUtf8 = u8(expected) ?? ustring.Empty; + ustring actualUtf8; + + if (culture is null) + { + actualUtf8 = inputSpan.ToUpperInvariant(); + } + else + { + actualUtf8 = inputSpan.ToUpper(culture); + } + + Assert.Equal(expectedUtf8, actualUtf8); + + // Next, try the non-allocating APIs with too small a buffer + + if (expectedUtf8.GetByteLength() > 0) + { + byte[] bufferTooSmall = new byte[expectedUtf8.GetByteLength() - 1]; + + if (culture is null) + { + Assert.Equal(-1, inputSpan.ToUpperInvariant(bufferTooSmall)); + } + else + { + Assert.Equal(-1, inputSpan.ToUpper(bufferTooSmall, culture)); + } + } + + // Then the non-allocating APIs with a properly sized buffer + + foreach (int bufferSize in new[] { expectedUtf8.GetByteLength(), expectedUtf8.GetByteLength() + 1 }) + { + byte[] buffer = new byte[expectedUtf8.GetByteLength()]; + + if (culture is null) + { + Assert.Equal(expectedUtf8.GetByteLength(), inputSpan.ToUpperInvariant(buffer)); + } + else + { + Assert.Equal(expectedUtf8.GetByteLength(), inputSpan.ToUpper(buffer, culture)); + } + + Assert.True(expectedUtf8.AsBytes().SequenceEqual(buffer)); + } + } + + RunTest(testData, testData?.ToUpperInvariant(), null); + RunTest(testData, testData?.ToUpper(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + RunTest(testData, testData?.ToUpper(CultureInfo.GetCultureInfo("en-US")), CultureInfo.GetCultureInfo("en-US")); + RunTest(testData, testData?.ToUpper(CultureInfo.GetCultureInfo("tr-TR")), CultureInfo.GetCultureInfo("tr-TR")); + } + + public static IEnumerable CaseConversionData() + { + string[] testCases = new string[] + { + null, + string.Empty, + "Hello", + "iı", // dotted and dotless I + "İI", // dotted and dotless I + }; + + foreach (string testCase in testCases) + { + yield return new object[] { testCase }; + } + } + + public static IEnumerable NormalizationData() + { + // These test cases are from the Unicode Standard Annex #15, Figure 6 + // https://unicode.org/reports/tr15/ + + var testCases = new[] + { + new + { + Source = "\ufb01", // "fi" (LATIN SMALL LIGATURE FI) + NFD = "\ufb01", // same as source + NFC = "\ufb01", // same as source + NFKD = "fi", // compatibility decomposed into ASCII chars + NFKC = "fi", // compatibility decomposed into ASCII chars + }, + new + { + Source = "2\u2075", // "2⁵" (SUPERSCRIPT FIVE) + NFD = "2\u2075", // same as source + NFC = "2\u2075", // same as source + NFKD = "25", // compatibility decomposed into ASCII chars + NFKC = "25", // compatibility decomposed into ASCII chars + }, + new + { + Source = "\u1e9b\u0323", // 'ẛ' (LATIN SMALL LETTER LONG S WITH DOT ABOVE) + COMBINING DOT BELOW + NFD = "\u017f\u0323\u0307", // 'ſ' (LATIN SMALL LETTER LONG S) + COMBINING DOT BELOW + COMBINING DOT ABOVE + NFC = "\u1e9b\u0323", // same as source + NFKD = "s\u0323\u0307", // ASCII 's' + COMBINING DOT BELOW + COMBINING DOT ABOVE + NFKC = "\u1e69", // "ṩ" (LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE) + } + }; + + foreach (var testCase in testCases) + { + yield return new object[] { testCase.Source, testCase.NFD, NormalizationForm.FormD }; + yield return new object[] { testCase.Source, testCase.NFC, NormalizationForm.FormC }; + yield return new object[] { testCase.Source, testCase.NFKD, NormalizationForm.FormKD }; + yield return new object[] { testCase.Source, testCase.NFKC, NormalizationForm.FormKC }; + } + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Ctor.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Ctor.cs new file mode 100644 index 0000000..725a775 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Ctor.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public unsafe partial class Utf8SpanTests + { + [Fact] + public static void Ctor_EmptyUtf8String() + { + // Arrange + + ustring str = ustring.Empty; + + // Act + + Utf8Span span = new Utf8Span(str); + + // Assert + // GetPinnableReference should be 'null' to match behavior of empty ROS.GetPinnableReference(); + + Assert.True(span.IsEmpty); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in str.GetPinnableReference()), ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(0, span.Bytes.Length); + } + + [Fact] + public static void Ctor_NonEmptyUtf8String() + { + // Arrange + + ustring str = u8("Hello!"); + + // Act + + Utf8Span span = new Utf8Span(str); + + // Assert + + Assert.False(span.IsEmpty); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in str.GetPinnableReference()), ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in str.GetPinnableReference()), ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(6, span.Bytes.Length); + } + + [Fact] + public static void Ctor_NullUtf8String() + { + // Arrange + + ustring str = null; + + // Act + + Utf8Span span = new Utf8Span(str); + + // Assert + // GetPinnableReference should be 'null' to match behavior of empty ROS.GetPinnableReference(); + + Assert.True(span.IsEmpty); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(0, span.Bytes.Length); + } + + [Fact] + public static void Ctor_UnsafeFromByteSpan_NonEmpty() + { + // Arrange + + ReadOnlySpan original = new byte[] { 1, 2, 3, 4, 5 }; + + // Act + + Utf8Span span = Utf8Span.UnsafeCreateWithoutValidation(original); + + // Assert + + Assert.False(span.IsEmpty); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in original.GetPinnableReference()), ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in original.GetPinnableReference()), ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(5, span.Bytes.Length); + } + + [Fact] + public static void Ctor_UnsafeFromByteSpan_NonNullEmptyArray() + { + // Arrange + + ReadOnlySpan original = new byte[0]; + + // Act + + Utf8Span span = Utf8Span.UnsafeCreateWithoutValidation(original); + + // Assert + // GetPinnableReference should be 'null' to match behavior of empty ROS.GetPinnableReference(); + + Assert.True(span.IsEmpty); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.True(Unsafe.AreSame(ref MemoryMarshal.GetReference(original), ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(0, span.Bytes.Length); + } + + [Fact] + public static void Ctor_UnsafeFromByteSpan_Default() + { + // Arrange + + ReadOnlySpan original = default; + + // Act + + Utf8Span span = Utf8Span.UnsafeCreateWithoutValidation(original); + + // Assert + // GetPinnableReference should be 'null' to match behavior of empty ROS.GetPinnableReference(); + + Assert.True(span.IsEmpty); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(0, span.Bytes.Length); + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Enumeration.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Enumeration.cs new file mode 100644 index 0000000..64b85a9 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Enumeration.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Tests; +using Xunit; + +namespace System.Text.Tests +{ + public unsafe partial class Utf8SpanTests + { + [Fact] + public static void CharsProperty_FromData() + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234"); + Utf8Span span = boundedSpan.Span; + + var charsEnumerator = span.Chars.GetEnumerator(); + + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\U00000012', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\U00000123', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\U00001234', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\uDBC4', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\uDE34', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\U00000012', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\U00000123', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\U00001234', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\uDBC4', charsEnumerator.Current); + Assert.True(charsEnumerator.MoveNext()); + Assert.Equal('\uDE34', charsEnumerator.Current); + Assert.False(charsEnumerator.MoveNext()); + } + + [Fact] + public static void CharsProperty_FromEmpty() + { + Assert.False(Utf8Span.Empty.Chars.GetEnumerator().MoveNext()); + } + + [Fact] + public static void RunesProperty_FromEmpty() + { + Assert.False(Utf8Span.Empty.Runes.GetEnumerator().MoveNext()); + } + + [Fact] + public static void RunesProperty_FromData() + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234"); + Utf8Span span = boundedSpan.Span; + + var runesEnumerator = span.Runes.GetEnumerator(); + + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x0012), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x0123), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x1234), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x101234), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x0012), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x0123), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x1234), runesEnumerator.Current); + Assert.True(runesEnumerator.MoveNext()); + Assert.Equal(new Rune(0x101234), runesEnumerator.Current); + Assert.False(runesEnumerator.MoveNext()); + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.TestData.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.TestData.cs new file mode 100644 index 0000000..95503bc --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.TestData.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections.Generic; +using System.Globalization; +using System.Tests; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public partial class Utf8SpanTests + { + public static IEnumerable SplitData_CharSeparator() + { + foreach (SplitTestData entry in SplitData_All()) + { + if (!TryParseSearchTermAsChar(entry.SearchTerm, out char searchChar)) + { + continue; + } + + yield return new object[] + { + entry.Source, + searchChar, + entry.ExpectedRanges + }; + } + } + + public static IEnumerable SplitData_RuneSeparator() + { + foreach (SplitTestData entry in SplitData_All()) + { + if (!TryParseSearchTermAsRune(entry.SearchTerm, out Rune searchRune)) + { + continue; + } + + yield return new object[] + { + entry.Source, + searchRune, + entry.ExpectedRanges + }; + } + } + + public static IEnumerable SplitData_Utf8SpanSeparator() + { + foreach (SplitTestData entry in SplitData_All()) + { + if (!TryParseSearchTermAsUtf8String(entry.SearchTerm, out ustring searchTerm)) + { + continue; + } + + yield return new object[] + { + entry.Source, + searchTerm, + entry.ExpectedRanges + }; + } + } + + private static IEnumerable SplitData_All() + { + SplitTestData[] testDataEntries = new SplitTestData[] + { + new SplitTestData + { + // Empty source, searching for anything results in no match + Source = null, + SearchTerm = '\0', + ExpectedRanges = new[] { 0..0 } + }, + new SplitTestData + { + // If no match, then return original span + Source = u8("Hello"), + SearchTerm = 'x', + ExpectedRanges = new[] { Range.All } + }, + new SplitTestData + { + // Match returns multiple spans (some may be empty) + Source = u8("Hello"), + SearchTerm = 'l', + ExpectedRanges = new[] { 0..2, 3..3, 4..5 } + }, + new SplitTestData + { + // Match returns multiple spans (non-empty) + Source = u8("Hello"), + SearchTerm = "ell", + ExpectedRanges = new[] { 0..1, ^1.. } + }, + new SplitTestData + { + // Match returns multiple spans (non-empty, with whitespace) + Source = u8("aax aaa xxax \u2028\u2029"), // includes LS, PS as whitespace + SearchTerm = 'x', + ExpectedRanges = new[] { 0..2, 3..8, 9..9, 10..11, 12.. } + }, + new SplitTestData + { + // Matching on U+1F600 GRINNING FACE (with whitespace) + Source = u8("x \U0001F600 y"), + SearchTerm = new Rune(0x1F600), + ExpectedRanges = new[] { 0..2, ^2.. } + }, + }; + + return testDataEntries; + } + + public class SplitTestData + { + public ustring Source; + public object SearchTerm; + public Range[] ExpectedRanges; + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.cs new file mode 100644 index 0000000..cc42dbb --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Manipulation.cs @@ -0,0 +1,329 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.Buffers; +using System.Collections.Generic; +using System.Tests; +using Xunit; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public partial class Utf8SpanTests + { + private delegate Utf8Span.SplitResult Utf8SpanSplitDelegate(Utf8Span span, Utf8StringSplitOptions splitOptions); + + [Fact] + public static void Split_EmptySearchSpan_Throws() + { + // Shouldn't be able to split on an empty UTF-8 span. + // Such an enumerator would iterate forever, so we forbid it. + + var ex = Assert.Throws(() => { u8("Hello").AsSpan().Split(Utf8Span.Empty); }); + Assert.Equal("separator", ex.ParamName); + } + + [Fact] + public static void Split_InvalidChar_Throws() + { + // Shouldn't be able to split on a standalone surrogate char + // Other search methods (TryFind) return false when given a standalone surrogate char as input, + // but the Split methods returns a complex data structure instead of a simple bool. So to keep + // the logic of that data structure relatively simple we'll forbid the bad char at the call site. + + var ex = Assert.Throws(() => { u8("Hello").AsSpan().Split('\ud800'); }); + Assert.Equal("separator", ex.ParamName); + } + + [Fact] + public static void Split_Char_NullInput() + { + // First, make sure that .Split(',') yields a single-element [ ]. + + Utf8Span source = Utf8Span.Empty; + + var enumerator = source.Split(',').GetEnumerator(); + Assert.True(enumerator.MoveNext()); + Assert.True(source.Bytes == enumerator.Current.Bytes); // referential equality + Assert.False(enumerator.MoveNext()); + + // Next, make sure that if "remove empty entries" is specified, yields the empty set [ ]. + + enumerator = source.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Theory] + [MemberData(nameof(SplitData_CharSeparator))] + public static void Split_Char(ustring source, char separator, Range[] expectedRanges) + { + SplitTest_Common(source, (span, splitOptions) => span.Split(separator, splitOptions), expectedRanges); + } + + [Fact] + public static void Split_Deconstruct() + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("a,b,c,d,e"); + Utf8Span span = boundedSpan.Span; + + // Note referential equality checks below (since we want to know exact slices + // into the original buffer), not deep (textual) equality checks. + + { + (Utf8Span a, Utf8Span b) = span.Split('x'); // not found + Assert.True(a.Bytes == span.Bytes, "Expected referential equality of input."); + Assert.True(b.Bytes == default); + } + + { + (Utf8Span a, Utf8Span b) = span.Split(','); + Assert.True(a.Bytes == span.Bytes[..1]); // "a" + Assert.True(b.Bytes == span.Bytes[2..]); // "b,c,d,e" + } + + { + (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e) = span.Split(','); + Assert.True(a.Bytes == span.Bytes[0..1]); // "a" + Assert.True(b.Bytes == span.Bytes[2..3]); // "b" + Assert.True(c.Bytes == span.Bytes[4..5]); // "c" + Assert.True(d.Bytes == span.Bytes[6..7]); // "d" + Assert.True(e.Bytes == span.Bytes[8..9]); // "e" + } + + { + (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e, Utf8Span f, Utf8Span g, Utf8Span h) = span.Split(','); + Assert.True(a.Bytes == span.Bytes[0..1]); // "a" + Assert.True(b.Bytes == span.Bytes[2..3]); // "b" + Assert.True(c.Bytes == span.Bytes[4..5]); // "c" + Assert.True(d.Bytes == span.Bytes[6..7]); // "d" + Assert.True(e.Bytes == span.Bytes[8..9]); // "e" + Assert.True(f.Bytes == default); + Assert.True(g.Bytes == default); + Assert.True(h.Bytes == default); + } + } + + [Fact] + public static void Split_Deconstruct_WithOptions() + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("a, , b, c,, d, e"); + Utf8Span span = boundedSpan.Span; + + // Note referential equality checks below (since we want to know exact slices + // into the original buffer), not deep (textual) equality checks. + + { + (Utf8Span a, Utf8Span b) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries); + Assert.True(a.Bytes == span.Bytes[..1]); // "a" + Assert.True(b.Bytes == span.Bytes[2..]); // " , b, c,, d, e" + } + + { + (Utf8Span a, Utf8Span x, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries); + Assert.True(a.Bytes == span.Bytes[0..1]); // "a" + Assert.True(x.Bytes == span.Bytes[2..3]); // " " + Assert.True(b.Bytes == span.Bytes[4..6]); // " b" + Assert.True(c.Bytes == span.Bytes[7..9]); // " c" + Assert.True(d.Bytes == span.Bytes[11..13]); // " d" + Assert.True(e.Bytes == span.Bytes[14..]); // " e" + } + + { + (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e, Utf8Span f, Utf8Span g, Utf8Span h) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries | Utf8StringSplitOptions.TrimEntries); + Assert.True(a.Bytes == span.Bytes[0..1]); // "a" + Assert.True(b.Bytes == span.Bytes[5..6]); // "b" + Assert.True(c.Bytes == span.Bytes[8..9]); // "c" + Assert.True(d.Bytes == span.Bytes[12..13]); // "d" + Assert.True(e.Bytes == span.Bytes[15..]); // "e" + Assert.True(f.Bytes == default); + Assert.True(g.Bytes == default); + Assert.True(h.Bytes == default); + } + } + + [Theory] + [MemberData(nameof(SplitData_RuneSeparator))] + public static void Split_Rune(ustring source, Rune separator, Range[] expectedRanges) + { + SplitTest_Common(source, (span, splitOptions) => span.Split(separator, splitOptions), expectedRanges); + } + + [Theory] + [MemberData(nameof(SplitData_Utf8SpanSeparator))] + public static void Split_Utf8Span(ustring source, ustring separator, Range[] expectedRanges) + { + SplitTest_Common(source, (span, splitOptions) => span.Split(separator.AsSpan(), splitOptions), expectedRanges); + } + + private static void SplitTest_Common(ustring source, Utf8SpanSplitDelegate splitAction, Range[] expectedRanges) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span span = boundedSpan.Span; + int totalSpanLengthInBytes = span.Bytes.Length; + source = null; // to avoid inadvertently using this for the remainder of the method + + // First, run the split with default options and make sure the ranges are equivalent + + List actualRanges = new List(); + foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.None)) + { + actualRanges.Add(GetRangeOfSubspan(span, slice)); + } + + Assert.Equal(expectedRanges, actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes)); + + // Next, run the split with empty entries removed + + actualRanges = new List(); + foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.RemoveEmptyEntries)) + { + actualRanges.Add(GetRangeOfSubspan(span, slice)); + } + + Assert.Equal(expectedRanges.Where(range => !range.IsEmpty(totalSpanLengthInBytes)), actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes)); + + // Next, run the split with results trimmed (but allowing empty results) + + expectedRanges = (Range[])expectedRanges.Clone(); // clone the array since we're about to mutate it + for (int i = 0; i < expectedRanges.Length; i++) + { + expectedRanges[i] = GetRangeOfSubspan(span, span[expectedRanges[i]].Trim()); + } + + actualRanges = new List(); + foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.TrimEntries)) + { + actualRanges.Add(GetRangeOfSubspan(span, slice)); + } + + Assert.Equal(expectedRanges, actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes)); + + // Finally, run the split both trimmed and with empty entries removed + + actualRanges = new List(); + foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.TrimEntries | Utf8StringSplitOptions.RemoveEmptyEntries)) + { + actualRanges.Add(GetRangeOfSubspan(span, slice)); + } + + Assert.Equal(expectedRanges.Where(range => !range.IsEmpty(totalSpanLengthInBytes)), actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes)); + } + + [Theory] + [MemberData(nameof(Trim_TestData))] + public static void Trim(string input) + { + // Arrange + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + Utf8Span span = boundedSpan.Span; + + // Act + + Utf8Span trimmed = span.Trim(); + + // Assert + // Compute the trim manually and ensure it matches the trimmed span's characteristics. + + ReadOnlySpan utf8Bytes = span.Bytes; + while (!utf8Bytes.IsEmpty) + { + OperationStatus status = Rune.DecodeFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed); + Assert.Equal(OperationStatus.Done, status); + + if (!Rune.IsWhiteSpace(decodedRune)) + { + break; + } + + utf8Bytes = utf8Bytes.Slice(bytesConsumed); + } + while (!utf8Bytes.IsEmpty) + { + OperationStatus status = Rune.DecodeLastFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed); + Assert.Equal(OperationStatus.Done, status); + + if (!Rune.IsWhiteSpace(decodedRune)) + { + break; + } + + utf8Bytes = utf8Bytes[..^bytesConsumed]; + } + + Assert.True(trimmed.Bytes == utf8Bytes); // must be an exact buffer match (address + length) + } + + [Theory] + [MemberData(nameof(Trim_TestData))] + public static void TrimEnd(string input) + { + // Arrange + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + Utf8Span span = boundedSpan.Span; + + // Act + + Utf8Span trimmed = span.TrimEnd(); + + // Assert + // Compute the trim manually and ensure it matches the trimmed span's characteristics. + + ReadOnlySpan utf8Bytes = span.Bytes; + while (!utf8Bytes.IsEmpty) + { + OperationStatus status = Rune.DecodeLastFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed); + Assert.Equal(OperationStatus.Done, status); + + if (!Rune.IsWhiteSpace(decodedRune)) + { + break; + } + + utf8Bytes = utf8Bytes[..^bytesConsumed]; + } + + Assert.True(trimmed.Bytes == utf8Bytes); // must be an exact buffer match (address + length) + } + + [Theory] + [MemberData(nameof(Trim_TestData))] + public static void TrimStart(string input) + { + // Arrange + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + Utf8Span span = boundedSpan.Span; + + // Act + + Utf8Span trimmed = span.TrimStart(); + + // Assert + // Compute the trim manually and ensure it matches the trimmed span's characteristics. + + ReadOnlySpan utf8Bytes = span.Bytes; + while (!utf8Bytes.IsEmpty) + { + OperationStatus status = Rune.DecodeFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed); + Assert.Equal(OperationStatus.Done, status); + + if (!Rune.IsWhiteSpace(decodedRune)) + { + break; + } + + utf8Bytes = utf8Bytes.Slice(bytesConsumed); + } + + Assert.True(trimmed.Bytes == utf8Bytes); // must be an exact buffer match (address + length) + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.TestData.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.TestData.cs new file mode 100644 index 0000000..19ccfd4 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.TestData.cs @@ -0,0 +1,576 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Globalization; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public partial class Utf8SpanTests + { + public static IEnumerable TryFindData_Char_Ordinal() + { + foreach (TryFindTestData entry in TryFindData_All()) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal) || entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + continue; + } + + if (!TryParseSearchTermAsChar(entry.SearchTerm, out char searchChar)) + { + continue; + } + + yield return new object[] + { + entry.Source, + searchChar, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + public static IEnumerable TryFindData_Char_WithComparison() + { + foreach (TryFindTestData entry in TryFindData_All()) + { + if (!TryParseSearchTermAsChar(entry.SearchTerm, out char searchChar)) + { + continue; + } + + if (entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal)) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchChar, + StringComparison.Ordinal, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchChar, + StringComparison.OrdinalIgnoreCase, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + foreach (CultureInfo culture in entry.AdditionalCultures ?? Array.Empty()) + { + if (culture == CultureInfo.InvariantCulture) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchChar, + StringComparison.InvariantCulture, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchChar, + StringComparison.InvariantCultureIgnoreCase, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchChar, + StringComparison.CurrentCulture, + culture, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchChar, + StringComparison.CurrentCultureIgnoreCase, + culture, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + } + } + + public static IEnumerable TryFindData_Rune_Ordinal() + { + foreach (TryFindTestData entry in TryFindData_All()) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal) || entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + continue; + } + + if (!TryParseSearchTermAsRune(entry.SearchTerm, out Rune searchRune)) + { + continue; + } + + yield return new object[] + { + entry.Source, + searchRune, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + public static IEnumerable TryFindData_Rune_WithComparison() + { + foreach (TryFindTestData entry in TryFindData_All()) + { + if (!TryParseSearchTermAsRune(entry.SearchTerm, out Rune searchRune)) + { + continue; + } + + if (entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal)) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchRune, + StringComparison.Ordinal, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchRune, + StringComparison.OrdinalIgnoreCase, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + foreach (CultureInfo culture in entry.AdditionalCultures ?? Array.Empty()) + { + if (culture == CultureInfo.InvariantCulture) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchRune, + StringComparison.InvariantCulture, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchRune, + StringComparison.InvariantCultureIgnoreCase, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchRune, + StringComparison.CurrentCulture, + culture, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchRune, + StringComparison.CurrentCultureIgnoreCase, + culture, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + } + } + + public static IEnumerable TryFindData_Utf8Span_Ordinal() + { + foreach (TryFindTestData entry in TryFindData_All()) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal) || entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + continue; + } + + if (!TryParseSearchTermAsUtf8String(entry.SearchTerm, out ustring searchTerm)) + { + continue; + } + + yield return new object[] + { + entry.Source, + searchTerm, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + public static IEnumerable TryFindData_Utf8Span_WithComparison() + { + foreach (TryFindTestData entry in TryFindData_All()) + { + if (!TryParseSearchTermAsUtf8String(entry.SearchTerm, out ustring searchTerm)) + { + continue; + } + + if (entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal)) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchTerm, + StringComparison.Ordinal, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchTerm, + StringComparison.OrdinalIgnoreCase, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + foreach (CultureInfo culture in entry.AdditionalCultures ?? Array.Empty()) + { + if (culture == CultureInfo.InvariantCulture) + { + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchTerm, + StringComparison.InvariantCulture, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchTerm, + StringComparison.InvariantCultureIgnoreCase, + null /* culture */, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly)) + { + yield return new object[] + { + entry.Source, + searchTerm, + StringComparison.CurrentCulture, + culture, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly)) + { + yield return new object[] + { + entry.Source, + searchTerm, + StringComparison.CurrentCultureIgnoreCase, + culture, + entry.ExpectedFirstMatch, + entry.ExpectedLastMatch, + }; + } + } + } + } + + private static IEnumerable TryFindData_All() + { + CultureInfo inv = CultureInfo.InvariantCulture; + CultureInfo en_US = CultureInfo.GetCultureInfo("en-US"); + CultureInfo tr_TR = CultureInfo.GetCultureInfo("tr-TR"); + CultureInfo hu_HU = CultureInfo.GetCultureInfo("hu-HU"); + + TryFindTestData[] testDataEntries = new TryFindTestData[] + { + new TryFindTestData + { + // Searching for the empty string within the empty string should result in 0..0 / ^0..^0 across all comparers and all cultures + Source = null, + SearchTerm = null, + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = new CultureInfo[] { inv, en_US, tr_TR, hu_HU }, + ExpectedFirstMatch = 0..0, + ExpectedLastMatch = ^0..^0, + }, + new TryFindTestData + { + // Searching for the empty string within a non-empty string should result in 0..0 / ^0..^0 across all comparers and all cultures + Source = u8("Hello"), + SearchTerm = null, + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = new CultureInfo[] { inv, en_US, tr_TR, hu_HU }, + ExpectedFirstMatch = 0..0, + ExpectedLastMatch = ^0..^0, + }, + new TryFindTestData + { + // Searching for a non-empty string within an empty string should fail across all comparers and all cultures + Source = null, + SearchTerm = u8("Hello"), + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = new CultureInfo[] { inv, en_US, tr_TR, hu_HU }, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // Searching for the null terminator shouldn't match unless the input contains a null terminator + Source = u8("Hello"), + SearchTerm = '\0', + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = null, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // Searching for the null terminator shouldn't match unless the input contains a null terminator + Source = u8("H\0ell\0o"), + SearchTerm = '\0', + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = null, + ExpectedFirstMatch = 1..2, + ExpectedLastMatch = ^2..^1, + }, + new TryFindTestData + { + // Simple ASCII search with success (case-sensitive) + Source = u8("Hello"), + SearchTerm = 'l', + Options = TryFindTestDataOptions.TestOrdinal | TryFindTestDataOptions.TestCaseSensitiveOnly, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = 2..3, + ExpectedLastMatch = 3..4, + }, + new TryFindTestData + { + // Simple ASCII search with failure (case-sensitive) + Source = u8("Hello"), + SearchTerm = 'L', + Options = TryFindTestDataOptions.TestOrdinal | TryFindTestDataOptions.TestCaseSensitiveOnly, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // Simple ASCII search with success (case-insensitive) + Source = u8("Hello"), + SearchTerm = 'L', + Options = TryFindTestDataOptions.TestOrdinal | TryFindTestDataOptions.TestIgnoreCaseOnly, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = 2..3, + ExpectedLastMatch = 3..4, + }, + new TryFindTestData + { + // U+1F600 GRINNING FACE, should match an exact Rune search + Source = u8("x\U0001F600y"), + SearchTerm = new Rune(0x1F600), + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = 1..5, + ExpectedLastMatch = 1..5, + }, + new TryFindTestData + { + // U+1F600 GRINNING FACE, shouldn't match looking for individual UTF-16 surrogate chars + Source = u8("x\ud83d\ude00y"), + SearchTerm = '\ud83d', + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // U+1F600 GRINNING FACE, shouldn't match on the standalone [ F0 ] byte that begins the multi-byte sequence + Source = u8("x\ud83d\ude00y"), + SearchTerm = '\u00f0', + Options = TryFindTestDataOptions.TestOrdinal, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // hu_HU shouldn't match "d" within "dz" + Source = u8("ab_dz_ba"), + SearchTerm = 'd', + Options = TryFindTestDataOptions.None, + AdditionalCultures = new CultureInfo[] { hu_HU }, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // Turkish I, case-sensitive + Source = u8("\u0069\u0130\u0131\u0049"), // iİıI + SearchTerm = 'i', + Options = TryFindTestDataOptions.TestCaseSensitiveOnly, + AdditionalCultures = new CultureInfo[] { tr_TR }, + ExpectedFirstMatch = 0..1, + ExpectedLastMatch = 0..1, + }, + new TryFindTestData + { + // Turkish I, case-insensitive + Source = u8("\u0069\u0130\u0131\u0049"), // iİıI + SearchTerm = 'i', + Options = TryFindTestDataOptions.TestIgnoreCaseOnly, + AdditionalCultures = new CultureInfo[] { tr_TR }, + ExpectedFirstMatch = 0..1, + ExpectedLastMatch = 1..3, + }, + new TryFindTestData + { + // denormalized forms, no match + Source = u8("a\u0308e\u0308A\u0308E\u0308"), // äëÄË (denormalized) + SearchTerm = 'e', // shouldn't match letter paired with diacritic + Options = TryFindTestDataOptions.None, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = null, + ExpectedLastMatch = null, + }, + new TryFindTestData + { + // denormalized forms, case-sensitive + Source = u8("a\u0308e\u0308A\u0308E\u0308"), // äëÄË (denormalized) + SearchTerm = '\u00eb', // ë, normalized form + Options = TryFindTestDataOptions.TestCaseSensitiveOnly, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = 3..6, + ExpectedLastMatch = 3..6, + }, + new TryFindTestData + { + // denormalized forms, case-insensitive + Source = u8("a\u0308e\u0308A\u0308E\u0308"), // äëÄË (denormalized) + SearchTerm = '\u00eb', // ë, normalized form + Options = TryFindTestDataOptions.TestIgnoreCaseOnly, + AdditionalCultures = new CultureInfo[] { inv }, + ExpectedFirstMatch = 3..6, + ExpectedLastMatch = ^3.., + }, + }; + + return testDataEntries; + } + + public class TryFindTestData + { + public ustring Source; + public object SearchTerm; + public TryFindTestDataOptions Options; + public CultureInfo[] AdditionalCultures; + public Range? ExpectedFirstMatch; + public Range? ExpectedLastMatch; + } + + [Flags] + public enum TryFindTestDataOptions + { + None = 0, + TestOrdinal = 1 << 0, + TestCaseSensitiveOnly = 1 << 1, + TestIgnoreCaseOnly = 2 << 1, + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs new file mode 100644 index 0000000..7df9259 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs @@ -0,0 +1,416 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using System.Tests; +using Xunit; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public unsafe partial class Utf8SpanTests + { + [Theory] + [MemberData(nameof(TryFindData_Char_Ordinal))] + public static void TryFind_Char_Ordinal(ustring source, char searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span searchSpan = boundedSpan.Span; + source = null; // to avoid accidentally using this for the remainder of the test + + // First, search forward + + bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch); + Assert.Equal(expectedForwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedForwardMatch.Value, actualForwardMatch); + } + + // Also check Contains / StartsWith / SplitOn + + Assert.Equal(wasFound, searchSpan.Contains(searchTerm)); + Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm)); + + (var before, var after) = searchSpan.SplitOn(searchTerm); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + + // Now search backward + + wasFound = searchSpan.TryFindLast(searchTerm, out Range actualBackwardMatch); + Assert.Equal(expectedBackwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedBackwardMatch.Value, actualBackwardMatch); + } + + // Also check EndsWith / SplitOnLast + + Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm)); + + (before, after) = searchSpan.SplitOnLast(searchTerm); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [MemberData(nameof(TryFindData_Char_WithComparison))] + public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) + { + RunOnDedicatedThread(() => + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span searchSpan = boundedSpan.Span; + source = null; // to avoid accidentally using this for the remainder of the test + + if (currentCulture != null) + { + CultureInfo.CurrentCulture = currentCulture; + } + + // First, search forward + + bool wasFound = searchSpan.TryFind(searchTerm, comparison, out Range actualForwardMatch); + Assert.Equal(expectedForwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedForwardMatch.Value, actualForwardMatch); + } + + // Also check Contains / StartsWith / SplitOn + + Assert.Equal(wasFound, searchSpan.Contains(searchTerm, comparison)); + Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison)); + + (var before, var after) = searchSpan.SplitOn(searchTerm, comparison); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + + // Now search backward + + wasFound = searchSpan.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch); + Assert.Equal(expectedBackwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedBackwardMatch.Value, actualBackwardMatch); + } + + // Also check EndsWith / SplitOnLast + + Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison)); + + (before, after) = searchSpan.SplitOnLast(searchTerm, comparison); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + }); + } + + [Theory] + [MemberData(nameof(TryFindData_Rune_Ordinal))] + public static void TryFind_Rune_Ordinal(ustring source, Rune searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span searchSpan = boundedSpan.Span; + source = null; // to avoid accidentally using this for the remainder of the test + + // First, search forward + + bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch); + Assert.Equal(expectedForwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedForwardMatch.Value, actualForwardMatch); + } + + // Also check Contains / StartsWith / SplitOn + + Assert.Equal(wasFound, searchSpan.Contains(searchTerm)); + Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm)); + + (var before, var after) = searchSpan.SplitOn(searchTerm); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + + // Now search backward + + wasFound = searchSpan.TryFindLast(searchTerm, out Range actualBackwardMatch); + Assert.Equal(expectedBackwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedBackwardMatch.Value, actualBackwardMatch); + } + + // Also check EndsWith / SplitOnLast + + Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm)); + + (before, after) = searchSpan.SplitOnLast(searchTerm); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [MemberData(nameof(TryFindData_Rune_WithComparison))] + public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) + { + RunOnDedicatedThread(() => + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span searchSpan = boundedSpan.Span; + source = null; // to avoid accidentally using this for the remainder of the test + + if (currentCulture != null) + { + CultureInfo.CurrentCulture = currentCulture; + } + + // First, search forward + + bool wasFound = searchSpan.TryFind(searchTerm, comparison, out Range actualForwardMatch); + Assert.Equal(expectedForwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedForwardMatch.Value, actualForwardMatch); + } + + // Also check Contains / StartsWith / SplitOn + + Assert.Equal(wasFound, searchSpan.Contains(searchTerm, comparison)); + Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison)); + + (var before, var after) = searchSpan.SplitOn(searchTerm, comparison); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + + // Now search backward + + wasFound = searchSpan.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch); + Assert.Equal(expectedBackwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedBackwardMatch.Value, actualBackwardMatch); + } + + // Also check EndsWith / SplitOnLast + + Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison)); + + (before, after) = searchSpan.SplitOnLast(searchTerm, comparison); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + }); + } + + [Theory] + [MemberData(nameof(TryFindData_Utf8Span_Ordinal))] + public static void TryFind_Utf8Span_Ordinal(ustring source, ustring searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span searchSpan = boundedSpan.Span; + source = null; // to avoid accidentally using this for the remainder of the test + + // First, search forward + + bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch); + Assert.Equal(expectedForwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedForwardMatch.Value, actualForwardMatch); + } + + // Also check Contains / StartsWith / SplitOn + + Assert.Equal(wasFound, searchSpan.Contains(searchTerm)); + Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm)); + + (var before, var after) = searchSpan.SplitOn(searchTerm); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + + // Now search backward + + wasFound = searchSpan.TryFindLast(searchTerm, out Range actualBackwardMatch); + Assert.Equal(expectedBackwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedBackwardMatch.Value, actualBackwardMatch); + } + + // Also check EndsWith / SplitOnLast + + Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm)); + + (before, after) = searchSpan.SplitOnLast(searchTerm); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [MemberData(nameof(TryFindData_Utf8Span_WithComparison))] + public static void TryFind_Utf8Span_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) + { + RunOnDedicatedThread(() => + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes()); + Utf8Span searchSpan = boundedSpan.Span; + source = null; // to avoid accidentally using this for the remainder of the test + + if (currentCulture != null) + { + CultureInfo.CurrentCulture = currentCulture; + } + + // First, search forward + + bool wasFound = searchSpan.TryFind(searchTerm, comparison, out Range actualForwardMatch); + Assert.Equal(expectedForwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedForwardMatch.Value, actualForwardMatch); + } + + // Also check Contains / StartsWith / SplitOn + + Assert.Equal(wasFound, searchSpan.Contains(searchTerm, comparison)); + Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison)); + + (var before, var after) = searchSpan.SplitOn(searchTerm, comparison); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + + // Now search backward + + wasFound = searchSpan.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch); + Assert.Equal(expectedBackwardMatch.HasValue, wasFound); + + if (wasFound) + { + AssertRangesEqual(searchSpan.Bytes.Length, expectedBackwardMatch.Value, actualBackwardMatch); + } + + // Also check EndsWith / SplitOnLast + + Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison)); + + (before, after) = searchSpan.SplitOnLast(searchTerm, comparison); + if (wasFound) + { + Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality + Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality + } + else + { + Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality + Assert.True(after.IsNull()); + } + }); + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.TestData.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.TestData.cs new file mode 100644 index 0000000..290eb2d --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.TestData.cs @@ -0,0 +1,178 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections.Generic; +using System.Linq; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public unsafe partial class Utf8SpanTests + { + /// + /// All s, U+0000..U+D800 and U+E000..U+10FFFF. + /// + private static IEnumerable AllRunes + { + get + { + for (uint i = 0; i < 0xD800; i++) + { + yield return new Rune(i); + } + for (uint i = 0xE000; i <= 0x10FFFF; i++) + { + yield return new Rune(i); + } + } + } + + /// + /// All s where returns . + /// + private static readonly Lazy WhiteSpaceRunes = new Lazy(() => AllRunes.Where(Rune.IsWhiteSpace).ToArray()); + + public static IEnumerable Trim_TestData() + { + string[] testData = new string[] + { + null, // null + "", // empty + "\0", // contains null character - shouldn't be trimmed + "Hello", // simple non-whitespace ASCII data + "\u0009Hello\u000d", // C0 whitespace characters + "\u0009\u0008\u0009Hello\u000e\u000b", // C0 whitespace + non-whitespace characters + " Hello! ", // simple space chars (plus !, since it's adjacent to U+0020 SPACE) + "\u0085\u0084\u0086\u0085", // U+0085 NEXT LINE (NEL), surrounded by adjacent non-whitespace chars + }; + + foreach (string entry in testData) + { + yield return new object[] { entry }; + } + + // A string with every possible whitespace character, just to test the limits + + StringBuilder builder = new StringBuilder(); + foreach (Rune whitespaceRune in WhiteSpaceRunes.Value) + { + builder.Append(whitespaceRune); + } + builder.Append("xyz"); + foreach (Rune whitespaceRune in WhiteSpaceRunes.Value) + { + builder.Append(whitespaceRune); + } + + yield return new object[] { builder.ToString() }; + } + + private static bool TryParseSearchTermAsChar(object searchTerm, out char parsed) + { + if (searchTerm is char ch) + { + parsed = ch; + return true; + } + else if (searchTerm is Rune r) + { + if (r.IsBmp) + { + parsed = (char)r.Value; + return true; + } + } + else if (searchTerm is string str) + { + if (str.Length == 1) + { + parsed = str[0]; + return true; + } + } + else if (searchTerm is ustring ustr) + { + var asString = ustr.ToString(); + if (asString.Length == 1) + { + parsed = asString[0]; + return true; + } + } + + parsed = default; // failed to turn the search term into a single char + return false; + } + + private static bool TryParseSearchTermAsRune(object searchTerm, out Rune parsed) + { + if (searchTerm is char ch) + { + return Rune.TryCreate(ch, out parsed); + } + else if (searchTerm is Rune r) + { + parsed = r; + return true; + } + else if (searchTerm is string str) + { + if (Rune.DecodeFromUtf16(str, out parsed, out int charsConsumed) == OperationStatus.Done + && charsConsumed == str.Length) + { + return true; + } + } + else if (searchTerm is ustring ustr) + { + if (Rune.DecodeFromUtf8(ustr.AsBytes(), out parsed, out int bytesConsumed) == OperationStatus.Done + && bytesConsumed == ustr.GetByteLength()) + { + return true; + } + } + + parsed = default; // failed to turn the search term into a single Rune + return false; + } + + private static bool TryParseSearchTermAsUtf8String(object searchTerm, out ustring parsed) + { + if (searchTerm is char ch) + { + if (Rune.TryCreate(ch, out Rune rune)) + { + parsed = rune.ToUtf8String(); + return true; + } + } + else if (searchTerm is Rune r) + { + parsed = r.ToUtf8String(); + return true; + } + else if (searchTerm is string str) + { + ustring asUtf8 = new ustring(str); + if (asUtf8.ToString() == str) // make sure this round-trips properly + { + parsed = asUtf8; + return true; + } + } + else if (searchTerm is ustring ustr) + { + parsed = ustr; + return true; + } + + parsed = default; // failed to turn the search term into a ustring + return false; + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs new file mode 100644 index 0000000..25579c3 --- /dev/null +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs @@ -0,0 +1,353 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Tests; +using Xunit; + +using static System.Tests.Utf8TestUtilities; + +using ustring = System.Utf8String; + +namespace System.Text.Tests +{ + public unsafe partial class Utf8SpanTests + { + [Fact] + public static void BytesProperty_FromCustomBytes() + { + byte[] bytes = Encoding.UTF8.GetBytes("Hello!"); + Assert.True(bytes.AsSpan() == Utf8Span.UnsafeCreateWithoutValidation(bytes).Bytes); + } + + [Fact] + public static void BytesProperty_FromEmpty() + { + Assert.True(Utf8Span.Empty.Bytes == ReadOnlySpan.Empty); + } + + [Fact] + public static void BytesProperty_FromUtf8String() + { + ustring ustr = u8("Hello!"); + Utf8Span uspan = new Utf8Span(ustr); + + Assert.True(ustr.AsBytes() == uspan.Bytes); + } + + [Fact] + public static void EmptyProperty() + { + // Act + + Utf8Span span = Utf8Span.Empty; + + // Assert + // GetPinnableReference should be 'null' to match behavior of empty ROS.GetPinnableReference(); + + Assert.True(span.IsEmpty); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference()))); + Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span.Bytes))); + Assert.Equal(0, span.Bytes.Length); + } + + [Fact] + public static void GetHashCode_Ordinal() + { + // Generate 17 all-null strings and make sure they have unique hash codes. + // Assuming Marvin32 is a good PRF and has a full 32-bit output domain, we should + // expect this test to fail only once every ~30 million runs due to the birthday paradox. + // That should be good enough for inclusion as a unit test. + + HashSet seenHashCodes = new HashSet(); + + for (int i = 0; i <= 16; i++) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(new byte[i]); + Utf8Span span = boundedSpan.Span; + + Assert.True(seenHashCodes.Add(span.GetHashCode()), "This hash code was previously seen."); + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void GetHashCode_WithComparison() + { + // Since hash code generation is randomized, it's possible (though unlikely) that + // we might see unanticipated collisions. It's ok if this unit test fails once in + // every few million runs, but if the unit test becomes truly flaky then that would + // be indicative of a larger problem with hash code generation. + // + // These tests also make sure that the hash code is computed over the buffer rather + // than over the reference. + + // Ordinal + + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ababaaAA"); + Utf8Span span = boundedSpan.Span; + + Assert.Equal(span[0..2].GetHashCode(StringComparison.Ordinal), span[2..4].GetHashCode(StringComparison.Ordinal)); + Assert.NotEqual(span[4..6].GetHashCode(StringComparison.Ordinal), span[6..8].GetHashCode(StringComparison.Ordinal)); + Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.Ordinal), span[^0..].GetHashCode(StringComparison.Ordinal)); // null should equal empty + } + + // OrdinalIgnoreCase + + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ababaaAA"); + Utf8Span span = boundedSpan.Span; + + Assert.Equal(span[0..2].GetHashCode(StringComparison.OrdinalIgnoreCase), span[2..4].GetHashCode(StringComparison.OrdinalIgnoreCase)); + Assert.Equal(span[4..6].GetHashCode(StringComparison.OrdinalIgnoreCase), span[6..8].GetHashCode(StringComparison.OrdinalIgnoreCase)); + Assert.NotEqual(span[0..2].GetHashCode(StringComparison.OrdinalIgnoreCase), span[6..8].GetHashCode(StringComparison.OrdinalIgnoreCase)); + Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.OrdinalIgnoreCase), span[^0..].GetHashCode(StringComparison.OrdinalIgnoreCase)); // null should equal empty + } + + // InvariantCulture + + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ae\u00e6AE\u00c6"); // U+00E6 = 'æ' LATIN SMALL LETTER AE, U+00E6 = 'Æ' LATIN CAPITAL LETTER AE + Utf8Span span = boundedSpan.Span; + + Assert.Equal(span[0..2].GetHashCode(StringComparison.InvariantCulture), span[2..4].GetHashCode(StringComparison.InvariantCulture)); + Assert.NotEqual(span[0..2].GetHashCode(StringComparison.InvariantCulture), span[4..6].GetHashCode(StringComparison.InvariantCulture)); + Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.InvariantCulture), span[^0..].GetHashCode(StringComparison.InvariantCulture)); // null should equal empty + } + + // InvariantCultureIgnoreCase + + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ae\u00e6AE\u00c6EA"); + Utf8Span span = boundedSpan.Span; + + Assert.Equal(span[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[2..4].GetHashCode(StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(span[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[6..8].GetHashCode(StringComparison.InvariantCultureIgnoreCase)); + Assert.NotEqual(span[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[8..10].GetHashCode(StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[^0..].GetHashCode(StringComparison.InvariantCultureIgnoreCase)); // null should equal empty + } + + // Invariant culture should not match Turkish I case conversion + + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("i\u0130"); // U+0130 = 'İ' LATIN CAPITAL LETTER I WITH DOT ABOVE + Utf8Span span = boundedSpan.Span; + + Assert.NotEqual(span[0..1].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[1..3].GetHashCode(StringComparison.InvariantCultureIgnoreCase)); + } + + // CurrentCulture (we'll use tr-TR) + + RunOnDedicatedThread(() => + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("i\u0131\u0130Ii\u0131\u0130I"); // U+0131 = 'ı' LATIN SMALL LETTER DOTLESS I + Utf8Span span = boundedSpan.Span; + + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR"); + + Assert.Equal(span[0..6].GetHashCode(StringComparison.CurrentCulture), span[6..12].GetHashCode(StringComparison.CurrentCulture)); + Assert.NotEqual(span[0..1].GetHashCode(StringComparison.CurrentCulture), span[1..3].GetHashCode(StringComparison.CurrentCulture)); + Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.CurrentCulture), span[^0..].GetHashCode(StringComparison.CurrentCulture)); // null should equal empty + }); + + // CurrentCultureIgnoreCase (we'll use tr-TR) + + RunOnDedicatedThread(() => + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("i\u0131\u0130Ii\u0131\u0130I"); + Utf8Span span = boundedSpan.Span; + + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR"); + + Assert.Equal(span[0..6].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[6..12].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); + Assert.NotEqual(span[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[1..3].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' shouldn't match 'ı' + Assert.Equal(span[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[3..5].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' should match 'İ' + Assert.NotEqual(span[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[5..6].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' shouldn't match 'I' + Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[^0..].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // null should equal empty + }); + } + + [Theory] + [InlineData("", true)] + [InlineData("Hello", true)] + [InlineData("\u1234", false)] + public static void IsAscii(string input, bool expected) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + + Assert.Equal(expected, boundedSpan.Span.IsAscii()); + } + + [Theory] + [InlineData(null, true)] + [InlineData("", true)] + [InlineData(" \u2028\u2029\t\v", true)] + [InlineData(" x\r\n", false)] + [InlineData("\r\nhello\r\n", false)] + [InlineData("\r\n\0\r\n", false)] + [InlineData("\r\n\r\n", true)] + public static void IsEmptyOrWhiteSpace(string input, bool expected) + { + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + + Assert.Equal(expected, boundedSpan.Span.IsEmptyOrWhiteSpace()); + } + + [Theory] + [InlineData("", "..")] + [InlineData("Hello", "1..")] + [InlineData("Hello", "1..2")] + [InlineData("Hello", "1..^2")] + [InlineData("Hello", "^2..")] + [InlineData("Hello", "^0..")] + [InlineData("résumé", "1..^2")] // include first 'é', exclude last 'é' + [InlineData("résumé", "^2..")] // include only last 'é' + public static void Indexer_Success(string input, string rangeExpression) + { + Range range = ParseRangeExpr(rangeExpression); + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + Utf8Span originalSpan = boundedSpan.Span; + Utf8Span slicedSpan = originalSpan[range]; // shouldn't throw + + ref byte startOfOriginalSpan = ref MemoryMarshal.GetReference(originalSpan.Bytes); + ref byte startOfSlicedSpan = ref MemoryMarshal.GetReference(slicedSpan.Bytes); + + // Now ensure the slice was correctly produced by comparing the references directly. + + (int offset, int length) = range.GetOffsetAndLength(originalSpan.Bytes.Length); + Assert.True(Unsafe.AreSame(ref startOfSlicedSpan, ref Unsafe.Add(ref startOfOriginalSpan, offset))); + Assert.Equal(length, slicedSpan.Bytes.Length); + } + + [Theory] + [InlineData("résumé", "2..")] // try to split the first 'é' + [InlineData("résumé", "..^1")] // try to split the last 'é' + public static void Indexer_ThrowsIfTryToSplitMultiByteSubsequence(string input, string rangeExpression) + { + Range range = ParseRangeExpr(rangeExpression); + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input); + + Assert.Throws(() => { var _ = boundedSpan.Span[range]; }); + } + + + [Theory] + [MemberData(nameof(TranscodingTestData))] + public static void ToCharArrayTest(string expected) + { + // Arrange + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected); + Utf8Span span = boundedSpan.Span; + + // Act + + char[] returned = span.ToCharArray(); + + // Assert + + Assert.Equal(expected, returned); // IEnumerable + } + + [Fact] + public static void ToCharArrayTest_Null() + { + Assert.Same(Array.Empty(), Utf8Span.Empty.ToCharArray()); + } + + [Theory] + [MemberData(nameof(TranscodingTestData))] + public static void ToCharsTest(string expected) + { + // Arrange + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected); + Utf8Span span = boundedSpan.Span; + + // Act & assert, first with improperly-sized buffer + + if (expected.Length > 0) + { + using BoundedMemory boundedMemory = BoundedMemory.Allocate(expected.Length - 1); + Assert.Equal(-1, span.ToChars(boundedMemory.Span)); + } + + // Then with properly-sized buffer and too-large buffer + + for (int i = expected.Length; i <= expected.Length + 1; i++) + { + using BoundedMemory boundedMemory = BoundedMemory.Allocate(i); + Assert.Equal(expected.Length, span.ToChars(boundedMemory.Span)); + Assert.True(boundedMemory.Span.Slice(0, expected.Length).SequenceEqual(expected)); + } + } + + [Fact] + public static void ToCharsTest_Null() + { + for (int i = 0; i <= 1; i++) // test both with properly-sized buffer and with too-large buffer + { + using BoundedMemory boundedMemory = BoundedMemory.Allocate(i); + Assert.Equal(0, Utf8Span.Empty.ToChars(boundedMemory.Span)); + } + } + + [Theory] + [MemberData(nameof(TranscodingTestData))] + public static void ToStringTest(string expected) + { + // Arrange + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected); + Utf8Span span = boundedSpan.Span; + + // Act & assert + + Assert.Equal(expected, span.ToString()); + } + + [Fact] + public static void ToStringTest_Null() + { + Assert.Same(string.Empty, Utf8Span.Empty.ToString()); + } + + [Theory] + [MemberData(nameof(TranscodingTestData))] + public static void ToUtf8StringTest(string expected) + { + // Arrange + + ustring utf8 = u8(expected); + + using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected); + Utf8Span span = boundedSpan.Span; + + // Act & assert + + Assert.Equal(utf8, span.ToUtf8String()); + } + + [Fact] + public static void ToUtf8StringTest_Null() + { + Assert.Same(ustring.Empty, Utf8Span.Empty.ToUtf8String()); + } + + public static IEnumerable TranscodingTestData() + { + yield return new object[] { "" }; // empty + yield return new object[] { "Hello" }; // simple ASCII + yield return new object[] { "a\U00000123b\U00001234c\U00101234d" }; // with multi-byte sequences of varying lengths + yield return new object[] { "\uF8FF\uE000\U000FFFFF" }; // with scalars from the private use areas + } + } +} diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8TestUtilities.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8TestUtilities.cs index 1ea63d1..96fe60f 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8TestUtilities.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8TestUtilities.cs @@ -2,9 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; using System.Text; +using System.Threading; using Xunit; namespace System.Tests @@ -23,6 +28,110 @@ namespace System.Tests }); } + public static int GetByteLength(this Utf8String value) + { + return value.AsBytes().Length; + } + + public unsafe static bool IsNull(this Utf8Span span) + { + return Unsafe.AreSame(ref Unsafe.AsRef(null), ref MemoryMarshal.GetReference(span.Bytes)); + } + + /// + /// Parses an expression of the form "a..b" and returns a . + /// + public static Range ParseRangeExpr(ReadOnlySpan expression) + { + int idxOfDots = expression.IndexOf("..", StringComparison.Ordinal); + if (idxOfDots < 0) + { + goto Error; + } + + ReadOnlySpan firstPart = expression[..idxOfDots].Trim(); + Index firstIndex = Index.Start; + + if (!firstPart.IsWhiteSpace()) + { + bool fromEnd = false; + + if (!firstPart.IsEmpty && firstPart[0] == '^') + { + fromEnd = true; + firstPart = firstPart[1..]; + } + + if (!int.TryParse(firstPart, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out int startIndex)) + { + goto Error; + } + + firstIndex = new Index(startIndex, fromEnd); + } + + ReadOnlySpan secondPart = expression[(idxOfDots + 2)..].Trim(); + Index secondIndex = Index.End; + + if (!secondPart.IsWhiteSpace()) + { + bool fromEnd = false; + + if (!secondPart.IsEmpty && secondPart[0] == '^') + { + fromEnd = true; + secondPart = secondPart[1..]; + } + + if (!int.TryParse(secondPart, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out int endIndex)) + { + goto Error; + } + + secondIndex = new Index(endIndex, fromEnd); + } + + return new Range(firstIndex, secondIndex); + + Error: + throw new ArgumentException($"Range expression '{expression.ToString()}' is invalid."); + } + + public static void AssertRangesEqual(int originalLength, Range expected, Range actual) + { + Assert.Equal(expected, actual, new RangeEqualityComparer(originalLength)); + } + + /// + /// Runs this test on its own dedicated thread; allows for setting CurrentCulture and other thread-statics. + /// + /// + public static void RunOnDedicatedThread(Action testCode) + { + Assert.NotNull(testCode); + + ExceptionDispatchInfo edi = default; + Thread newThread = new Thread(() => + { + try + { + testCode(); + } + catch (Exception ex) + { + edi = ExceptionDispatchInfo.Capture(ex); + } + }); + + newThread.Start(); + newThread.Join(); + + if (edi != null) + { + edi.Throw(); + } + } + /// /// Mimics returning a literal instance. /// @@ -68,10 +177,36 @@ namespace System.Tests Utf8String newUtf8String = _utf8StringFactory.Value(buffer.Count); fixed (byte* pNewUtf8String = newUtf8String) { - buffer.AsSpan().CopyTo(new Span(pNewUtf8String, newUtf8String.Length)); + buffer.AsSpan().CopyTo(new Span(pNewUtf8String, newUtf8String.GetByteLength())); } return newUtf8String; } + + public unsafe static Range GetRangeOfSubspan(ReadOnlySpan outerSpan, ReadOnlySpan innerSpan) + { + ulong byteOffset = (ulong)(void*)Unsafe.ByteOffset(ref MemoryMarshal.GetReference(outerSpan), ref MemoryMarshal.GetReference(innerSpan)); + ulong elementOffset = byteOffset / (uint)Unsafe.SizeOf(); + + checked + { + int elementOffsetAsInt = (int)elementOffset; + Range retVal = elementOffsetAsInt..(elementOffsetAsInt + innerSpan.Length); + + _ = outerSpan[retVal]; // call the real slice logic to make sure we're really within the outer span + return retVal; + } + } + + public static Range GetRangeOfSubspan(Utf8Span outerSpan, Utf8Span innerSpan) + { + return GetRangeOfSubspan(outerSpan.Bytes, innerSpan.Bytes); + } + + public static bool IsEmpty(this Range range, int length) + { + (_, int actualLength) = range.GetOffsetAndLength(length); + return (actualLength == 0); + } } } -- 2.7.4