Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace unsafe code with Span<char> #6784

Merged
merged 6 commits into from Feb 1, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 19 additions & 21 deletions src/Http/Http/src/Features/HttpRequestIdentifierFeature.cs
Expand Up @@ -8,9 +8,9 @@ namespace Microsoft.AspNetCore.Http.Features
{
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";
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
// 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
// for a roughly increasing _requestId over restarts
private static long _requestId = DateTime.UtcNow.Ticks;
Expand All @@ -34,31 +34,29 @@ public string TraceIdentifier
}
}

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
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
// See: https://github.com/aspnet/Hosting/pull/385

// stackalloc to allocate array on stack rather than heap
char* charBuffer = stackalloc char[13];

charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 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);
return string.Create(13, id, (span, value) =>
{
span[12] = _encode32Chars[(int)value & 31];
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
span[11] = _encode32Chars[(int)(value >> 5) & 31];
span[10] = _encode32Chars[(int)(value >> 10) & 31];
span[9] = _encode32Chars[(int)(value >> 15) & 31];
span[8] = _encode32Chars[(int)(value >> 20) & 31];
span[7] = _encode32Chars[(int)(value >> 25) & 31];
span[6] = _encode32Chars[(int)(value >> 30) & 31];
span[5] = _encode32Chars[(int)(value >> 35) & 31];
span[4] = _encode32Chars[(int)(value >> 40) & 31];
span[3] = _encode32Chars[(int)(value >> 45) & 31];
span[2] = _encode32Chars[(int)(value >> 50) & 31];
span[1] = _encode32Chars[(int)(value >> 55) & 31];
span[0] = _encode32Chars[(int)(value >> 60) & 31];
});
}
}
}
40 changes: 18 additions & 22 deletions src/Middleware/ResponseCaching/src/Internal/FastGuid.cs
@@ -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.

using System;
Expand Down Expand Up @@ -53,28 +53,24 @@ internal static FastGuid NewGuid()
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
char* charBuffer = stackalloc char[13];

// ID
charBuffer[0] = _encode32Chars[(int)(guid.IdValue >> 60) & 31];
charBuffer[1] = _encode32Chars[(int)(guid.IdValue >> 55) & 31];
charBuffer[2] = _encode32Chars[(int)(guid.IdValue >> 50) & 31];
charBuffer[3] = _encode32Chars[(int)(guid.IdValue >> 45) & 31];
charBuffer[4] = _encode32Chars[(int)(guid.IdValue >> 40) & 31];
charBuffer[5] = _encode32Chars[(int)(guid.IdValue >> 35) & 31];
charBuffer[6] = _encode32Chars[(int)(guid.IdValue >> 30) & 31];
charBuffer[7] = _encode32Chars[(int)(guid.IdValue >> 25) & 31];
charBuffer[8] = _encode32Chars[(int)(guid.IdValue >> 20) & 31];
charBuffer[9] = _encode32Chars[(int)(guid.IdValue >> 15) & 31];
charBuffer[10] = _encode32Chars[(int)(guid.IdValue >> 10) & 31];
charBuffer[11] = _encode32Chars[(int)(guid.IdValue >> 5) & 31];
charBuffer[12] = _encode32Chars[(int)guid.IdValue & 31];

// string ctor overload that takes char*
return new string(charBuffer, 0, 13);
return string.Create(13, guid.IdValue, (span, value) =>
{
span[12] = _encode32Chars[(int)value & 31];
span[11] = _encode32Chars[(int)(value >> 5) & 31];
span[10] = _encode32Chars[(int)(value >> 10) & 31];
span[9] = _encode32Chars[(int)(value >> 15) & 31];
span[8] = _encode32Chars[(int)(value >> 20) & 31];
span[7] = _encode32Chars[(int)(value >> 25) & 31];
span[6] = _encode32Chars[(int)(value >> 30) & 31];
span[5] = _encode32Chars[(int)(value >> 35) & 31];
span[4] = _encode32Chars[(int)(value >> 40) & 31];
span[3] = _encode32Chars[(int)(value >> 45) & 31];
span[2] = _encode32Chars[(int)(value >> 50) & 31];
span[1] = _encode32Chars[(int)(value >> 55) & 31];
span[0] = _encode32Chars[(int)(value >> 60) & 31];
});
}
}
}
Expand Up @@ -18,31 +18,28 @@ internal static class CorrelationIdGenerator

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
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
// See: https://github.com/aspnet/Hosting/pull/385

// stackalloc to allocate array on stack rather than heap
char* charBuffer = stackalloc char[13];

charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 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);
return string.Create(13, id, (span, value) =>
{
span[12] = _encode32Chars[(int)value & 31];
span[11] = _encode32Chars[(int)(value >> 5) & 31];
span[10] = _encode32Chars[(int)(value >> 10) & 31];
span[9] = _encode32Chars[(int)(value >> 15) & 31];
span[8] = _encode32Chars[(int)(value >> 20) & 31];
span[7] = _encode32Chars[(int)(value >> 25) & 31];
span[6] = _encode32Chars[(int)(value >> 30) & 31];
span[5] = _encode32Chars[(int)(value >> 35) & 31];
span[4] = _encode32Chars[(int)(value >> 40) & 31];
span[3] = _encode32Chars[(int)(value >> 45) & 31];
span[2] = _encode32Chars[(int)(value >> 50) & 31];
span[1] = _encode32Chars[(int)(value >> 55) & 31];
span[0] = _encode32Chars[(int)(value >> 60) & 31];
});
}
}
}
@@ -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.

using System;
Expand Down Expand Up @@ -94,8 +94,8 @@ public static unsafe bool TryGetAsciiString(byte* input, char* output, int count
var vector = Unsafe.AsRef<Vector<sbyte>>(input);
isValid &= CheckBytesInAsciiRange(vector);
Vector.Widen(
vector,
out Unsafe.AsRef<Vector<short>>(output),
vector,
out Unsafe.AsRef<Vector<short>>(output),
out Unsafe.AsRef<Vector<short>>(output + Vector<short>.Count));

input += Vector<sbyte>.Count;
Expand All @@ -118,39 +118,35 @@ public static unsafe bool TryGetAsciiString(byte* input, char* output, int count
/// <param name="separator"></param>
/// <param name="number"></param>
/// <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;
if (str != null)
{
length += str.Length;
}

// stackalloc to allocate array on stack rather than heap
char* charBuffer = stackalloc char[length];

var i = 0;
if (str != null)
return string.Create(length, (str, separator, number), (charBuffer, tuple) =>
{
for (i = 0; i < str.Length; i++)
var i = 0;
if (tuple.str != null)
{
charBuffer[i] = str[i];
for (i = 0; i < tuple.str.Length; i++)
{
charBuffer[i] = tuple.str[i];
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

charBuffer[i] = separator;

charBuffer[i + 1] = _encode16Chars[(int)(number >> 28) & 0xF];
charBuffer[i + 2] = _encode16Chars[(int)(number >> 24) & 0xF];
charBuffer[i + 3] = _encode16Chars[(int)(number >> 20) & 0xF];
charBuffer[i + 4] = _encode16Chars[(int)(number >> 16) & 0xF];
charBuffer[i + 5] = _encode16Chars[(int)(number >> 12) & 0xF];
charBuffer[i + 6] = _encode16Chars[(int)(number >> 8) & 0xF];
charBuffer[i + 7] = _encode16Chars[(int)(number >> 4) & 0xF];
charBuffer[i + 8] = _encode16Chars[(int)number & 0xF];

// string ctor overload that takes char*
return new string(charBuffer, 0, length);

charBuffer[i + 8] = _encode16Chars[(int)tuple.number & 0xF];
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
charBuffer[i + 7] = _encode16Chars[(int)(tuple.number >> 4) & 0xF];
charBuffer[i + 6] = _encode16Chars[(int)(tuple.number >> 8) & 0xF];
charBuffer[i + 5] = _encode16Chars[(int)(tuple.number >> 12) & 0xF];
charBuffer[i + 4] = _encode16Chars[(int)(tuple.number >> 16) & 0xF];
charBuffer[i + 3] = _encode16Chars[(int)(tuple.number >> 20) & 0xF];
charBuffer[i + 2] = _encode16Chars[(int)(tuple.number >> 24) & 0xF];
charBuffer[i + 1] = _encode16Chars[(int)(tuple.number >> 28) & 0xF];
charBuffer[i] = tuple.separator;
});
}

[MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push
Expand Down