diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.Generated.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.Generated.cs new file mode 100644 index 0000000000..c6cec88193 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.Generated.cs @@ -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[] _knownMethods = new Tuple[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; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs index da9719a841..9d33889fe8 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs @@ -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[] _knownMethods = new Tuple[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(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 span) { diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj b/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj index 35ccda46b3..93bebcbe0f 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj @@ -11,6 +11,10 @@ CS1591;$(NoWarn) + + + + diff --git a/tools/CodeGenerator/CodeGenerator.csproj b/tools/CodeGenerator/CodeGenerator.csproj index d581f31921..b4d8bf1d86 100644 --- a/tools/CodeGenerator/CodeGenerator.csproj +++ b/tools/CodeGenerator/CodeGenerator.csproj @@ -1,4 +1,4 @@ - + @@ -13,9 +13,21 @@ + + + + $(MSBuildThisFileDirectory)..\..\src\Microsoft.AspNetCore.Server.Kestrel\Internal\Http FrameHeaders.Generated.cs Frame.Generated.cs + + True + + + + True + + diff --git a/tools/CodeGenerator/HttpUtilities/CombinationsWithoutRepetition.cs b/tools/CodeGenerator/HttpUtilities/CombinationsWithoutRepetition.cs new file mode 100644 index 0000000000..b7de0f4c4e --- /dev/null +++ b/tools/CodeGenerator/HttpUtilities/CombinationsWithoutRepetition.cs @@ -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 : IEnumerator + { + 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; + } + } +} diff --git a/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs b/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs new file mode 100644 index 0000000000..2c674fc71b --- /dev/null +++ b/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs @@ -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("CONNECT ", HttpMethod.Connect), + new Tuple("DELETE ", HttpMethod.Delete), + new Tuple("HEAD ", HttpMethod.Head), + new Tuple("PATCH ", HttpMethod.Patch), + new Tuple("POST ", HttpMethod.Post), + new Tuple("PUT ", HttpMethod.Put), + new Tuple("OPTIONS ", HttpMethod.Options), + new Tuple("TRACE ", HttpMethod.Trace), + new Tuple("GET ", HttpMethod.Get) + }; + + return GenerateFile(httpMethods); + } + + private static string GenerateFile(Tuple[] 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[] _knownMethods = new Tuple[{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 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 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 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 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 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; + } + } + } +} \ No newline at end of file diff --git a/tools/CodeGenerator/HttpUtilities/HttpUtilitiesGeneratorHelpers.cs b/tools/CodeGenerator/HttpUtilities/HttpUtilitiesGeneratorHelpers.cs new file mode 100644 index 0000000000..0cadd80aa0 --- /dev/null +++ b/tools/CodeGenerator/HttpUtilities/HttpUtilitiesGeneratorHelpers.cs @@ -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 + { + public TMask Mask; + public byte Shift; + } + + public static ShiftInfo[] GetShifts(ulong mask) + { + var shifts = new List>(); + + 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 + { + 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(bits, bitsCount); + + ulong? maskFound = null; + int bit1ChunksFoundMask = 0; + + int arrayLength = values.Length; + + var mashHash = new HashSet(); + + 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); + } + } +} diff --git a/tools/CodeGenerator/Program.cs b/tools/CodeGenerator/Program.cs index a057b078c6..24be8360b9 100644 --- a/tools/CodeGenerator/Program.cs +++ b/tools/CodeGenerator/Program.cs @@ -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); + } } } }