From: Levi Broderick Date: Mon, 19 Nov 2018 23:07:08 +0000 (-0800) Subject: Add EnumerateRunes() ref APIs and unit tests (dotnet/corefxdotnet/coreclr#33504) X-Git-Tag: submit/tizen/20210909.063632~11030^2~3202 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=587de700305b59c2b251f396db586899517de3fc;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Add EnumerateRunes() ref APIs and unit tests (dotnet/corefxdotnet/coreclr#33504) Signed-off-by: dotnet-bot Commit migrated from https://github.com/dotnet/coreclr/commit/dade3835822d72b57e880e1eba79abb4ccfe9b28 --- diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Fast.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Fast.cs index 11980fb..51ebd8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Fast.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Fast.cs @@ -2,10 +2,12 @@ // 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.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Xunit; using Internal.Runtime.CompilerServices; @@ -380,6 +382,58 @@ namespace System CultureInfo.CurrentCulture.CompareInfo.IsPrefix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType)); } + [Theory] + [InlineData(new char[0], new int[0])] // empty + [InlineData(new char[] { 'x', 'y', 'z' }, new int[] { 'x', 'y', 'z' })] + [InlineData(new char[] { 'x', '\uD86D', '\uDF54', 'y' }, new int[] { 'x', 0x2B754, 'y' })] // valid surrogate pair + [InlineData(new char[] { 'x', '\uD86D', 'y' }, new int[] { 'x', 0xFFFD, 'y' })] // standalone high surrogate + [InlineData(new char[] { 'x', '\uDF54', 'y' }, new int[] { 'x', 0xFFFD, 'y' })] // standalone low surrogate + [InlineData(new char[] { 'x', '\uD86D' }, new int[] { 'x', 0xFFFD })] // standalone high surrogate at end of string + [InlineData(new char[] { 'x', '\uDF54' }, new int[] { 'x', 0xFFFD })] // standalone low surrogate at end of string + [InlineData(new char[] { 'x', '\uD86D', '\uD86D', 'y' }, new int[] { 'x', 0xFFFD, 0xFFFD, 'y' })] // two high surrogates should be two replacement chars + [InlineData(new char[] { 'x', '\uFFFD', 'y' }, new int[] { 'x', 0xFFFD, 'y' })] // literal U+FFFD + public static void EnumerateRunes(char[] chars, int[] expected) + { + // Test data is smuggled as char[] instead of straight-up string since the test framework + // doesn't like invalid UTF-16 literals. + + // first, test Span + + List enumeratedValues = new List(); + foreach (Rune rune in ((Span)chars).EnumerateRunes()) + { + enumeratedValues.Add(rune.Value); + } + Assert.Equal(expected, enumeratedValues.ToArray()); + + + // next, ROS + + enumeratedValues.Clear(); + foreach (Rune rune in ((ReadOnlySpan)chars).EnumerateRunes()) + { + enumeratedValues.Add(rune.Value); + } + Assert.Equal(expected, enumeratedValues.ToArray()); + } + + [Fact] + public static void EnumerateRunes_DoesNotReadPastEndOfSpan(char[] chars, int[] expected) + { + // As an optimization, reading scalars from a string *may* read past the end of the string + // to the terminating null. This optimization is invalid for arbitrary spans, so this test + // ensures that we're not performing this optimization here. + + ReadOnlySpan span = "xy\U0002B754z".AsSpan(1, 2); // well-formed string, but span splits surrogate pair + + List enumeratedValues = new List(); + foreach (Rune rune in span.EnumerateRunes()) + { + enumeratedValues.Add(rune.Value); + } + Assert.Equal(new int[] { 'y', '\uFFFD' }, enumeratedValues.ToArray()); + } + /// /// Creates a new span over the portion of the target array. ///