Skip to content

Commit b9f61a0

Browse files
committed
Combine ReplacePlusWithSpace and string materialization and void span-copying to a scratch-buffer
1 parent a6353d1 commit b9f61a0

File tree

1 file changed

+37
-44
lines changed

1 file changed

+37
-44
lines changed

src/Http/Http/src/Features/QueryFeature.cs

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Buffers;
66
using System.Collections.Generic;
77
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
89
using System.Runtime.Intrinsics;
910
using System.Runtime.Intrinsics.X86;
1011
using Microsoft.AspNetCore.Internal;
@@ -111,16 +112,8 @@ public IQueryCollection Query
111112
return null;
112113
}
113114

114-
KvpAccumulator accumulator = new();
115-
var queryStringLength = queryString.Length;
116-
117-
char[]? arryToReturnToPool = null;
118-
Span<char> query = (queryStringLength <= 128
119-
? stackalloc char[128]
120-
: arryToReturnToPool = ArrayPool<char>.Shared.Rent(queryStringLength)
121-
).Slice(0, queryStringLength);
122-
123-
queryString.AsSpan().CopyTo(query);
115+
var accumulator = new KvpAccumulator();
116+
var query = queryString.AsSpan();
124117

125118
if (query[0] == '?')
126119
{
@@ -148,15 +141,12 @@ public IQueryCollection Query
148141
}
149142
}
150143

151-
var name = querySegment[i..equalIndex];
152-
var value = querySegment.Slice(equalIndex + 1);
153-
154-
SpanHelper.ReplacePlusWithSpaceInPlace(name);
155-
SpanHelper.ReplacePlusWithSpaceInPlace(value);
144+
var name = SpanHelper.ReplacePlusWithSpace(querySegment[i..equalIndex]);
145+
var value = SpanHelper.ReplacePlusWithSpace(querySegment.Slice(equalIndex + 1));
156146

157147
accumulator.Append(
158-
Uri.UnescapeDataString(name.ToString()),
159-
Uri.UnescapeDataString(value.ToString()));
148+
Uri.UnescapeDataString(name),
149+
Uri.UnescapeDataString(value));
160150
}
161151
else
162152
{
@@ -174,17 +164,9 @@ public IQueryCollection Query
174164
query = query.Slice(delimiterIndex + 1);
175165
}
176166

177-
if (arryToReturnToPool is not null)
178-
{
179-
ArrayPool<char>.Shared.Return(arryToReturnToPool);
180-
}
181-
182-
if (!accumulator.HasValues)
183-
{
184-
return null;
185-
}
186-
187-
return accumulator.GetResults();
167+
return accumulator.HasValues
168+
? accumulator.GetResults()
169+
: null;
188170
}
189171

190172
internal struct KvpAccumulator
@@ -288,40 +270,51 @@ public AdaptiveCapacityDictionary<string, StringValues> GetResults()
288270

289271
private static class SpanHelper
290272
{
291-
public static void ReplacePlusWithSpaceInPlace(Span<char> span)
292-
=> ReplaceInPlace(span, '+', ' ');
273+
private static readonly SpanAction<char, IntPtr> s_replacePlusWithSpace = ReplacePlusWithSpaceCore;
293274

294275
[MethodImpl(MethodImplOptions.AggressiveInlining)]
295-
public static unsafe void ReplaceInPlace(Span<char> span, char oldChar, char newChar)
276+
public static unsafe string ReplacePlusWithSpace(ReadOnlySpan<char> span)
296277
{
297-
var i = (nint)0;
298-
var n = (nint)(uint)span.Length;
278+
fixed (char* ptr = &MemoryMarshal.GetReference(span))
279+
{
280+
return string.Create(span.Length, (IntPtr)ptr, s_replacePlusWithSpace);
281+
}
282+
}
299283

300-
fixed (char* ptr = span)
284+
private static unsafe void ReplacePlusWithSpaceCore(Span<char> buffer, IntPtr state)
285+
{
286+
fixed (char* ptr = &MemoryMarshal.GetReference(buffer))
301287
{
302-
var pVec = (ushort*)ptr;
288+
var input = (ushort*)state.ToPointer();
289+
var output = (ushort*)ptr;
290+
291+
var i = (nint)0;
292+
var n = (nint)(uint)buffer.Length;
303293

304294
if (Sse41.IsSupported && n >= Vector128<ushort>.Count)
305295
{
306-
var vecOldChar = Vector128.Create((ushort)oldChar);
307-
var vecNewChar = Vector128.Create((ushort)newChar);
296+
var vecPlus = Vector128.Create((ushort)'+');
297+
var vecSpace = Vector128.Create((ushort)' ');
308298

309299
do
310300
{
311-
var vec = Sse2.LoadVector128(pVec + i);
312-
var mask = Sse2.CompareEqual(vec, vecOldChar);
313-
var res = Sse41.BlendVariable(vec, vecNewChar, mask);
314-
Sse2.Store(pVec + i, res);
315-
301+
var vec = Sse2.LoadVector128(input + i);
302+
var mask = Sse2.CompareEqual(vec, vecPlus);
303+
var res = Sse41.BlendVariable(vec, vecSpace, mask);
304+
Sse2.Store(output + i, res);
316305
i += Vector128<ushort>.Count;
317306
} while (i <= n - Vector128<ushort>.Count);
318307
}
319308

320309
for (; i < n; ++i)
321310
{
322-
if (ptr[i] == oldChar)
311+
if (input[i] != '+')
312+
{
313+
output[i] = input[i];
314+
}
315+
else
323316
{
324-
ptr[i] = newChar;
317+
output[i] = ' ';
325318
}
326319
}
327320
}

0 commit comments

Comments
 (0)