/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Shared/ServerInfrastructure/StringUtilities.cs
199 строк
7 KB
Jiri Cincura ↹
Fix typos in code. (#67428)
02 июл 2026, 13:29
Не верифицирован
02 июл 2026, 13:29
fa8126f
Код
Авторство
О чём код?
// 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.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using System.Text; #nullable enable namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; internal static class StringUtilities { // Null checks must be done independently of this method (if required) public static string GetAsciiOrUTF8String(this ReadOnlySpan<byte> span, Encoding defaultEncoding) { if (span.IsEmpty) { return string.Empty; } var resultString = string.Create(span.Length, span, static (destination, source) => { if (Ascii.ToUtf16(source, destination, out var written) != OperationStatus.Done) { // Mark resultString for UTF-8 encoding destination[0] = '\0'; } Debug.Assert(destination[0] == '\0' || written == destination.Length); }); // If resultString is marked, perform UTF-8 encoding if (resultString[0] == '\0') { try { resultString = defaultEncoding.GetString(span); } catch (DecoderFallbackException) { throw new InvalidOperationException(); } } return resultString; } // Null checks must be done independently of this method (if required) public static string GetAsciiString(this ReadOnlySpan<byte> span) { return string.Create(span.Length, span, static (destination, source) => { if (Ascii.ToUtf16(source, destination, out var written) != OperationStatus.Done) { throw new InvalidOperationException(); } Debug.Assert(written == destination.Length); }); } // Null checks must be done independently of this method (if required) public static string GetLatin1String(this ReadOnlySpan<byte> span) { if (span.IsEmpty) { return string.Empty; } return Encoding.Latin1.GetString(span); } [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static bool BytesOrdinalEqualsStringAndAscii(string previousValue, ReadOnlySpan<byte> newValue) { // previousValue is a previously materialized string which *must* have already passed validation. Debug.Assert(IsValidHeaderString(previousValue)); return Ascii.Equals(previousValue, newValue); } private static bool IsValidHeaderString(string value) { // Method for Debug.Assert to ensure BytesOrdinalEqualsStringAndAscii // is not called with an unvalidated string comparator. try { if (value is null) { return false; } new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetByteCount(value); return !value.Contains('\0'); } catch (ArgumentOutOfRangeException) { return false; // 'value' too large to compute a UTF-8 byte count } catch (EncoderFallbackException) { return false; // 'value' cannot be converted losslessly to UTF-8 } } /// <summary> /// A faster version of String.Concat(<paramref name="str"/>, <paramref name="separator"/>, <paramref name="number"/>.ToString("X8")) /// </summary> /// <param name="str"></param> /// <param name="separator"></param> /// <param name="number"></param> /// <returns></returns> public static string ConcatAsHexSuffix(string str, char separator, uint number) { var length = 1 + 8; if (str != null) { length += str.Length; } return string.Create(length, (str, separator, number), s_populateSpanWithHexSuffix); } private static readonly SpanAction<char, (string? str, char separator, uint number)> s_populateSpanWithHexSuffix = (Span<char> buffer, (string? str, char separator, uint number) tuple) => { var (tupleStr, tupleSeparator, tupleNumber) = tuple; var i = 0; if (tupleStr != null) { tupleStr.AsSpan().CopyTo(buffer); i = tupleStr.Length; } buffer[i] = tupleSeparator; i++; if (Ssse3.IsSupported) { // The constant inline vectors are read from the data section without any additional // moves. See https://github.com/dotnet/runtime/issues/44115 Case 1.1 for further details. var lowNibbles = Ssse3.Shuffle(Vector128.CreateScalarUnsafe(tupleNumber).AsByte(), Vector128.Create( 0xF, 0xF, 3, 0xF, 0xF, 0xF, 2, 0xF, 0xF, 0xF, 1, 0xF, 0xF, 0xF, 0, 0xF ).AsByte()); var highNibbles = Sse2.ShiftRightLogical(Sse2.ShiftRightLogical128BitLane(lowNibbles, 2).AsInt32(), 4).AsByte(); var indices = Sse2.And(Sse2.Or(lowNibbles, highNibbles), Vector128.Create((byte)0xF)); // Lookup the hex values at the positions of the indices var hex = Ssse3.Shuffle(Vector128.Create( (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' ), indices); // The high bytes (0x00) of the chars have also been converted to ascii hex '0', so clear them out. hex = Sse2.And(hex, Vector128.Create((ushort)0xFF).AsByte()); // This generates much more efficient asm than fixing the buffer and using // Sse2.Store((byte*)(p + i), chars.AsByte()); Unsafe.WriteUnaligned( ref Unsafe.As<char, byte>( ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), i)), hex); } else { var number = (int)tupleNumber; // Slice the buffer so we can use constant offsets in a backwards order // and the highest index [7] will eliminate the bounds checks for all the lower indices. buffer = buffer.Slice(i); // This must be explicitly typed as ReadOnlySpan<byte> // This then becomes a non-allocating mapping to the data section of the assembly. // If it is a var, Span<byte> or byte[], it allocates the byte array per call. ReadOnlySpan<byte> hexEncodeMap = "0123456789ABCDEF"u8; // Note: this only works with byte due to endian ambiguity for other types, // hence the later (char) casts buffer[7] = (char)hexEncodeMap[number & 0xF]; buffer[6] = (char)hexEncodeMap[(number >> 4) & 0xF]; buffer[5] = (char)hexEncodeMap[(number >> 8) & 0xF]; buffer[4] = (char)hexEncodeMap[(number >> 12) & 0xF]; buffer[3] = (char)hexEncodeMap[(number >> 16) & 0xF]; buffer[2] = (char)hexEncodeMap[(number >> 20) & 0xF]; buffer[1] = (char)hexEncodeMap[(number >> 24) & 0xF]; buffer[0] = (char)hexEncodeMap[(number >> 28) & 0xF]; } }; }