| File: src\runtime\src\libraries\System.Private.CoreLib\src\System\Globalization\Ordinal.Utf8.cs | Web Access |
| Project: System.Private.CoreLib.csproj (System.Private.CoreLib) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Text; using System.Text.Unicode; namespace System.Globalization { internal static partial class Ordinal { // Not optimized for large inputs: the only callers (number parsing) pass short sign/NaN/Infinity symbols. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool EqualsIgnoreCaseUtf8(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right) { // ASCII-only loops without calls, so they stay cheap when inlined into callers while ((left.Length >= sizeof(ulong)) && (right.Length >= sizeof(ulong))) { ulong a = BinaryPrimitives.ReadUInt64LittleEndian(left); ulong b = BinaryPrimitives.ReadUInt64LittleEndian(right); if (!Utf8Utility.AllBytesInUInt64AreAscii(a | b)) { break; } if (!Utf8Utility.UInt64OrdinalIgnoreCaseAscii(a, b)) { return false; } left = left.Slice(sizeof(ulong)); right = right.Slice(sizeof(ulong)); } for (int i = 0; i < right.Length; i++) { if (i >= left.Length) { return false; } uint a = left[i]; uint b = right[i]; if ((a | b) > 0x7F) { // No non-ASCII scalar is equal to an ASCII one under ordinal casing return ((a ^ b) <= 0x7F) && MatchIgnoreCaseUtf8(left.Slice(i), right.Slice(i), prefixOnly: false); } // Ordinal equals or lowercase equals if the result ends up in the a-z range if ((a != b) && (((a | 0x20) != (b | 0x20)) || !char.IsAsciiLetter((char)a))) { return false; } } return left.Length == right.Length; } internal static bool StartsWithIgnoreCaseUtf8(ReadOnlySpan<byte> source, ReadOnlySpan<byte> prefix) => MatchIgnoreCaseUtf8(source, prefix, prefixOnly: true); private static bool MatchIgnoreCaseUtf8(ReadOnlySpan<byte> source, ReadOnlySpan<byte> prefix, bool prefixOnly) { // NOTE: Two UTF-8 inputs of different length might compare as equal under // the OrdinalIgnoreCase comparer. This is distinct from UTF-16, where the // inputs being different length will mean that they can never compare as // equal under an OrdinalIgnoreCase comparer. while (!prefix.IsEmpty) { if (source.IsEmpty) { // The source ended before the prefix return false; } uint a = source[0]; uint b = prefix[0]; if ((a | b) <= 0x7F) { // Ordinal equals or lowercase equals if the result ends up in the a-z range if ((a != b) && (((a | 0x20) != (b | 0x20)) || !char.IsAsciiLetter((char)a))) { return false; } source = source.Slice(1); prefix = prefix.Slice(1); continue; } if ((a ^ b) > 0x7F) { // No non-ASCII scalar is equal to an ASCII one under ordinal casing return false; } // NLS/ICU doesn't provide native UTF-8 support so we need to do our own corresponding ordinal comparison OperationStatus statusA = Rune.DecodeFromUtf8(source, out Rune runeA, out int bytesConsumedA); OperationStatus statusB = Rune.DecodeFromUtf8(prefix, out Rune runeB, out int bytesConsumedB); if (statusA != statusB) { return false; } if (statusA == OperationStatus.Done) { if ((runeA != runeB) && (Rune.ToUpperOrdinal(runeA) != Rune.ToUpperOrdinal(runeB))) { return false; } } else if (!source.Slice(0, bytesConsumedA).SequenceEqual(prefix.Slice(0, bytesConsumedB))) { // Invalid sequences must match exactly return false; } source = source.Slice(bytesConsumedA); prefix = prefix.Slice(bytesConsumedB); } return prefixOnly || source.IsEmpty; } } }