|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements.
|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 |
|
4 |
| -using System.Collections.ObjectModel; |
| 4 | +using System.Buffers; |
5 | 5 | using System.Diagnostics;
|
6 | 6 | using System.Diagnostics.CodeAnalysis;
|
7 | 7 | using System.Globalization;
|
@@ -116,9 +116,11 @@ public static partial class RequestDelegateFactory
|
116 | 116 | private static readonly MemberExpression FilterContextHttpContextStatusCodeExpr = Expression.Property(FilterContextHttpContextResponseExpr, typeof(HttpResponse).GetProperty(nameof(HttpResponse.StatusCode))!);
|
117 | 117 | private static readonly ParameterExpression InvokedFilterContextExpr = Expression.Parameter(typeof(EndpointFilterInvocationContext), "filterContext");
|
118 | 118 |
|
119 |
| - private static readonly ConstructorInfo FormDataReaderConstructor = typeof(FormDataReader).GetConstructor(new[] { typeof(IReadOnlyDictionary<string, StringValues>), typeof(CultureInfo) })!; |
120 |
| - private static readonly MethodInfo FormToReadOnlyDictionaryMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ToReadOnlyDictionary), BindingFlags.Static | BindingFlags.NonPublic, new[] { typeof(IFormCollection) })!; |
| 119 | + private static readonly ConstructorInfo FormDataReaderConstructor = typeof(FormDataReader).GetConstructor(new[] { typeof(IReadOnlyDictionary<FormKey, StringValues>), typeof(CultureInfo), typeof(Memory<char>) })!; |
| 120 | + private static readonly MethodInfo ProcessFormMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ProcessForm), BindingFlags.Static | BindingFlags.NonPublic)!; |
121 | 121 | private static readonly MethodInfo FormDataMapperMapMethod = typeof(FormDataMapper).GetMethod(nameof(FormDataMapper.Map))!;
|
| 122 | + private static readonly MethodInfo AsMemoryMethod = new Func<char[]?, int, int, Memory<char>>(MemoryExtensions.AsMemory).Method; |
| 123 | + private static readonly MethodInfo ArrayPoolSharedReturnMethod = typeof(ArrayPool<char>).GetMethod(nameof(ArrayPool<char>.Shared.Return))!; |
122 | 124 |
|
123 | 125 | private static readonly string[] DefaultAcceptsAndProducesContentType = new[] { JsonConstants.JsonContentType };
|
124 | 126 | private static readonly string[] FormFileContentType = new[] { "multipart/form-data" };
|
@@ -738,7 +740,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat
|
738 | 740 | return BindParameterFromFormCollection(parameter, factoryContext);
|
739 | 741 | }
|
740 | 742 | // Continue to use the simple binding support that exists in RDF/RDG for currently
|
741 |
| - // supported scenarios to maintain compatible semantics between versions of RDG. |
| 743 | + // supported scenarios to maintain compatible semantics between versions of RDG. |
742 | 744 | // For complex types, leverage the shared form binding infrastructure. For example,
|
743 | 745 | // shared form binding does not currently only supports types that implement IParsable
|
744 | 746 | // while RDF's binding implementation supports all TryParse implementations.
|
@@ -1972,30 +1974,56 @@ private static Expression BindComplexParameterFromFormItem(
|
1972 | 1974 |
|
1973 | 1975 | // var name_local;
|
1974 | 1976 | // var name_reader;
|
| 1977 | + // var form_dict; |
| 1978 | + // var form_buffer; |
| 1979 | + // var form_max_key_length; |
1975 | 1980 | var formArgument = Expression.Variable(parameter.ParameterType, $"{parameter.Name}_local");
|
1976 | 1981 | var formReader = Expression.Variable(typeof(FormDataReader), $"{parameter.Name}_reader");
|
| 1982 | + var formDict = Expression.Variable(typeof(IReadOnlyDictionary<FormKey, StringValues>), "form_dict"); |
| 1983 | + var formBuffer = Expression.Variable(typeof(char[]), "form_buffer"); |
| 1984 | + var formMaxKeyLength = Expression.Variable(typeof(int), "form_max_key_length"); |
1977 | 1985 |
|
1978 |
| - // name_reader = new FormDataReader(context.Request.Form.ToReadOnlyDictionary()), CultureInfo.InvariantCulture); |
| 1986 | + // ProcessForm(context.Request.Form, form_dict, form_max_key_length, form_buffer); |
| 1987 | + var processFormExpr = Expression.Call(ProcessFormMethod, FormExpr, formDict, formMaxKeyLength, formBuffer); |
| 1988 | + // name_reader = new FormDataReader(form_dict, CultureInfo.InvariantCulture, form_buffer.AsMemory(0, form_max_key_length)); |
1979 | 1989 | var initializeReaderExpr = Expression.Assign(
|
1980 | 1990 | formReader,
|
1981 | 1991 | Expression.New(FormDataReaderConstructor,
|
1982 |
| - Expression.Call(FormToReadOnlyDictionaryMethod, FormExpr), |
1983 |
| - Expression.Constant(CultureInfo.InvariantCulture))); |
| 1992 | + formDict, |
| 1993 | + Expression.Constant(CultureInfo.InvariantCulture), |
| 1994 | + Expression.Call(AsMemoryMethod, formBuffer, Expression.Constant(0), formMaxKeyLength))); |
1984 | 1995 | // FormDataMapper.Map<string>(name_reader, FormDataMapperOptions);
|
1985 | 1996 | var invokeMapMethodExpr = Expression.Call(
|
1986 | 1997 | FormDataMapperMapMethod.MakeGenericMethod(parameter.ParameterType),
|
1987 | 1998 | formReader,
|
1988 | 1999 | Expression.Constant(FormDataMapperOptions));
|
| 2000 | + // ArrayPool<char>.Shared.Return(form_buffer); |
| 2001 | + var returnBufferExpr = Expression.Call(ArrayPoolSharedReturnMethod, formBuffer); |
1989 | 2002 |
|
1990 | 2003 | return Expression.Block(
|
1991 |
| - new[] { formArgument, formReader }, |
| 2004 | + new[] { formArgument, formReader, formDict, formBuffer }, |
| 2005 | + processFormExpr, |
1992 | 2006 | initializeReaderExpr,
|
1993 |
| - Expression.Assign(formArgument, invokeMapMethodExpr) |
| 2007 | + Expression.Assign(formArgument, invokeMapMethodExpr), |
| 2008 | + returnBufferExpr |
1994 | 2009 | );
|
1995 | 2010 | }
|
1996 | 2011 |
|
1997 |
| - private static IReadOnlyDictionary<string, StringValues> ToReadOnlyDictionary(IFormCollection form) |
1998 |
| - => new ReadOnlyDictionary<string, StringValues>(form.ToDictionary()); |
| 2012 | + private static void ProcessForm(IFormCollection form, ref IReadOnlyDictionary<FormKey, StringValues> formDictionary, ref int maxKeyLength, ref char[] buffer) |
| 2013 | + { |
| 2014 | + var dictionary = new Dictionary<FormKey, StringValues>(); |
| 2015 | + maxKeyLength = -1; |
| 2016 | + foreach (var (key, value) in form) |
| 2017 | + { |
| 2018 | + if (key.Length > maxKeyLength) |
| 2019 | + { |
| 2020 | + maxKeyLength = key.Length; |
| 2021 | + } |
| 2022 | + dictionary.Add(new FormKey(key.AsMemory()), value); |
| 2023 | + } |
| 2024 | + formDictionary = dictionary.AsReadOnly(); |
| 2025 | + buffer = ArrayPool<char>.Shared.Rent(maxKeyLength); |
| 2026 | + } |
1999 | 2027 |
|
2000 | 2028 | private static Expression BindParameterFromFormFiles(
|
2001 | 2029 | ParameterInfo parameter,
|
|
0 commit comments