Add HttpUtilities to CodeGenerator

This commit is contained in:
arespr 2017-03-08 19:17:12 +01:00
parent 0a45cbbb95
commit 0bca84a268
8 changed files with 745 additions and 63 deletions

View File

@ -0,0 +1,62 @@
// 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, bool>[] _knownMethods = new Tuple<ulong, ulong, HttpMethod, int, bool>[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;
}
}
}

View File

@ -5,70 +5,20 @@ using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.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";
// 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 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 });
private readonly static Tuple<ulong, ulong, HttpMethod, int, bool>[] _knownMethods = new Tuple<ulong, ulong, HttpMethod, int, bool>[17];
private readonly static string[] _methodNames = new string[9];
static HttpUtilities()
{
SetKnownMethod(_mask4Chars, _httpPutMethodLong, HttpMethod.Put, 3);
SetKnownMethod(_mask5Chars, _httpPostMethodLong, HttpMethod.Post, 4);
SetKnownMethod(_mask5Chars, _httpHeadMethodLong, HttpMethod.Head, 4);
SetKnownMethod(_mask6Chars, _httpTraceMethodLong, HttpMethod.Trace, 5);
SetKnownMethod(_mask6Chars, _httpPatchMethodLong, HttpMethod.Patch, 5);
SetKnownMethod(_mask7Chars, _httpDeleteMethodLong, HttpMethod.Delete, 6);
SetKnownMethod(_mask8Chars, _httpConnectMethodLong, HttpMethod.Connect, 7);
SetKnownMethod(_mask8Chars, _httpOptionsMethodLong, HttpMethod.Options, 7);
FillKnownMethodsGaps();
_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;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetKnownMethodIndex(ulong value)
{
const int magicNumer = 0x0600000C;
var tmp = (int)value & magicNumer;
return ((tmp >> 2) | (tmp >> 23)) & 0x0F;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetKnownMethod(ulong mask, ulong knownMethodUlong, HttpMethod knownMethod, int length)
@ -76,6 +26,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
_knownMethods[GetKnownMethodIndex(knownMethodUlong)] = new Tuple<ulong, ulong, HttpMethod, int, bool>(mask, knownMethodUlong, knownMethod, length, true);
}
private unsafe static ulong GetMaskAsLong(byte[] bytes)
{
Debug.Assert(bytes.Length == 8, "Mask must be exactly 8 bytes long.");
fixed (byte* ptr = bytes)
{
return *(ulong*)ptr;
}
}
private static void FillKnownMethodsGaps()
{
var knownMethods = _knownMethods;
@ -114,15 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
}
private unsafe static ulong GetMaskAsLong(byte[] bytes)
{
Debug.Assert(bytes.Length == 8, "Mask must be exactly 8 bytes long.");
fixed (byte* ptr = bytes)
{
return *(ulong*)ptr;
}
}
public unsafe static string GetAsciiStringNonNullCharacters(this Span<byte> span)
{

View File

@ -11,6 +11,10 @@
<NoWarn>CS1591;$(NoWarn)</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Include="Internal\Infrastructure\HttpUtilities.Generated.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Libuv" Version="$(LibUvVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.2.0-*" />

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
@ -13,9 +13,21 @@
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="1.2.0-*" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj" />
</ItemGroup>
<PropertyGroup>
<StartWorkingDirectory>$(MSBuildThisFileDirectory)..\..\src\Microsoft.AspNetCore.Server.Kestrel\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>

View File

@ -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;
}
}
}

View File

@ -0,0 +1,320 @@
// 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, bool>[] _knownMethods = new Tuple<ulong, ulong, HttpMethod, int, bool>[{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("\t\tprivate 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("\t\tprivate readonly static ulong {0} = GetMaskAsLong(new byte[] {{ {1} }});", maskFieldName, hexMaskString);
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("\t\t\tSetKnownMethod({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("\t\t\t_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\t\t\tvar tmp = (int)value & magicNumer;\r\n\t\t\t{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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}