diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 53d3da5e0441c2..2aea6f21fe34a1 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -277,6 +277,7 @@ public static partial class MemoryExtensions public static bool ContainsAnyInRange(this System.ReadOnlySpan span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static bool ContainsAnyInRange(this System.Span span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } + public static bool ContainsAnyWhiteSpace(this System.ReadOnlySpan span) { throw null; } public static void CopyTo(this T[]? source, System.Memory destination) { } public static void CopyTo(this T[]? source, System.Span destination) { } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] @@ -331,6 +332,7 @@ public static void CopyTo(this T[]? source, System.Span destination) { } public static int IndexOfAnyExceptInRange(this System.ReadOnlySpan span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int IndexOfAnyExceptInRange(this System.Span span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } + public static int IndexOfAnyExceptWhiteSpace(this System.ReadOnlySpan span) { throw null; } public static int IndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable? { throw null; } public static int IndexOf(this System.ReadOnlySpan span, T value) where T : System.IEquatable? { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] @@ -340,6 +342,7 @@ public static void CopyTo(this T[]? source, System.Span destination) { } public static int IndexOfAnyInRange(this System.ReadOnlySpan span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int IndexOfAnyInRange(this System.Span span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } + public static int IndexOfAnyWhiteSpace(this System.ReadOnlySpan span) { throw null; } public static bool IsWhiteSpace(this System.ReadOnlySpan span) { throw null; } public static int LastIndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value, System.StringComparison comparisonType) { throw null; } public static int LastIndexOfAny(this System.ReadOnlySpan span, System.Buffers.SearchValues values) where T : System.IEquatable? { throw null; } @@ -372,6 +375,7 @@ public static void CopyTo(this T[]? source, System.Span destination) { } public static int LastIndexOfAnyExceptInRange(this System.ReadOnlySpan span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int LastIndexOfAnyExceptInRange(this System.Span span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } + public static int LastIndexOfAnyExceptWhiteSpace(this System.ReadOnlySpan span) { throw null; } public static int LastIndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable? { throw null; } public static int LastIndexOf(this System.ReadOnlySpan span, T value) where T : System.IEquatable? { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] @@ -381,6 +385,7 @@ public static void CopyTo(this T[]? source, System.Span destination) { } public static int LastIndexOfAnyInRange(this System.ReadOnlySpan span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int LastIndexOfAnyInRange(this System.Span span, T lowInclusive, T highInclusive) where T : System.IComparable { throw null; } + public static int LastIndexOfAnyWhiteSpace(this System.ReadOnlySpan span) { throw null; } public static bool Overlaps(this System.ReadOnlySpan span, System.ReadOnlySpan other) { throw null; } public static bool Overlaps(this System.ReadOnlySpan span, System.ReadOnlySpan other, out int elementOffset) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/WhiteSpace.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/WhiteSpace.cs new file mode 100644 index 00000000000000..09eb02983cf497 --- /dev/null +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/WhiteSpace.cs @@ -0,0 +1,261 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void IsWhiteSpace_True() + { + Assert.True(Span.Empty.IsWhiteSpace()); + + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + Assert.True(CollectionsMarshal.AsSpan(chars).IsWhiteSpace()); + } + + [Fact] + public static void IsWhiteSpace_False() + { + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + var index = chars.Count; + chars.AddRange(chars.ToArray()); + chars.Insert(index, ' '); + var span = CollectionsMarshal.AsSpan(chars); + + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + { + chars[index] = (char)i; + Assert.False(span.IsWhiteSpace()); + } + } + } + + [Fact] + public static void ContainsAnyWhiteSpace_Found() + { + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + var index = chars.Count; + chars.AddRange(chars.ToArray()); + chars.Insert(index, ' '); + var span = CollectionsMarshal.AsSpan(chars); + + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + { + chars[index] = (char)i; + Assert.True(span.ContainsAnyWhiteSpace()); + } + } + } + + [Fact] + public static void ContainsAnyWhiteSpace_NotFound() + { + Assert.False(Span.Empty.ContainsAnyWhiteSpace()); + + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + Assert.False(CollectionsMarshal.AsSpan(chars).ContainsAnyWhiteSpace()); + } + + [Fact] + public static void IndexOfAnyWhiteSpace_Found() + { + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + var index = chars.Count; + chars.AddRange(chars.ToArray()); + chars.Insert(index, ' '); + chars.Insert(index, ' '); + var span = CollectionsMarshal.AsSpan(chars); + + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + { + chars[index] = (char)i; + chars[index + 1] = (char)i; + Assert.Equal(index, span.IndexOfAnyWhiteSpace()); + } + } + } + + [Fact] + public static void IndexOfAnyWhiteSpace_NotFound() + { + Assert.Equal(-1, Span.Empty.IndexOfAnyWhiteSpace()); + + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + Assert.Equal(-1, CollectionsMarshal.AsSpan(chars).IndexOfAnyWhiteSpace()); + } + + [Fact] + public static void IndexOfAnyExceptWhiteSpace_Found() + { + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + var index = chars.Count; + chars.AddRange(chars.ToArray()); + chars.Insert(index, ' '); + chars.Insert(index, ' '); + var span = CollectionsMarshal.AsSpan(chars); + + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + { + chars[index] = (char)i; + chars[index + 1] = (char)i; + Assert.Equal(index, span.IndexOfAnyExceptWhiteSpace()); + } + } + } + + [Fact] + public static void IndexOfAnyExceptWhiteSpace_NotFound() + { + Assert.Equal(-1, Span.Empty.IndexOfAnyExceptWhiteSpace()); + + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + Assert.Equal(-1, CollectionsMarshal.AsSpan(chars).IndexOfAnyExceptWhiteSpace()); + } + + [Fact] + public static void LastIndexOfAnyWhiteSpace_Found() + { + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + var index = chars.Count; + chars.AddRange(chars.ToArray()); + chars.Insert(index, ' '); + chars.Insert(index, ' '); + var span = CollectionsMarshal.AsSpan(chars); + + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + { + chars[index] = (char)i; + chars[index + 1] = (char)i; + Assert.Equal(index + 1, span.LastIndexOfAnyWhiteSpace()); + } + } + } + + [Fact] + public static void LastIndexOfAnyWhiteSpace_NotFound() + { + Assert.Equal(-1, Span.Empty.LastIndexOfAnyWhiteSpace()); + + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + Assert.Equal(-1, CollectionsMarshal.AsSpan(chars).LastIndexOfAnyWhiteSpace()); + } + + [Fact] + public static void LastIndexOfAnyExceptWhiteSpace_Found() + { + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + var index = chars.Count; + chars.AddRange(chars.ToArray()); + chars.Insert(index, ' '); + chars.Insert(index, ' '); + var span = CollectionsMarshal.AsSpan(chars); + + for (int i = 0; i <= char.MaxValue; i++) + { + if (!char.IsWhiteSpace((char)i)) + { + chars[index] = (char)i; + chars[index + 1] = (char)i; + Assert.Equal(index + 1, span.LastIndexOfAnyExceptWhiteSpace()); + } + } + } + + [Fact] + public static void LastIndexOfAnyExceptWhiteSpace_NotFound() + { + Assert.Equal(-1, Span.Empty.LastIndexOfAnyExceptWhiteSpace()); + + List chars = []; + for (int i = 0; i <= char.MaxValue; i++) + { + if (char.IsWhiteSpace((char)i)) + chars.Add((char)i); + } + + Assert.Equal(-1, CollectionsMarshal.AsSpan(chars).LastIndexOfAnyExceptWhiteSpace()); + } + } +} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index 38b40b284d1b25..933d3f34d31970 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -193,6 +193,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs index 5b64d503b836b8..71cd0bcb2e0ee3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs @@ -14,15 +14,9 @@ public static partial class MemoryExtensions /// /// Indicates whether the specified span contains only white-space characters. /// - public static bool IsWhiteSpace(this ReadOnlySpan span) - { - for (int i = 0; i < span.Length; i++) - { - if (!char.IsWhiteSpace(span[i])) - return false; - } - return true; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWhiteSpace(this ReadOnlySpan span) => + !string.SearchValuesStorage.WhiteSpaceChars.ContainsAnyExcept(span); /// /// Returns a value indicating whether the specified occurs within the . @@ -35,6 +29,16 @@ public static bool Contains(this ReadOnlySpan span, ReadOnlySpan val return IndexOf(span, value, comparisonType) >= 0; } + /// + /// Returns a value indicating whether the specified span contains any + /// white-space characters, and returns if found. If not found, returns + /// . + /// + /// The source span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ContainsAnyWhiteSpace(this ReadOnlySpan span) => + string.SearchValuesStorage.WhiteSpaceChars.ContainsAny(span); + /// /// Determines whether this and the specified span have the same characters /// when compared using the specified option. @@ -149,6 +153,24 @@ public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value } } + /// + /// Reports the zero-based index of the first occurrence of any white-space + /// characters in the current , or -1 if not founded. + /// + /// The source span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAnyWhiteSpace(this ReadOnlySpan span) => + string.SearchValuesStorage.WhiteSpaceChars.IndexOfAny(span); + + /// + /// Reports the zero-based index of the first occurrence of any + /// non-white-space characters in the current , or -1 if not founded. + /// + /// The source span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAnyExceptWhiteSpace(this ReadOnlySpan span) => + string.SearchValuesStorage.WhiteSpaceChars.IndexOfAnyExcept(span); + /// /// Reports the zero-based index of the last occurrence of the specified in the current . /// @@ -184,6 +206,24 @@ ref MemoryMarshal.GetReference(value), } } + /// + /// Reports the zero-based index of the last occurrence of any white-space + /// characters in the current , or -1 if not founded. + /// + /// The source span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAnyWhiteSpace(this ReadOnlySpan span) => + string.SearchValuesStorage.WhiteSpaceChars.LastIndexOfAny(span); + + /// + /// Reports the zero-based index of the last occurrence of any + /// non-white-space characters in the current , or -1 if not founded. + /// + /// The source span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAnyExceptWhiteSpace(this ReadOnlySpan span) => + string.SearchValuesStorage.WhiteSpaceChars.LastIndexOfAnyExcept(span); + /// /// Copies the characters from the source span into the destination, converting each character to lowercase, /// using the casing rules of the specified culture.