Merge branch 'arespr/knownmethods-optimizations' into dev
This commit is contained in:
commit
f464760bf8
|
|
@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Internal.System;
|
|||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||
{
|
||||
public static class HttpUtilities
|
||||
public static partial class HttpUtilities
|
||||
{
|
||||
public const string Http10Version = "HTTP/1.0";
|
||||
public const string Http11Version = "HTTP/1.1";
|
||||
|
|
@ -20,27 +20,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
public const string HttpsUriScheme = "https://";
|
||||
|
||||
// readonly primitive statics can be Jit'd to consts https://github.com/dotnet/coreclr/issues/1079
|
||||
|
||||
private readonly static ulong _httpSchemeLong = GetAsciiStringAsLong(HttpUriScheme + "\0");
|
||||
private readonly static ulong _httpsSchemeLong = GetAsciiStringAsLong(HttpsUriScheme);
|
||||
private readonly static ulong _httpConnectMethodLong = GetAsciiStringAsLong("CONNECT ");
|
||||
private readonly static ulong _httpDeleteMethodLong = GetAsciiStringAsLong("DELETE \0");
|
||||
|
||||
private const uint _httpGetMethodInt = 542393671; // retun of GetAsciiStringAsInt("GET "); const results in better codegen
|
||||
private readonly static ulong _httpHeadMethodLong = GetAsciiStringAsLong("HEAD \0\0\0");
|
||||
private readonly static ulong _httpPatchMethodLong = GetAsciiStringAsLong("PATCH \0\0");
|
||||
private readonly static ulong _httpPostMethodLong = GetAsciiStringAsLong("POST \0\0\0");
|
||||
private readonly static ulong _httpPutMethodLong = GetAsciiStringAsLong("PUT \0\0\0\0");
|
||||
private readonly static ulong _httpOptionsMethodLong = GetAsciiStringAsLong("OPTIONS ");
|
||||
private readonly static ulong _httpTraceMethodLong = GetAsciiStringAsLong("TRACE \0\0");
|
||||
|
||||
private const ulong _http10VersionLong = 3471766442030158920; // GetAsciiStringAsLong("HTTP/1.0"); const results in better codegen
|
||||
private const ulong _http11VersionLong = 3543824036068086856; // GetAsciiStringAsLong("HTTP/1.1"); const results in better codegen
|
||||
|
||||
private readonly static ulong _mask8Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff });
|
||||
private readonly static ulong _mask7Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 });
|
||||
private readonly static ulong _mask6Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 });
|
||||
private readonly static ulong _mask5Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 });
|
||||
private readonly static ulong _mask4Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 });
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void SetKnownMethod(ulong mask, ulong knownMethodUlong, HttpMethod knownMethod, int length)
|
||||
{
|
||||
_knownMethods[GetKnownMethodIndex(knownMethodUlong)] = new Tuple<ulong, ulong, HttpMethod, int>(mask, knownMethodUlong, knownMethod, length);
|
||||
}
|
||||
|
||||
private readonly static Tuple<ulong, ulong, HttpMethod, int>[] _knownMethods =
|
||||
{
|
||||
|
|
@ -54,21 +46,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
Tuple.Create(_mask8Chars, _httpOptionsMethodLong, HttpMethod.Options, 7),
|
||||
};
|
||||
|
||||
private readonly static string[] _methodNames = CreateMethodNames();
|
||||
|
||||
private static string[] CreateMethodNames()
|
||||
private static void FillKnownMethodsGaps()
|
||||
{
|
||||
var methodNames = new string[9];
|
||||
methodNames[(byte)HttpMethod.Get] = HttpMethods.Get;
|
||||
methodNames[(byte)HttpMethod.Put] = HttpMethods.Put;
|
||||
methodNames[(byte)HttpMethod.Delete] = HttpMethods.Delete;
|
||||
methodNames[(byte)HttpMethod.Post] = HttpMethods.Post;
|
||||
methodNames[(byte)HttpMethod.Head] = HttpMethods.Head;
|
||||
methodNames[(byte)HttpMethod.Trace] = HttpMethods.Trace;
|
||||
methodNames[(byte)HttpMethod.Patch] = HttpMethods.Patch;
|
||||
methodNames[(byte)HttpMethod.Connect] = HttpMethods.Connect;
|
||||
methodNames[(byte)HttpMethod.Options] = HttpMethods.Options;
|
||||
return methodNames;
|
||||
var knownMethods = _knownMethods;
|
||||
var length = knownMethods.Length;
|
||||
var invalidHttpMethod = new Tuple<ulong, ulong, HttpMethod, int>(_mask8Chars, 0ul, HttpMethod.Custom, 0);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (knownMethods[i] == null)
|
||||
{
|
||||
knownMethods[i] = invalidHttpMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static ulong GetAsciiStringAsLong(string str)
|
||||
|
|
@ -187,13 +176,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
else
|
||||
{
|
||||
var value = *(ulong*)data;
|
||||
foreach (var x in _knownMethods)
|
||||
var key = GetKnownMethodIndex(value);
|
||||
var x = _knownMethods[key];
|
||||
|
||||
if (x != null && (value & x.Item1) == x.Item2)
|
||||
{
|
||||
if ((value & x.Item1) == x.Item2)
|
||||
{
|
||||
methodLength = x.Item4;
|
||||
return x.Item3;
|
||||
}
|
||||
methodLength = x.Item4;
|
||||
return x.Item3;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
// 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;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||
{
|
||||
public static partial class HttpUtilities
|
||||
{
|
||||
// readonly primitive statics can be Jit'd to consts https://github.com/dotnet/coreclr/issues/1079
|
||||
private readonly static ulong _httpConnectMethodLong = GetAsciiStringAsLong("CONNECT ");
|
||||
private readonly static ulong _httpDeleteMethodLong = GetAsciiStringAsLong("DELETE \0");
|
||||
private readonly static ulong _httpHeadMethodLong = GetAsciiStringAsLong("HEAD \0\0\0");
|
||||
private readonly static ulong _httpPatchMethodLong = GetAsciiStringAsLong("PATCH \0\0");
|
||||
private readonly static ulong _httpPostMethodLong = GetAsciiStringAsLong("POST \0\0\0");
|
||||
private readonly static ulong _httpPutMethodLong = GetAsciiStringAsLong("PUT \0\0\0\0");
|
||||
private readonly static ulong _httpOptionsMethodLong = GetAsciiStringAsLong("OPTIONS ");
|
||||
private readonly static ulong _httpTraceMethodLong = GetAsciiStringAsLong("TRACE \0\0");
|
||||
|
||||
private readonly static ulong _mask8Chars = GetMaskAsLong(new byte[]
|
||||
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
|
||||
|
||||
private readonly static ulong _mask7Chars = GetMaskAsLong(new byte[]
|
||||
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00});
|
||||
|
||||
private readonly static ulong _mask6Chars = GetMaskAsLong(new byte[]
|
||||
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00});
|
||||
|
||||
private readonly static ulong _mask5Chars = GetMaskAsLong(new byte[]
|
||||
{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00});
|
||||
|
||||
private readonly static ulong _mask4Chars = GetMaskAsLong(new byte[]
|
||||
{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00});
|
||||
|
||||
private readonly static Tuple<ulong, ulong, HttpMethod, int>[] _knownMethods =
|
||||
new Tuple<ulong, ulong, HttpMethod, int>[17];
|
||||
|
||||
private readonly static string[] _methodNames = new string[9];
|
||||
|
||||
static HttpUtilities()
|
||||
{
|
||||
SetKnownMethod(_mask4Chars, _httpPutMethodLong, HttpMethod.Put, 3);
|
||||
SetKnownMethod(_mask5Chars, _httpHeadMethodLong, HttpMethod.Head, 4);
|
||||
SetKnownMethod(_mask5Chars, _httpPostMethodLong, HttpMethod.Post, 4);
|
||||
SetKnownMethod(_mask6Chars, _httpPatchMethodLong, HttpMethod.Patch, 5);
|
||||
SetKnownMethod(_mask6Chars, _httpTraceMethodLong, HttpMethod.Trace, 5);
|
||||
SetKnownMethod(_mask7Chars, _httpDeleteMethodLong, HttpMethod.Delete, 6);
|
||||
SetKnownMethod(_mask8Chars, _httpConnectMethodLong, HttpMethod.Connect, 7);
|
||||
SetKnownMethod(_mask8Chars, _httpOptionsMethodLong, HttpMethod.Options, 7);
|
||||
FillKnownMethodsGaps();
|
||||
_methodNames[(byte)HttpMethod.Connect] = HttpMethods.Connect;
|
||||
_methodNames[(byte)HttpMethod.Delete] = HttpMethods.Delete;
|
||||
_methodNames[(byte)HttpMethod.Get] = HttpMethods.Get;
|
||||
_methodNames[(byte)HttpMethod.Head] = HttpMethods.Head;
|
||||
_methodNames[(byte)HttpMethod.Options] = HttpMethods.Options;
|
||||
_methodNames[(byte)HttpMethod.Patch] = HttpMethods.Patch;
|
||||
_methodNames[(byte)HttpMethod.Post] = HttpMethods.Post;
|
||||
_methodNames[(byte)HttpMethod.Put] = HttpMethods.Put;
|
||||
_methodNames[(byte)HttpMethod.Trace] = HttpMethods.Trace;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetKnownMethodIndex(ulong value)
|
||||
{
|
||||
const int magicNumer = 0x600000C;
|
||||
var tmp = (int)value & magicNumer;
|
||||
return ((tmp >> 2) | (tmp >> 23)) & 0xF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,9 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCoreVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Server.Kestrel.Core\Microsoft.AspNetCore.Server.Kestrel.Core.csproj" />
|
||||
|
|
|
|||
|
|
@ -15,27 +15,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
const string frameHeadersGeneratedPath = "../../../../../src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameHeaders.Generated.cs";
|
||||
const string frameGeneratedPath = "../../../../../src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.Generated.cs";
|
||||
|
||||
const string httpUtilitiesGeneratedPath = "../../../../../src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.Generated.cs";
|
||||
var testFrameHeadersGeneratedPath = Path.GetTempFileName();
|
||||
var testFrameGeneratedPath = Path.GetTempFileName();
|
||||
|
||||
var testHttpUtilitiesGeneratedPath = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
var currentFrameHeadersGenerated = File.ReadAllText(frameHeadersGeneratedPath);
|
||||
var currentFrameGenerated = File.ReadAllText(frameGeneratedPath);
|
||||
var currentHttpUtilitiesGenerated = File.ReadAllText(httpUtilitiesGeneratedPath);
|
||||
|
||||
CodeGenerator.Program.Run(testFrameHeadersGeneratedPath, testFrameGeneratedPath);
|
||||
CodeGenerator.Program.Run(testFrameHeadersGeneratedPath, testFrameGeneratedPath, testHttpUtilitiesGeneratedPath);
|
||||
|
||||
var testFrameHeadersGenerated = File.ReadAllText(testFrameHeadersGeneratedPath);
|
||||
var testFrameGenerated = File.ReadAllText(testFrameGeneratedPath);
|
||||
var testHttpUtilitiesGenerated = File.ReadAllText(testHttpUtilitiesGeneratedPath);
|
||||
|
||||
Assert.Equal(currentFrameHeadersGenerated, testFrameHeadersGenerated, ignoreLineEndingDifferences: true);
|
||||
Assert.Equal(currentFrameGenerated, testFrameGenerated, ignoreLineEndingDifferences: true);
|
||||
Assert.Equal(currentHttpUtilitiesGenerated, testHttpUtilitiesGenerated, ignoreLineEndingDifferences: true);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(testFrameHeadersGeneratedPath);
|
||||
File.Delete(testFrameGeneratedPath);
|
||||
File.Delete(testHttpUtilitiesGeneratedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,95 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
{
|
||||
public class KnownStringsBenchmark
|
||||
{
|
||||
static byte[] _method = Encoding.UTF8.GetBytes("GET ");
|
||||
static byte[] _methodConnect = Encoding.ASCII.GetBytes("CONNECT ");
|
||||
static byte[] _methodDelete = Encoding.ASCII.GetBytes("DELETE \0");
|
||||
static byte[] _methodGet = Encoding.ASCII.GetBytes("GET ");
|
||||
static byte[] _methodHead = Encoding.ASCII.GetBytes("HEAD \0\0\0");
|
||||
static byte[] _methodPatch = Encoding.ASCII.GetBytes("PATCH \0\0");
|
||||
static byte[] _methodPost = Encoding.ASCII.GetBytes("POST \0\0\0");
|
||||
static byte[] _methodPut = Encoding.ASCII.GetBytes("PUT \0\0\0\0");
|
||||
static byte[] _methodOptions = Encoding.ASCII.GetBytes("OPTIONS ");
|
||||
static byte[] _methodTrace = Encoding.ASCII.GetBytes("TRACE \0\0");
|
||||
|
||||
static byte[] _version = Encoding.UTF8.GetBytes("HTTP/1.1\r\n");
|
||||
const int loops = 1000;
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_GET()
|
||||
{
|
||||
Span<byte> data = _methodGet;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_CONNECT()
|
||||
{
|
||||
Span<byte> data = _methodConnect;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_DELETE()
|
||||
{
|
||||
Span<byte> data = _methodDelete;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_HEAD()
|
||||
{
|
||||
Span<byte> data = _methodHead;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_PATCH()
|
||||
{
|
||||
Span<byte> data = _methodPatch;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_POST()
|
||||
{
|
||||
Span<byte> data = _methodPost;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_PUT()
|
||||
{
|
||||
Span<byte> data = _methodPut;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_OPTIONS()
|
||||
{
|
||||
Span<byte> data = _methodOptions;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_TRACE()
|
||||
{
|
||||
Span<byte> data = _methodTrace;
|
||||
|
||||
return GetKnownMethod(data);
|
||||
}
|
||||
|
||||
private int GetKnownMethod(Span<byte> data)
|
||||
{
|
||||
int len = 0;
|
||||
HttpMethod method;
|
||||
Span<byte> data = _method;
|
||||
for (int i = 0; i < loops; i++) {
|
||||
|
||||
for (int i = 0; i < loops; i++)
|
||||
{
|
||||
data.GetKnownMethod(out method, out var length);
|
||||
len += length;
|
||||
data.GetKnownMethod(out method, out length);
|
||||
|
|
@ -53,7 +131,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
int len = 0;
|
||||
HttpVersion version;
|
||||
Span<byte> data = _version;
|
||||
for (int i = 0; i < loops; i++) {
|
||||
for (int i = 0; i < loops; i++)
|
||||
{
|
||||
data.GetKnownVersion(out version, out var length);
|
||||
len += length;
|
||||
data.GetKnownVersion(out version, out length);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||
{
|
||||
public class KnownStringsTests
|
||||
{
|
||||
static byte[] _methodConnect = Encoding.ASCII.GetBytes("CONNECT ");
|
||||
static byte[] _methodDelete = Encoding.ASCII.GetBytes("DELETE \0");
|
||||
static byte[] _methodGet = Encoding.ASCII.GetBytes("GET ");
|
||||
static byte[] _methodHead = Encoding.ASCII.GetBytes("HEAD \0\0\0");
|
||||
static byte[] _methodPatch = Encoding.ASCII.GetBytes("PATCH \0\0");
|
||||
static byte[] _methodPost = Encoding.ASCII.GetBytes("POST \0\0\0");
|
||||
static byte[] _methodPut = Encoding.ASCII.GetBytes("PUT \0\0\0\0");
|
||||
static byte[] _methodOptions = Encoding.ASCII.GetBytes("OPTIONS ");
|
||||
static byte[] _methodTrace = Encoding.ASCII.GetBytes("TRACE \0\0");
|
||||
|
||||
const int MagicNumer = 0x0600000C;
|
||||
static byte[] _invalidMethod1 = BitConverter.GetBytes((ulong)MagicNumer);
|
||||
static byte[] _invalidMethod2 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
static byte[] _invalidMethod3 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
static byte[] _invalidMethod4 = Encoding.ASCII.GetBytes("CONNECT_");
|
||||
static byte[] _invalidMethod5 = Encoding.ASCII.GetBytes("DELETE_\0");
|
||||
static byte[] _invalidMethod6 = Encoding.ASCII.GetBytes("GET_");
|
||||
static byte[] _invalidMethod7 = Encoding.ASCII.GetBytes("HEAD_\0\0\0");
|
||||
static byte[] _invalidMethod8 = Encoding.ASCII.GetBytes("PATCH_\0\0");
|
||||
static byte[] _invalidMethod9 = Encoding.ASCII.GetBytes("POST_\0\0\0");
|
||||
static byte[] _invalidMethod10 = Encoding.ASCII.GetBytes("PUT_\0\0\0\0");
|
||||
static byte[] _invalidMethod11 = Encoding.ASCII.GetBytes("OPTIONS_");
|
||||
static byte[] _invalidMethod12 = Encoding.ASCII.GetBytes("TRACE_\0\0");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object[] CreateTestDataEntry(byte[] methodData, HttpMethod expectedMethod, int expectedLength, bool expectedResult)
|
||||
{
|
||||
return new object[] { methodData, expectedMethod, expectedLength, expectedResult };
|
||||
}
|
||||
|
||||
private static readonly object[][] _testData = new object[][]
|
||||
{
|
||||
CreateTestDataEntry(_methodGet, HttpMethod.Get, 3, true),
|
||||
CreateTestDataEntry(_methodPut, HttpMethod.Put, 3, true),
|
||||
CreateTestDataEntry(_methodPost, HttpMethod.Post, 4, true),
|
||||
CreateTestDataEntry(_methodHead, HttpMethod.Head, 4, true),
|
||||
CreateTestDataEntry(_methodTrace, HttpMethod.Trace, 5, true),
|
||||
CreateTestDataEntry(_methodPatch, HttpMethod.Patch, 5, true),
|
||||
CreateTestDataEntry(_methodDelete, HttpMethod.Delete, 6, true),
|
||||
CreateTestDataEntry(_methodConnect, HttpMethod.Connect, 7, true),
|
||||
CreateTestDataEntry(_methodOptions, HttpMethod.Options, 7, true),
|
||||
CreateTestDataEntry(_invalidMethod1, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod2, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod3, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod4, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod5, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod6, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod7, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod8, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod9, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod10, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod11, HttpMethod.Custom, 0, false),
|
||||
CreateTestDataEntry(_invalidMethod12, HttpMethod.Custom, 0, false),
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> TestData => _testData;
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestData), MemberType = typeof(KnownStringsTests))]
|
||||
public void GetsKnownMethod(byte[] methodData, HttpMethod expectedMethod, int expectedLength, bool expectedResult)
|
||||
{
|
||||
var data = new Span<byte>(methodData);
|
||||
|
||||
var result = data.GetKnownMethod(out var method, out var length);
|
||||
|
||||
Assert.Equal(expectedResult, result);
|
||||
Assert.Equal(expectedMethod, method);
|
||||
Assert.Equal(expectedLength, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,9 +13,21 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="$(AspNetCoreVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<StartWorkingDirectory>$(MSBuildThisFileDirectory)..\..\src\Microsoft.AspNetCore.Server.Kestrel.Core\Internal\Http</StartWorkingDirectory>
|
||||
<StartArguments>FrameHeaders.Generated.cs Frame.Generated.cs</StartArguments>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
// 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;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CodeGenerator.HttpUtilities
|
||||
{
|
||||
// C code for Algorithm L (Lexicographic combinations) in Section 7.2.1.3 of The Art of Computer Programming, Volume 4A: Combinatorial Algorithms, Part 1 :
|
||||
internal class CombinationsWithoutRepetition<T> : IEnumerator<T[]>
|
||||
{
|
||||
private int[] _pointers;
|
||||
private T[] _nElements;
|
||||
private readonly int _p;
|
||||
public T[] Current { get; private set; }
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public CombinationsWithoutRepetition(T[] nElements, int p)
|
||||
{
|
||||
if (nElements.Length < p) throw new ArgumentOutOfRangeException(nameof(p));
|
||||
|
||||
_nElements = nElements;
|
||||
_p = p;
|
||||
Current = new T[p];
|
||||
ResetCurrent();
|
||||
}
|
||||
|
||||
private bool _firstElement;
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_firstElement)
|
||||
{
|
||||
_firstElement = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
var p = _p;
|
||||
var pointers = _pointers;
|
||||
var current = Current;
|
||||
var nElements = _nElements;
|
||||
var index = 1;
|
||||
|
||||
while (pointers[index] + 1 == pointers[index + 1])
|
||||
{
|
||||
var j1 = index - 1;
|
||||
|
||||
pointers[index] = j1;
|
||||
current[j1] = nElements[j1];
|
||||
++index;
|
||||
}
|
||||
|
||||
if (index > p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
current[index - 1] = nElements[++pointers[index]];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ResetCurrent()
|
||||
{
|
||||
var p = _p;
|
||||
if (_pointers == null)
|
||||
_pointers = new int[p + 3];
|
||||
|
||||
var pointers = _pointers;
|
||||
var current = Current;
|
||||
var nElements = _nElements;
|
||||
|
||||
pointers[0] = 0;
|
||||
for (int j = 1; j <= _p; j++)
|
||||
{
|
||||
pointers[j] = j - 1;
|
||||
}
|
||||
pointers[_p + 1] = nElements.Length;
|
||||
pointers[_p + 2] = 0;
|
||||
|
||||
for (int j = _p; j > 0; j--)
|
||||
{
|
||||
current[j - 1] = nElements[pointers[j]];
|
||||
}
|
||||
_firstElement = true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Array.Clear(Current, 0, Current.Length);
|
||||
Current = null;
|
||||
ResetCurrent();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_nElements = null;
|
||||
Current = null;
|
||||
_pointers = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
|
||||
namespace CodeGenerator.HttpUtilities
|
||||
{
|
||||
public class HttpUtilities
|
||||
{
|
||||
public static string GeneratedFile()
|
||||
{
|
||||
var httpMethods = new []
|
||||
{
|
||||
new Tuple<string, HttpMethod>("CONNECT ", HttpMethod.Connect),
|
||||
new Tuple<string, HttpMethod>("DELETE ", HttpMethod.Delete),
|
||||
new Tuple<string, HttpMethod>("HEAD ", HttpMethod.Head),
|
||||
new Tuple<string, HttpMethod>("PATCH ", HttpMethod.Patch),
|
||||
new Tuple<string, HttpMethod>("POST ", HttpMethod.Post),
|
||||
new Tuple<string, HttpMethod>("PUT ", HttpMethod.Put),
|
||||
new Tuple<string, HttpMethod>("OPTIONS ", HttpMethod.Options),
|
||||
new Tuple<string, HttpMethod>("TRACE ", HttpMethod.Trace),
|
||||
new Tuple<string, HttpMethod>("GET ", HttpMethod.Get)
|
||||
};
|
||||
|
||||
return GenerateFile(httpMethods);
|
||||
}
|
||||
|
||||
private static string GenerateFile(Tuple<string, HttpMethod>[] httpMethods)
|
||||
{
|
||||
var maskLength = (byte)Math.Ceiling(Math.Log(httpMethods.Length, 2));
|
||||
|
||||
var methodsInfo = httpMethods.Select(GetMethodStringAndUlongAndMaskLength).ToList();
|
||||
|
||||
var methodsInfoWithoutGet = methodsInfo.Where(m => m.HttpMethod != HttpMethod.Get.ToString()).ToList();
|
||||
|
||||
var methodsAsciiStringAsLong = methodsInfo.Select(m => m.AsciiStringAsLong).ToArray();
|
||||
|
||||
var mask = HttpUtilitiesGeneratorHelpers.SearchKeyByLookThroughMaskCombinations(methodsAsciiStringAsLong, 0, sizeof(ulong) * 8, maskLength);
|
||||
|
||||
if (mask.HasValue == false)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Generated {0} not found.", nameof(mask)));
|
||||
}
|
||||
|
||||
var functionGetKnownMethodIndex = GetFunctionBodyGetKnownMethodIndex(mask.Value);
|
||||
|
||||
var methodsSection = GetMethodsSection(methodsInfoWithoutGet);
|
||||
|
||||
var masksSection = GetMasksSection(methodsInfoWithoutGet);
|
||||
|
||||
var setKnownMethodSection = GetSetKnownMethodSection(methodsInfoWithoutGet);
|
||||
var methodNamesSection = GetMethodNamesSection(methodsInfo);
|
||||
|
||||
int knownMethodsArrayLength = (int)(Math.Pow(2, maskLength) + 1);
|
||||
int methodNamesArrayLength = httpMethods.Length;
|
||||
|
||||
return string.Format(@"// 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;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||
{{
|
||||
public static partial class HttpUtilities
|
||||
{{
|
||||
// readonly primitive statics can be Jit'd to consts https://github.com/dotnet/coreclr/issues/1079
|
||||
{0}
|
||||
|
||||
{1}
|
||||
private readonly static Tuple<ulong, ulong, HttpMethod, int>[] _knownMethods =
|
||||
new Tuple<ulong, ulong, HttpMethod, int>[{2}];
|
||||
|
||||
private readonly static string[] _methodNames = new string[{3}];
|
||||
|
||||
static HttpUtilities()
|
||||
{{
|
||||
{4}
|
||||
FillKnownMethodsGaps();
|
||||
{5}
|
||||
}}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetKnownMethodIndex(ulong value)
|
||||
{{
|
||||
{6}
|
||||
}}
|
||||
}}
|
||||
}}", methodsSection, masksSection, knownMethodsArrayLength, methodNamesArrayLength, setKnownMethodSection, methodNamesSection, functionGetKnownMethodIndex);
|
||||
}
|
||||
|
||||
private static string GetMethodsSection(List<MethodInfo> methodsInfo)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
|
||||
for (var index = 0; index < methodsInfo.Count; index++)
|
||||
{
|
||||
var methodInfo = methodsInfo[index];
|
||||
|
||||
var httpMethodFieldName = GetHttpMethodFieldName(methodInfo);
|
||||
result.AppendFormat(" private readonly static ulong {0} = GetAsciiStringAsLong(\"{1}\");", httpMethodFieldName, methodInfo.MethodAsciiString.Replace("\0", "\\0"));
|
||||
|
||||
if (index < methodsInfo.Count - 1)
|
||||
{
|
||||
result.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string GetMasksSection(List<MethodInfo> methodsInfo)
|
||||
{
|
||||
var distinctLengths = methodsInfo.Select(m => m.MaskLength).Distinct().ToList();
|
||||
|
||||
distinctLengths.Sort((t1, t2) => -t1.CompareTo(t2));
|
||||
|
||||
var result = new StringBuilder();
|
||||
|
||||
for (var index = 0; index < distinctLengths.Count; index++)
|
||||
{
|
||||
var maskBytesLength = distinctLengths[index];
|
||||
var maskArray = GetMaskArray(maskBytesLength);
|
||||
|
||||
var hexMaskString = HttpUtilitiesGeneratorHelpers.GeHexString(maskArray, "0x", ", ");
|
||||
var maskFieldName = GetMaskFieldName(maskBytesLength);
|
||||
|
||||
result.AppendFormat(" private readonly static ulong {0} = GetMaskAsLong(new byte[]\r\n {{{1}}});", maskFieldName, hexMaskString);
|
||||
result.AppendLine();
|
||||
if (index < distinctLengths.Count - 1)
|
||||
{
|
||||
result.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string GetSetKnownMethodSection(List<MethodInfo> methodsInfo)
|
||||
{
|
||||
methodsInfo = methodsInfo.ToList();
|
||||
|
||||
methodsInfo.Sort((t1, t2) => t1.MaskLength.CompareTo(t2.MaskLength));
|
||||
|
||||
var result = new StringBuilder();
|
||||
|
||||
for (var index = 0; index < methodsInfo.Count; index++)
|
||||
{
|
||||
var methodInfo = methodsInfo[index];
|
||||
var maskFieldName = GetMaskFieldName(methodInfo.MaskLength);
|
||||
var httpMethodFieldName = GetHttpMethodFieldName(methodInfo);
|
||||
|
||||
result.AppendFormat(" SetKnownMethod({0}, {1}, {2}.{3}, {4});", maskFieldName, httpMethodFieldName, typeof(HttpMethod).Name, methodInfo.HttpMethod, methodInfo.MaskLength - 1);
|
||||
|
||||
if (index < methodsInfo.Count - 1)
|
||||
{
|
||||
result.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string GetMethodNamesSection(List<MethodInfo> methodsInfo)
|
||||
{
|
||||
methodsInfo = methodsInfo.ToList();
|
||||
|
||||
methodsInfo.Sort((t1, t2) => t1.HttpMethod.CompareTo(t2.HttpMethod));
|
||||
|
||||
var result = new StringBuilder();
|
||||
|
||||
for (var index = 0; index < methodsInfo.Count; index++)
|
||||
{
|
||||
var methodInfo = methodsInfo[index];
|
||||
|
||||
result.AppendFormat(" _methodNames[(byte){0}.{1}] = {2}.{3};", typeof(HttpMethod).Name, methodInfo.HttpMethod, typeof(HttpMethods).Name, methodInfo.HttpMethod);
|
||||
|
||||
if (index < methodsInfo.Count - 1)
|
||||
{
|
||||
result.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static string GetFunctionBodyGetKnownMethodIndex(ulong mask)
|
||||
{
|
||||
var shifts = HttpUtilitiesGeneratorHelpers.GetShifts(mask);
|
||||
|
||||
var maskHexString = HttpUtilitiesGeneratorHelpers.MaskToHexString(mask);
|
||||
|
||||
string bodyString;
|
||||
|
||||
if (shifts.Length > 0)
|
||||
{
|
||||
var bitsCount = HttpUtilitiesGeneratorHelpers.CountBits(mask);
|
||||
|
||||
var tmpReturn = string.Empty;
|
||||
foreach (var item in shifts)
|
||||
{
|
||||
if (tmpReturn.Length > 0)
|
||||
{
|
||||
tmpReturn += " | ";
|
||||
}
|
||||
|
||||
tmpReturn += string.Format("(tmp >> {1})", HttpUtilitiesGeneratorHelpers.MaskToHexString(item.Mask), item.Shift);
|
||||
}
|
||||
|
||||
var mask2 = (ulong)(Math.Pow(2, bitsCount) - 1);
|
||||
|
||||
string returnString = string.Format("return ({0}) & {1};", tmpReturn, HttpUtilitiesGeneratorHelpers.MaskToHexString(mask2));
|
||||
|
||||
bodyString = string.Format(" const int magicNumer = {0};\r\n var tmp = (int)value & magicNumer;\r\n {1}", HttpUtilitiesGeneratorHelpers.MaskToHexString(mask), returnString);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyString = string.Format("return (int)(value & {0});", maskHexString);
|
||||
}
|
||||
|
||||
return bodyString;
|
||||
}
|
||||
|
||||
private static string GetHttpMethodFieldName(MethodInfo methodsInfo)
|
||||
{
|
||||
return string.Format("_http{0}MethodLong", methodsInfo.HttpMethod.ToString());
|
||||
}
|
||||
|
||||
private static string GetMaskFieldName(int nBytes)
|
||||
{
|
||||
return string.Format("_mask{0}Chars", nBytes);
|
||||
}
|
||||
|
||||
private static string GetMethodString(string method)
|
||||
{
|
||||
if (method == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(method));
|
||||
}
|
||||
|
||||
const int length = sizeof(ulong);
|
||||
|
||||
if (method.Length > length)
|
||||
{
|
||||
throw new ArgumentException(string.Format("MethodAsciiString {0} length is greather than {1}", method, length));
|
||||
}
|
||||
string result = method;
|
||||
|
||||
if (result.Length == length)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.Length < length)
|
||||
{
|
||||
var count = length - result.Length;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
result += "\0";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class MethodInfo
|
||||
{
|
||||
public string MethodAsciiString;
|
||||
public ulong AsciiStringAsLong;
|
||||
public string HttpMethod;
|
||||
public int MaskLength;
|
||||
}
|
||||
|
||||
private static MethodInfo GetMethodStringAndUlongAndMaskLength(Tuple<string, HttpMethod> method)
|
||||
{
|
||||
var methodString = GetMethodString(method.Item1);
|
||||
|
||||
var asciiAsLong = GetAsciiStringAsLong(methodString);
|
||||
|
||||
return new MethodInfo
|
||||
{
|
||||
MethodAsciiString = methodString,
|
||||
AsciiStringAsLong = asciiAsLong,
|
||||
HttpMethod = method.Item2.ToString(),
|
||||
MaskLength = method.Item1.Length
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] GetMaskArray(int n, int length = sizeof(ulong))
|
||||
{
|
||||
var maskArray = new byte[length];
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
maskArray[i] = 0xff;
|
||||
}
|
||||
return maskArray;
|
||||
}
|
||||
|
||||
private unsafe static ulong GetAsciiStringAsLong(string str)
|
||||
{
|
||||
Debug.Assert(str.Length == sizeof(ulong), string.Format("String must be exactly {0} (ASCII) characters long.", sizeof(ulong)));
|
||||
|
||||
var bytes = Encoding.ASCII.GetBytes(str);
|
||||
|
||||
fixed (byte* ptr = &bytes[0])
|
||||
{
|
||||
return *(ulong*)ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace CodeGenerator.HttpUtilities
|
||||
{
|
||||
internal class HttpUtilitiesGeneratorHelpers
|
||||
{
|
||||
public class ShiftInfo<TMask>
|
||||
{
|
||||
public TMask Mask;
|
||||
public byte Shift;
|
||||
}
|
||||
|
||||
public static ShiftInfo<ulong>[] GetShifts(ulong mask)
|
||||
{
|
||||
var shifts = new List<ShiftInfo<ulong>>();
|
||||
|
||||
const ulong one = 0x01;
|
||||
|
||||
ulong currentMask = 0;
|
||||
|
||||
int currentBitsCount = 0;
|
||||
int lastShift = 0;
|
||||
for (int i = 0; i < sizeof(ulong) * 8; i++)
|
||||
{
|
||||
var currentBitMask = one << i;
|
||||
bool isCurrentBit0 = (currentBitMask & mask) == 0;
|
||||
|
||||
if (isCurrentBit0 == false)
|
||||
{
|
||||
currentMask |= currentBitMask;
|
||||
currentBitsCount++;
|
||||
}
|
||||
else if (currentBitsCount > 0)
|
||||
{
|
||||
var currentShift = (byte)(i - currentBitsCount - lastShift);
|
||||
shifts.Add(new ShiftInfo<ulong>
|
||||
{
|
||||
Mask = currentMask,
|
||||
Shift = currentShift
|
||||
});
|
||||
lastShift = currentShift;
|
||||
currentMask = 0;
|
||||
currentBitsCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return shifts.ToArray();
|
||||
}
|
||||
|
||||
public static ulong? SearchKeyByLookThroughMaskCombinations(ulong[] values, byte bitsIndexStart, byte bitsLength, byte bitsCount)
|
||||
{
|
||||
if (bitsIndexStart + bitsLength > sizeof(ulong) * 8)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bitsIndexStart));
|
||||
}
|
||||
|
||||
if (bitsLength < bitsCount || bitsCount == 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bitsCount));
|
||||
}
|
||||
|
||||
var bits = new byte[bitsLength];
|
||||
|
||||
for (byte i = bitsIndexStart; i < bitsIndexStart + bitsLength; i++)
|
||||
{
|
||||
bits[i - bitsIndexStart] = i;
|
||||
}
|
||||
|
||||
var combinations = new CombinationsWithoutRepetition<byte>(bits, bitsCount);
|
||||
|
||||
ulong? maskFound = null;
|
||||
int bit1ChunksFoundMask = 0;
|
||||
|
||||
int arrayLength = values.Length;
|
||||
|
||||
var mashHash = new HashSet<ulong>();
|
||||
|
||||
while (combinations.MoveNext())
|
||||
{
|
||||
var bitsCombination = combinations.Current;
|
||||
|
||||
ulong currentMask = 0;
|
||||
|
||||
for (int i = 0; i < bitsCombination.Length; i++)
|
||||
{
|
||||
var index = bitsCombination[i];
|
||||
|
||||
const ulong oneBit = 0x01;
|
||||
|
||||
currentMask |= oneBit << index;
|
||||
}
|
||||
|
||||
mashHash.Clear();
|
||||
bool invalidMask = false;
|
||||
for (int j = 0; j < arrayLength; j++)
|
||||
{
|
||||
var tmp = values[j] & currentMask;
|
||||
|
||||
bool alreadyExists = mashHash.Add(tmp) == false;
|
||||
if (alreadyExists)
|
||||
{
|
||||
invalidMask = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidMask == false)
|
||||
{
|
||||
var bit1Chunks = CountBit1Chunks(currentMask);
|
||||
|
||||
if (maskFound.HasValue)
|
||||
{
|
||||
if (bit1ChunksFoundMask > bit1Chunks)
|
||||
{
|
||||
maskFound = currentMask;
|
||||
bit1ChunksFoundMask = bit1Chunks;
|
||||
if (bit1ChunksFoundMask == 0)
|
||||
{
|
||||
return maskFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
maskFound = currentMask;
|
||||
bit1ChunksFoundMask = bit1Chunks;
|
||||
|
||||
if (bit1ChunksFoundMask == 0)
|
||||
{
|
||||
return maskFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maskFound;
|
||||
}
|
||||
|
||||
public static int CountBit1Chunks(ulong mask)
|
||||
{
|
||||
int currentBitsCount = 0;
|
||||
|
||||
int chunks = 0;
|
||||
|
||||
for (int i = 0; i < sizeof(ulong) * 8; i++)
|
||||
{
|
||||
const ulong oneBit = 0x01;
|
||||
|
||||
var currentBitMask = oneBit << i;
|
||||
bool isCurrentBit0 = (currentBitMask & mask) == 0;
|
||||
|
||||
if (isCurrentBit0 == false)
|
||||
{
|
||||
currentBitsCount++;
|
||||
}
|
||||
else if (currentBitsCount > 0)
|
||||
{
|
||||
chunks++;
|
||||
currentBitsCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public static string GeHexString(byte[] array, string prefix, string separator)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
int i = 0;
|
||||
for (; i < array.Length - 1; i++)
|
||||
{
|
||||
result.AppendFormat("{0}{1:x2}", prefix, array[i]);
|
||||
result.Append(separator);
|
||||
}
|
||||
|
||||
if (array.Length > 0)
|
||||
{
|
||||
result.AppendFormat("{0}{1:x2}", prefix, array[i]);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static string MaskToString(ulong mask)
|
||||
{
|
||||
var maskSizeInBIts = Math.Log(mask, 2);
|
||||
var hexMaskSize = Math.Ceiling(maskSizeInBIts / 4.0);
|
||||
return string.Format("0x{0:X" + hexMaskSize + "}", mask);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CountBits(ulong v)
|
||||
{
|
||||
const ulong Mask01010101 = 0x5555555555555555UL;
|
||||
const ulong Mask00110011 = 0x3333333333333333UL;
|
||||
const ulong Mask00001111 = 0x0F0F0F0F0F0F0F0FUL;
|
||||
const ulong Mask00000001 = 0x0101010101010101UL;
|
||||
v = v - ((v >> 1) & Mask01010101);
|
||||
v = (v & Mask00110011) + ((v >> 2) & Mask00110011);
|
||||
return (int)(unchecked(((v + (v >> 4)) & Mask00001111) * Mask00000001) >> 56);
|
||||
}
|
||||
|
||||
public static string MaskToHexString(ulong mask)
|
||||
{
|
||||
var maskSizeInBIts = Math.Log(mask, 2);
|
||||
var hexMaskSize = (byte)Math.Ceiling(maskSizeInBIts / 4);
|
||||
|
||||
return string.Format("0x{0:X" + (hexMaskSize == 0 ? 1 : hexMaskSize) + "}", mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,16 +20,22 @@ namespace CodeGenerator
|
|||
Console.Error.WriteLine("Missing path to Frame.Generated.cs");
|
||||
return 1;
|
||||
}
|
||||
else if (args.Length < 3)
|
||||
{
|
||||
Console.Error.WriteLine("Missing path to HttpUtilities.Generated.cs");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Run(args[0], args[1]);
|
||||
Run(args[0], args[1], args[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void Run(string knownHeadersPath, string frameFeaturesCollectionPath)
|
||||
public static void Run(string knownHeadersPath, string frameFeaturesCollectionPath, string httpUtilitiesPath)
|
||||
{
|
||||
var knownHeadersContent = KnownHeaders.GeneratedFile();
|
||||
var frameFeatureCollectionContent = FrameFeatureCollection.GeneratedFile();
|
||||
var httpUtilitiesContent = HttpUtilities.HttpUtilities.GeneratedFile();
|
||||
|
||||
var existingKnownHeaders = File.Exists(knownHeadersPath) ? File.ReadAllText(knownHeadersPath) : "";
|
||||
if (!string.Equals(knownHeadersContent, existingKnownHeaders))
|
||||
|
|
@ -42,6 +48,12 @@ namespace CodeGenerator
|
|||
{
|
||||
File.WriteAllText(frameFeaturesCollectionPath, frameFeatureCollectionContent);
|
||||
}
|
||||
|
||||
var existingHttpUtilities = File.Exists(httpUtilitiesPath) ? File.ReadAllText(httpUtilitiesPath) : "";
|
||||
if (!string.Equals(httpUtilitiesContent, existingHttpUtilities))
|
||||
{
|
||||
File.WriteAllText(httpUtilitiesPath, httpUtilitiesContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue