diff --git a/src/Http/Http/perf/Microbenchmarks/QueryCollectionBenchmarks.cs b/src/Http/Http/perf/Microbenchmarks/QueryCollectionBenchmarks.cs index 6d5c8a93f63f..8804ba2ca495 100644 --- a/src/Http/Http/perf/Microbenchmarks/QueryCollectionBenchmarks.cs +++ b/src/Http/Http/perf/Microbenchmarks/QueryCollectionBenchmarks.cs @@ -13,19 +13,10 @@ namespace Microsoft.AspNetCore.Http; [CategoriesColumn] public class QueryCollectionBenchmarks { - private string _queryString; - private string _singleValue; - private string _singleValueWithPlus; - private string _encoded; - - [IterationSetup] - public void Setup() - { - _queryString = "?key1=value1&key2=value2&key3=value3&key4=&key5="; - _singleValue = "?key1=value1"; - _singleValueWithPlus = "?key1=value1+value2+value3"; - _encoded = "?key1=value%231"; - } + private const string _queryString = "?key1=value1&key2=value2&key3=value3&key4=&key5="; + private const string _singleValue = "?key1=value1"; + private const string _singleValueWithPlus = "?key1=value1+value2+value3"; + private const string _encoded = "?key1=value%231"; [Benchmark(Description = "ParseNew")] [BenchmarkCategory("QueryString")] diff --git a/src/Shared/QueryStringEnumerable.cs b/src/Shared/QueryStringEnumerable.cs index b857af1ac88c..65b74994934a 100644 --- a/src/Shared/QueryStringEnumerable.cs +++ b/src/Shared/QueryStringEnumerable.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -88,21 +89,18 @@ public ReadOnlyMemory DecodeName() public ReadOnlyMemory DecodeValue() => Decode(EncodedValue); - private static unsafe ReadOnlyMemory Decode(ReadOnlyMemory chars) + private static ReadOnlyMemory Decode(ReadOnlyMemory chars) { - // If the value is short, it's cheap to check up front if it really needs decoding. If it doesn't, - // then we can save some allocations. - if (chars.Length < 16 && chars.Span.IndexOfAny('%', '+') < 0) + ReadOnlySpan source = chars.Span; + if (!source.ContainsAny('%', '+')) { return chars; } - -#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type - ReadOnlySpan span = chars.Span; - return Uri.UnescapeDataString( - string.Create(span.Length, - (IntPtr)(&span), static (dest, ptr) => ((ReadOnlySpan*)ptr)->Replace(dest, '+', ' '))).AsMemory(); -#pragma warning restore CS8500 + var buffer = new char[source.Length]; + source.Replace(buffer, '+', ' '); + var success = Uri.TryUnescapeDataString(buffer, buffer, out var unescapedLength); + Debug.Assert(success); + return buffer.AsMemory(0, unescapedLength); } }