< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 158
Coverable lines: 158
Total lines: 348
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 112
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
ToLower(...)100%110%
ToUpper(...)100%110%
ToLower(...)0%18180%
ToUpper(...)0%18180%
ToUpper(...)0%880%
ToLower(...)0%880%
GetScalar(...)0%660%
CompareStringIgnoreCase(...)0%660%
IndexOfIgnoreCase(...)0%24240%
LastIndexOfIgnoreCase(...)0%24240%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.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.Diagnostics;
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7using System.Text;
 8
 9namespace System.Globalization
 10{
 11    internal static class InvariantModeCasing
 12    {
 13        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 014        internal static char ToLower(char c) => CharUnicodeInfo.ToLower(c);
 15
 16        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 017        internal static char ToUpper(char c) => CharUnicodeInfo.ToUpper(c);
 18
 19        internal static string ToLower(string s)
 20        {
 021            if (s.Length == 0)
 22            {
 023                return string.Empty;
 24            }
 25
 026            ReadOnlySpan<char> source = s;
 27
 028            int i = 0;
 029            while (i < s.Length)
 30            {
 031                if (char.IsHighSurrogate(source[i]) && i < s.Length - 1 && char.IsLowSurrogate(source[i + 1]))
 32                {
 033                    SurrogateCasing.ToLower(source[i], source[i + 1], out char h, out char l);
 034                    if (source[i] != h || source[i + 1] != l)
 35                    {
 36                        break;
 37                    }
 38
 039                    i += 2;
 040                    continue;
 41                }
 42
 043                if (ToLower(source[i]) != source[i])
 44                {
 45                    break;
 46                }
 47
 048                i++;
 49            }
 50
 051            if (i >= s.Length)
 52            {
 053                return s;
 54            }
 55
 056            string result = string.FastAllocateString(s.Length);
 057            var destination = new Span<char>(ref result.GetRawStringData(), result.Length);
 058            ReadOnlySpan<char> src = s;
 059            src.Slice(0, i).CopyTo(destination);
 060            ToLower(src.Slice(i), destination.Slice(i));
 61
 062            return result;
 63        }
 64
 65        internal static string ToUpper(string s)
 66        {
 067            if (s.Length == 0)
 68            {
 069                return string.Empty;
 70            }
 71
 072            ReadOnlySpan<char> source = s;
 73
 074            int i = 0;
 075            while (i < s.Length)
 76            {
 077                if (char.IsHighSurrogate(source[i]) && i < s.Length - 1 && char.IsLowSurrogate(source[i + 1]))
 78                {
 079                    SurrogateCasing.ToUpper(source[i], source[i + 1], out char h, out char l);
 080                    if (source[i] != h || source[i + 1] != l)
 81                    {
 82                        break;
 83                    }
 84
 085                    i += 2;
 086                    continue;
 87                }
 88
 089                if (ToUpper(source[i]) != source[i])
 90                {
 91                    break;
 92                }
 93
 094                i++;
 95            }
 96
 097            if (i >= s.Length)
 98            {
 099                return s;
 100            }
 101
 0102            string result = string.FastAllocateString(s.Length);
 0103            var destination = new Span<char>(ref result.GetRawStringData(), result.Length);
 0104            ReadOnlySpan<char> src = s;
 0105            src.Slice(0, i).CopyTo(destination);
 0106            ToUpper(src.Slice(i), destination.Slice(i));
 107
 0108            return result;
 109        }
 110
 111        internal static void ToUpper(ReadOnlySpan<char> source, Span<char> destination)
 112        {
 0113            Debug.Assert(GlobalizationMode.Invariant);
 0114            Debug.Assert(source.Length <= destination.Length);
 115
 0116            for (int i = 0; i < source.Length; i++)
 117            {
 0118                char c = source[i];
 0119                if (char.IsHighSurrogate(c) && i < source.Length - 1)
 120                {
 0121                    char cl = source[i + 1];
 0122                    if (char.IsLowSurrogate(cl))
 123                    {
 124                        // well formed surrogates
 0125                        SurrogateCasing.ToUpper(c, cl, out char h, out char l);
 0126                        destination[i] = h;
 0127                        destination[i + 1] = l;
 0128                        i++; // skip the low surrogate
 0129                        continue;
 130                    }
 131                }
 132
 0133                destination[i] = ToUpper(c);
 134            }
 0135        }
 136
 137        internal static void ToLower(ReadOnlySpan<char> source, Span<char> destination)
 138        {
 0139            Debug.Assert(GlobalizationMode.Invariant);
 0140            Debug.Assert(source.Length <= destination.Length);
 141
 0142            for (int i = 0; i < source.Length; i++)
 143            {
 0144                char c = source[i];
 0145                if (char.IsHighSurrogate(c) && i < source.Length - 1)
 146                {
 0147                    char cl = source[i + 1];
 0148                    if (char.IsLowSurrogate(cl))
 149                    {
 150                        // well formed surrogates
 0151                        SurrogateCasing.ToLower(c, cl, out char h, out char l);
 0152                        destination[i] = h;
 0153                        destination[i + 1] = l;
 0154                        i++; // skip the low surrogate
 0155                        continue;
 156                    }
 157                }
 158
 0159                destination[i] = ToLower(c);
 160            }
 0161        }
 162
 163        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 164        private static (uint, int) GetScalar(ref char source, int index, int length)
 165        {
 0166            char charA = source;
 0167            if (!char.IsHighSurrogate(charA) || index >= length - 1)
 168            {
 0169                return ((uint)charA, 1);
 170            }
 171
 0172            char charB = Unsafe.Add(ref source, 1);
 0173            if (!char.IsLowSurrogate(charB))
 174            {
 0175                return ((uint)charA, 1);
 176            }
 177
 0178            return (UnicodeUtility.GetScalarFromUtf16SurrogatePair(charA, charB), 2);
 179        }
 180
 181        internal static int CompareStringIgnoreCase(ref char strA, int lengthA, ref char strB, int lengthB)
 182        {
 0183            Debug.Assert(GlobalizationMode.Invariant);
 184
 0185            int length = Math.Min(lengthA, lengthB);
 186
 0187            ref char charA = ref strA;
 0188            ref char charB = ref strB;
 189
 0190            int index = 0;
 191
 0192            while (index < length)
 193            {
 0194                (uint codePointA, int codePointLengthA) = GetScalar(ref charA, index, lengthA);
 0195                (uint codePointB, int codePointLengthB) = GetScalar(ref charB, index, lengthB);
 196
 0197                if (codePointA == codePointB)
 198                {
 0199                    Debug.Assert(codePointLengthA == codePointLengthB);
 0200                    index += codePointLengthA;
 0201                    charA = ref Unsafe.Add(ref charA, codePointLengthA);
 0202                    charB = ref Unsafe.Add(ref charB, codePointLengthB);
 0203                    continue;
 204                }
 205
 0206                uint aUpper = CharUnicodeInfo.ToUpper(codePointA);
 0207                uint bUpper = CharUnicodeInfo.ToUpper(codePointB);
 208
 0209                if (aUpper == bUpper)
 210                {
 0211                    Debug.Assert(codePointLengthA == codePointLengthB);
 0212                    index += codePointLengthA;
 0213                    charA = ref Unsafe.Add(ref charA, codePointLengthA);
 0214                    charB = ref Unsafe.Add(ref charB, codePointLengthB);
 0215                    continue;
 216                }
 217
 0218                return (int)aUpper - (int)bUpper;
 219            }
 220
 0221            return lengthA - lengthB;
 222        }
 223
 224        internal static unsafe int IndexOfIgnoreCase(ReadOnlySpan<char> source, ReadOnlySpan<char> value)
 225        {
 0226            Debug.Assert(value.Length > 0);
 0227            Debug.Assert(value.Length <= source.Length);
 0228            Debug.Assert(GlobalizationMode.Invariant);
 229
 0230            fixed (char* pSource = &MemoryMarshal.GetReference(source))
 0231            fixed (char* pValue  = &MemoryMarshal.GetReference(value))
 232            {
 0233                char* pSourceLimit = pSource + (source.Length - value.Length);
 0234                char* pValueLimit = pValue + value.Length - 1;
 0235                char* pCurrentSource = pSource;
 236
 0237                while (pCurrentSource <= pSourceLimit)
 238                {
 0239                    char *pVal = pValue;
 0240                    char *pSrc = pCurrentSource;
 241
 0242                    while (pVal <= pValueLimit)
 243                    {
 0244                        if (!char.IsHighSurrogate(*pVal) || pVal == pValueLimit)
 245                        {
 0246                            if (*pVal != *pSrc && ToUpper(*pVal) != ToUpper(*pSrc))
 247                                break; // no match
 248
 0249                            pVal++;
 0250                            pSrc++;
 0251                            continue;
 252                        }
 253
 0254                        if (char.IsHighSurrogate(*pSrc) && char.IsLowSurrogate(*(pSrc + 1)) && char.IsLowSurrogate(*(pVa
 255                        {
 256                            // Well formed surrogates
 257                            // both the source and the Value have well-formed surrogates.
 0258                            if (!SurrogateCasing.Equal(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1)))
 259                                break; // no match
 260
 0261                            pSrc += 2;
 0262                            pVal += 2;
 0263                            continue;
 264                        }
 265
 0266                        if (*pVal != *pSrc)
 267                            break; // no match
 268
 0269                        pSrc++;
 0270                        pVal++;
 271                    }
 272
 0273                    if (pVal > pValueLimit)
 274                    {
 275                        // Found match.
 0276                        return (int)(pCurrentSource - pSource);
 277                    }
 278
 0279                    pCurrentSource++;
 280                }
 281
 0282                return -1;
 283            }
 284        }
 285
 286        internal static unsafe int LastIndexOfIgnoreCase(ReadOnlySpan<char> source, ReadOnlySpan<char> value)
 287        {
 0288            Debug.Assert(value.Length > 0);
 0289            Debug.Assert(value.Length <= source.Length);
 0290            Debug.Assert(GlobalizationMode.Invariant);
 291
 0292            fixed (char* pSource = &MemoryMarshal.GetReference(source))
 0293            fixed (char* pValue  = &MemoryMarshal.GetReference(value))
 294            {
 0295                char* pValueLimit = pValue + value.Length - 1;
 0296                char* pCurrentSource = pSource + (source.Length - value.Length);
 297
 0298                while (pCurrentSource >= pSource)
 299                {
 0300                    char *pVal = pValue;
 0301                    char *pSrc = pCurrentSource;
 302
 0303                    while (pVal <= pValueLimit)
 304                    {
 0305                        if (!char.IsHighSurrogate(*pVal) || pVal == pValueLimit)
 306                        {
 0307                            if (*pVal != *pSrc && ToUpper(*pVal) != ToUpper(*pSrc))
 308                                break; // no match
 309
 0310                            pVal++;
 0311                            pSrc++;
 0312                            continue;
 313                        }
 314
 0315                        if (char.IsHighSurrogate(*pSrc) && char.IsLowSurrogate(*(pSrc + 1)) && char.IsLowSurrogate(*(pVa
 316                        {
 317                            // Well formed surrogates
 318                            // both the source and the Value have well-formed surrogates.
 0319                            if (!SurrogateCasing.Equal(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1)))
 320                                break; // no match
 321
 0322                            pSrc += 2;
 0323                            pVal += 2;
 0324                            continue;
 325                        }
 326
 0327                        if (*pVal != *pSrc)
 328                            break; // no match
 329
 0330                        pSrc++;
 0331                        pVal++;
 332                    }
 333
 0334                    if (pVal > pValueLimit)
 335                    {
 336                        // Found match.
 0337                        return (int)(pCurrentSource - pSource);
 338                    }
 339
 0340                    pCurrentSource--;
 341                }
 342
 0343                return -1;
 344            }
 345        }
 346    }
 347}
 348