< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 54
Coverable lines: 54
Total lines: 231
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 30
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/StringInfo.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics.CodeAnalysis;
 6using System.Text.Unicode;
 7
 8namespace System.Globalization
 9{
 10    /// <summary>
 11    /// This class defines behaviors specific to a writing system.
 12    /// A writing system is the collection of scripts and orthographic rules
 13    /// required to represent a language as text.
 14    /// </summary>
 15    public class StringInfo
 16    {
 17        private string _str;
 18
 19        private int[]? _indexes;
 20
 021        public StringInfo() : this(string.Empty)
 22        {
 023        }
 24
 025        public StringInfo(string value)
 26        {
 027            this.String = value;
 028        }
 29
 30        public override bool Equals([NotNullWhen(true)] object? value)
 31        {
 032            return value is StringInfo otherStringInfo
 033                && _str.Equals(otherStringInfo._str);
 34        }
 35
 036        public override int GetHashCode() => _str.GetHashCode();
 37
 38        /// <summary>
 39        /// Our zero-based array of index values into the string. Initialize if
 40        /// our private array is not yet, in fact, initialized.
 41        /// </summary>
 42        private int[]? Indexes
 43        {
 44            get
 45            {
 046                if (_indexes == null && String.Length > 0)
 47                {
 048                    _indexes = ParseCombiningCharacters(String);
 49                }
 50
 051                return _indexes;
 52            }
 53        }
 54
 55        public string String
 56        {
 057            get => _str;
 58            [MemberNotNull(nameof(_str))]
 59            set
 60            {
 061                ArgumentNullException.ThrowIfNull(value);
 062                _str = value;
 063                _indexes = null;
 064            }
 65        }
 66
 067        public int LengthInTextElements => Indexes?.Length ?? 0;
 68
 69        public string SubstringByTextElements(int startingTextElement)
 70        {
 071            return SubstringByTextElements(startingTextElement, (Indexes?.Length ?? 0) - startingTextElement);
 72        }
 73
 74        public string SubstringByTextElements(int startingTextElement, int lengthInTextElements)
 75        {
 076            int[] indexes = Indexes ?? [];
 77
 078            if ((uint)startingTextElement >= (uint)indexes.Length)
 79            {
 080                throw new ArgumentOutOfRangeException(nameof(startingTextElement), startingTextElement, SR.Arg_ArgumentO
 81            }
 082            if ((uint)lengthInTextElements > (uint)(indexes.Length - startingTextElement))
 83            {
 084                throw new ArgumentOutOfRangeException(nameof(lengthInTextElements), lengthInTextElements, SR.Arg_Argumen
 85            }
 86
 087            int start = indexes[startingTextElement];
 088            Index end = ^0; // assume reading to end of the string unless the caller told us to stop early
 89
 090            if ((uint)(startingTextElement + lengthInTextElements) < (uint)indexes.Length)
 91            {
 092                end = indexes[startingTextElement + lengthInTextElements];
 93            }
 94
 095            return String[start..end];
 96        }
 97
 98        /// <summary>
 99        /// Returns the first text element (extended grapheme cluster) that occurs in the input string.
 100        /// </summary>
 101        /// <remarks>
 102        /// A grapheme cluster is a sequence of one or more Unicode code points that should be treated as a single unit.
 103        /// </remarks>
 104        /// <param name="str">The input string to analyze.</param>
 105        /// <returns>The substring corresponding to the first text element within <paramref name="str"/>,
 106        /// or the empty string if <paramref name="str"/> is empty.</returns>
 107        /// <exception cref="ArgumentNullException"><paramref name="str"/> is null.</exception>
 0108        public static string GetNextTextElement(string str) => GetNextTextElement(str, 0);
 109
 110        /// <summary>
 111        /// Returns the first text element (extended grapheme cluster) that occurs in the input string
 112        /// starting at the specified index.
 113        /// </summary>
 114        /// <remarks>
 115        /// A grapheme cluster is a sequence of one or more Unicode code points that should be treated as a single unit.
 116        /// </remarks>
 117        /// <param name="str">The input string to analyze.</param>
 118        /// <param name="index">The char offset in <paramref name="str"/> at which to begin analysis.</param>
 119        /// <returns>The substring corresponding to the first text element within <paramref name="str"/> starting
 120        /// at index <paramref name="index"/>, or the empty string if <paramref name="index"/> corresponds to
 121        /// the end of <paramref name="str"/>.</returns>
 122        /// <exception cref="ArgumentNullException"><paramref name="str"/> is null.</exception>
 123        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is negative or beyond the end of <par
 124        public static string GetNextTextElement(string str, int index)
 125        {
 0126            int nextTextElementLength = GetNextTextElementLength(str, index);
 0127            return str.Substring(index, nextTextElementLength);
 128        }
 129
 130        /// <summary>
 131        /// Returns the length of the first text element (extended grapheme cluster) that occurs in the input string.
 132        /// </summary>
 133        /// <remarks>
 134        /// A grapheme cluster is a sequence of one or more Unicode code points that should be treated as a single unit.
 135        /// </remarks>
 136        /// <param name="str">The input string to analyze.</param>
 137        /// <returns>The length (in chars) of the substring corresponding to the first text element within <paramref nam
 138        /// or 0 if <paramref name="str"/> is empty.</returns>
 139        /// <exception cref="ArgumentNullException"><paramref name="str"/> is null.</exception>
 0140        public static int GetNextTextElementLength(string str) => GetNextTextElementLength(str, 0);
 141
 142        /// <summary>
 143        /// Returns the length of the first text element (extended grapheme cluster) that occurs in the input string
 144        /// starting at the specified index.
 145        /// </summary>
 146        /// <remarks>
 147        /// A grapheme cluster is a sequence of one or more Unicode code points that should be treated as a single unit.
 148        /// </remarks>
 149        /// <param name="str">The input string to analyze.</param>
 150        /// <param name="index">The char offset in <paramref name="str"/> at which to begin analysis.</param>
 151        /// <returns>The length (in chars) of the substring corresponding to the first text element within <paramref nam
 152        /// at index <paramref name="index"/>, or 0 if <paramref name="index"/> corresponds to the end of <paramref name
 153        /// <exception cref="ArgumentNullException"><paramref name="str"/> is null.</exception>
 154        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is negative or beyond the end of <par
 155        public static int GetNextTextElementLength(string str, int index)
 156        {
 0157            if (str is null)
 158            {
 0159                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.str);
 160            }
 0161            if ((uint)index > (uint)str.Length)
 162            {
 0163                ThrowHelper.ThrowArgumentOutOfRange_IndexMustBeLessOrEqualException();
 164            }
 165
 0166            return GetNextTextElementLength(str.AsSpan(index));
 167        }
 168
 169        /// <summary>
 170        /// Returns the length of the first text element (extended grapheme cluster) that occurs in the input span.
 171        /// </summary>
 172        /// <remarks>
 173        /// A grapheme cluster is a sequence of one or more Unicode code points that should be treated as a single unit.
 174        /// </remarks>
 175        /// <param name="str">The input span to analyze.</param>
 176        /// <returns>The length (in chars) of the substring corresponding to the first text element within <paramref nam
 177        /// or 0 if <paramref name="str"/> is empty.</returns>
 0178        public static int GetNextTextElementLength(ReadOnlySpan<char> str) => TextSegmentationUtility.GetLengthOfFirstUt
 179
 0180        public static TextElementEnumerator GetTextElementEnumerator(string str) => GetTextElementEnumerator(str, 0);
 181
 182        public static TextElementEnumerator GetTextElementEnumerator(string str, int index)
 183        {
 0184            if (str is null)
 185            {
 0186                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.str);
 187            }
 0188            if ((uint)index > (uint)str.Length)
 189            {
 0190                ThrowHelper.ThrowArgumentOutOfRange_IndexMustBeLessOrEqualException();
 191            }
 192
 0193            return new TextElementEnumerator(str, index);
 194        }
 195
 196        /// <summary>
 197        /// Returns the indices of each base character or properly formed surrogate
 198        /// pair  within the str. It recognizes a base character plus one or more
 199        /// combining characters or a properly formed surrogate pair as a text
 200        /// element and returns the index of the base character or high surrogate.
 201        /// Each index is the beginning of a text element within a str. The length
 202        /// of each element is easily computed as the difference between successive
 203        /// indices. The length of the array will always be less than or equal to
 204        /// the length of the str. For example, given the str
 205        /// \u4f00\u302a\ud800\udc00\u4f01, this method would return the indices:
 206        /// 0, 2, 4.
 207        /// </summary>
 208        public static unsafe int[] ParseCombiningCharacters(string str)
 209        {
 0210            if (str is null)
 211            {
 0212                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.str);
 213            }
 214
 0215            ValueListBuilder<int> builder = new ValueListBuilder<int>(stackalloc int[64]); // 64 arbitrarily chosen
 0216            ReadOnlySpan<char> remaining = str;
 217
 0218            while (!remaining.IsEmpty)
 219            {
 0220                builder.Append(str.Length - remaining.Length); // a new extended grapheme cluster begins at this offset
 0221                remaining = remaining.Slice(GetNextTextElementLength(remaining)); // consume this cluster
 222            }
 223
 0224            int[] retVal = builder.AsSpan().ToArray();
 0225            builder.Dispose();
 226
 0227            return retVal;
 228        }
 229    }
 230}
 231