Replace unsafe code with string.Create for hex generation (#6784)
This commit is contained in:
parent
1a61a58c51
commit
f1b24ccb92
|
|
@ -8,9 +8,9 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
{
|
{
|
||||||
public class HttpRequestIdentifierFeature : IHttpRequestIdentifierFeature
|
public class HttpRequestIdentifierFeature : IHttpRequestIdentifierFeature
|
||||||
{
|
{
|
||||||
// Base32 encoding - in ascii sort order for easy text based sorting
|
// Base32 encoding - in ascii sort order for easy text based sorting
|
||||||
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray();
|
||||||
// Seed the _requestId for this application instance with
|
// Seed the _requestId for this application instance with
|
||||||
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
|
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
|
||||||
// for a roughly increasing _requestId over restarts
|
// for a roughly increasing _requestId over restarts
|
||||||
private static long _requestId = DateTime.UtcNow.Ticks;
|
private static long _requestId = DateTime.UtcNow.Ticks;
|
||||||
|
|
@ -34,31 +34,26 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe string GenerateRequestId(long id)
|
private static string GenerateRequestId(long id)
|
||||||
{
|
{
|
||||||
// The following routine is ~310% faster than calling long.ToString() on x64
|
return string.Create(13, id, (buffer, value) =>
|
||||||
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
|
{
|
||||||
// See: https://github.com/aspnet/Hosting/pull/385
|
char[] encode32Chars = s_encode32Chars;
|
||||||
|
|
||||||
// stackalloc to allocate array on stack rather than heap
|
buffer[12] = encode32Chars[value & 31];
|
||||||
char* charBuffer = stackalloc char[13];
|
buffer[11] = encode32Chars[(value >> 5) & 31];
|
||||||
|
buffer[10] = encode32Chars[(value >> 10) & 31];
|
||||||
charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
|
buffer[9] = encode32Chars[(value >> 15) & 31];
|
||||||
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
|
buffer[8] = encode32Chars[(value >> 20) & 31];
|
||||||
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
|
buffer[7] = encode32Chars[(value >> 25) & 31];
|
||||||
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
|
buffer[6] = encode32Chars[(value >> 30) & 31];
|
||||||
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
|
buffer[5] = encode32Chars[(value >> 35) & 31];
|
||||||
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
|
buffer[4] = encode32Chars[(value >> 40) & 31];
|
||||||
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
|
buffer[3] = encode32Chars[(value >> 45) & 31];
|
||||||
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
|
buffer[2] = encode32Chars[(value >> 50) & 31];
|
||||||
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
|
buffer[1] = encode32Chars[(value >> 55) & 31];
|
||||||
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31];
|
buffer[0] = encode32Chars[(value >> 60) & 31];
|
||||||
charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31];
|
});
|
||||||
charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31];
|
|
||||||
charBuffer[12] = _encode32Chars[(int)id & 31];
|
|
||||||
|
|
||||||
// string ctor overload that takes char*
|
|
||||||
return new string(charBuffer, 0, 13);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||||
internal class FastGuid
|
internal class FastGuid
|
||||||
{
|
{
|
||||||
// Base32 encoding - in ascii sort order for easy text based sorting
|
// Base32 encoding - in ascii sort order for easy text based sorting
|
||||||
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray();
|
||||||
// Global ID
|
// Global ID
|
||||||
private static long NextId;
|
private static long NextId;
|
||||||
|
|
||||||
|
|
@ -53,28 +53,25 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||||
return new FastGuid(Interlocked.Increment(ref NextId));
|
return new FastGuid(Interlocked.Increment(ref NextId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe string GenerateGuidString(FastGuid guid)
|
private static string GenerateGuidString(FastGuid guid)
|
||||||
{
|
{
|
||||||
// stackalloc to allocate array on stack rather than heap
|
return string.Create(13, guid.IdValue, (buffer, value) =>
|
||||||
char* charBuffer = stackalloc char[13];
|
{
|
||||||
|
char[] encode32Chars = s_encode32Chars;
|
||||||
// ID
|
buffer[12] = encode32Chars[value & 31];
|
||||||
charBuffer[0] = _encode32Chars[(int)(guid.IdValue >> 60) & 31];
|
buffer[11] = encode32Chars[(value >> 5) & 31];
|
||||||
charBuffer[1] = _encode32Chars[(int)(guid.IdValue >> 55) & 31];
|
buffer[10] = encode32Chars[(value >> 10) & 31];
|
||||||
charBuffer[2] = _encode32Chars[(int)(guid.IdValue >> 50) & 31];
|
buffer[9] = encode32Chars[(value >> 15) & 31];
|
||||||
charBuffer[3] = _encode32Chars[(int)(guid.IdValue >> 45) & 31];
|
buffer[8] = encode32Chars[(value >> 20) & 31];
|
||||||
charBuffer[4] = _encode32Chars[(int)(guid.IdValue >> 40) & 31];
|
buffer[7] = encode32Chars[(value >> 25) & 31];
|
||||||
charBuffer[5] = _encode32Chars[(int)(guid.IdValue >> 35) & 31];
|
buffer[6] = encode32Chars[(value >> 30) & 31];
|
||||||
charBuffer[6] = _encode32Chars[(int)(guid.IdValue >> 30) & 31];
|
buffer[5] = encode32Chars[(value >> 35) & 31];
|
||||||
charBuffer[7] = _encode32Chars[(int)(guid.IdValue >> 25) & 31];
|
buffer[4] = encode32Chars[(value >> 40) & 31];
|
||||||
charBuffer[8] = _encode32Chars[(int)(guid.IdValue >> 20) & 31];
|
buffer[3] = encode32Chars[(value >> 45) & 31];
|
||||||
charBuffer[9] = _encode32Chars[(int)(guid.IdValue >> 15) & 31];
|
buffer[2] = encode32Chars[(value >> 50) & 31];
|
||||||
charBuffer[10] = _encode32Chars[(int)(guid.IdValue >> 10) & 31];
|
buffer[1] = encode32Chars[(value >> 55) & 31];
|
||||||
charBuffer[11] = _encode32Chars[(int)(guid.IdValue >> 5) & 31];
|
buffer[0] = encode32Chars[(value >> 60) & 31];
|
||||||
charBuffer[12] = _encode32Chars[(int)guid.IdValue & 31];
|
});
|
||||||
|
|
||||||
// string ctor overload that takes char*
|
|
||||||
return new string(charBuffer, 0, 13);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
internal static class CorrelationIdGenerator
|
internal static class CorrelationIdGenerator
|
||||||
{
|
{
|
||||||
// Base32 encoding - in ascii sort order for easy text based sorting
|
// Base32 encoding - in ascii sort order for easy text based sorting
|
||||||
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray();
|
||||||
|
|
||||||
// Seed the _lastConnectionId for this application instance with
|
// Seed the _lastConnectionId for this application instance with
|
||||||
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
|
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
|
||||||
|
|
@ -18,31 +18,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
|
|
||||||
public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId));
|
public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId));
|
||||||
|
|
||||||
private static unsafe string GenerateId(long id)
|
private static string GenerateId(long id)
|
||||||
{
|
{
|
||||||
// The following routine is ~310% faster than calling long.ToString() on x64
|
return string.Create(13, id, (buffer, value) =>
|
||||||
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
|
{
|
||||||
// See: https://github.com/aspnet/Hosting/pull/385
|
char[] encode32Chars = s_encode32Chars;
|
||||||
|
|
||||||
// stackalloc to allocate array on stack rather than heap
|
buffer[12] = encode32Chars[value & 31];
|
||||||
char* charBuffer = stackalloc char[13];
|
buffer[11] = encode32Chars[(value >> 5) & 31];
|
||||||
|
buffer[10] = encode32Chars[(value >> 10) & 31];
|
||||||
charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
|
buffer[9] = encode32Chars[(value >> 15) & 31];
|
||||||
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
|
buffer[8] = encode32Chars[(value >> 20) & 31];
|
||||||
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
|
buffer[7] = encode32Chars[(value >> 25) & 31];
|
||||||
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
|
buffer[6] = encode32Chars[(value >> 30) & 31];
|
||||||
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
|
buffer[5] = encode32Chars[(value >> 35) & 31];
|
||||||
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
|
buffer[4] = encode32Chars[(value >> 40) & 31];
|
||||||
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
|
buffer[3] = encode32Chars[(value >> 45) & 31];
|
||||||
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
|
buffer[2] = encode32Chars[(value >> 50) & 31];
|
||||||
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
|
buffer[1] = encode32Chars[(value >> 55) & 31];
|
||||||
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31];
|
buffer[0] = encode32Chars[(value >> 60) & 31];
|
||||||
charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31];
|
});
|
||||||
charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31];
|
|
||||||
charBuffer[12] = _encode32Chars[(int)id & 31];
|
|
||||||
|
|
||||||
// string ctor overload that takes char*
|
|
||||||
return new string(charBuffer, 0, 13);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -94,8 +94,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
var vector = Unsafe.AsRef<Vector<sbyte>>(input);
|
var vector = Unsafe.AsRef<Vector<sbyte>>(input);
|
||||||
isValid &= CheckBytesInAsciiRange(vector);
|
isValid &= CheckBytesInAsciiRange(vector);
|
||||||
Vector.Widen(
|
Vector.Widen(
|
||||||
vector,
|
vector,
|
||||||
out Unsafe.AsRef<Vector<short>>(output),
|
out Unsafe.AsRef<Vector<short>>(output),
|
||||||
out Unsafe.AsRef<Vector<short>>(output + Vector<short>.Count));
|
out Unsafe.AsRef<Vector<short>>(output + Vector<short>.Count));
|
||||||
|
|
||||||
input += Vector<sbyte>.Count;
|
input += Vector<sbyte>.Count;
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string _encode16Chars = "0123456789ABCDEF";
|
private static readonly char[] s_encode16Chars = "0123456789ABCDEF".ToCharArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A faster version of String.Concat(<paramref name="str"/>, <paramref name="separator"/>, <paramref name="number"/>.ToString("X8"))
|
/// A faster version of String.Concat(<paramref name="str"/>, <paramref name="separator"/>, <paramref name="number"/>.ToString("X8"))
|
||||||
|
|
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
/// <param name="separator"></param>
|
/// <param name="separator"></param>
|
||||||
/// <param name="number"></param>
|
/// <param name="number"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static unsafe string ConcatAsHexSuffix(string str, char separator, uint number)
|
public static string ConcatAsHexSuffix(string str, char separator, uint number)
|
||||||
{
|
{
|
||||||
var length = 1 + 8;
|
var length = 1 + 8;
|
||||||
if (str != null)
|
if (str != null)
|
||||||
|
|
@ -126,31 +126,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
length += str.Length;
|
length += str.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stackalloc to allocate array on stack rather than heap
|
return string.Create(length, (str, separator, number), (buffer, tuple) =>
|
||||||
char* charBuffer = stackalloc char[length];
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
if (str != null)
|
|
||||||
{
|
{
|
||||||
for (i = 0; i < str.Length; i++)
|
var (tupleStr, tupleSeparator, tupleNumber) = tuple;
|
||||||
|
char[] encode16Chars = s_encode16Chars;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
if (tupleStr != null)
|
||||||
{
|
{
|
||||||
charBuffer[i] = str[i];
|
tupleStr.AsSpan().CopyTo(buffer);
|
||||||
|
i = tupleStr.Length;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
charBuffer[i] = separator;
|
buffer[i + 8] = encode16Chars[tupleNumber & 0xF];
|
||||||
|
buffer[i + 7] = encode16Chars[(tupleNumber >> 4) & 0xF];
|
||||||
charBuffer[i + 1] = _encode16Chars[(int)(number >> 28) & 0xF];
|
buffer[i + 6] = encode16Chars[(tupleNumber >> 8) & 0xF];
|
||||||
charBuffer[i + 2] = _encode16Chars[(int)(number >> 24) & 0xF];
|
buffer[i + 5] = encode16Chars[(tupleNumber >> 12) & 0xF];
|
||||||
charBuffer[i + 3] = _encode16Chars[(int)(number >> 20) & 0xF];
|
buffer[i + 4] = encode16Chars[(tupleNumber >> 16) & 0xF];
|
||||||
charBuffer[i + 4] = _encode16Chars[(int)(number >> 16) & 0xF];
|
buffer[i + 3] = encode16Chars[(tupleNumber >> 20) & 0xF];
|
||||||
charBuffer[i + 5] = _encode16Chars[(int)(number >> 12) & 0xF];
|
buffer[i + 2] = encode16Chars[(tupleNumber >> 24) & 0xF];
|
||||||
charBuffer[i + 6] = _encode16Chars[(int)(number >> 8) & 0xF];
|
buffer[i + 1] = encode16Chars[(tupleNumber >> 28) & 0xF];
|
||||||
charBuffer[i + 7] = _encode16Chars[(int)(number >> 4) & 0xF];
|
buffer[i] = tupleSeparator;
|
||||||
charBuffer[i + 8] = _encode16Chars[(int)number & 0xF];
|
});
|
||||||
|
|
||||||
// string ctor overload that takes char*
|
|
||||||
return new string(charBuffer, 0, length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue