Skip to content

Commit 357eca4

Browse files
committed
Refactor UriHelper.cs to use a static lambda and UriComponents struct
This commit includes a significant refactor of the `UriHelper.cs` file. Unused namespaces `System.Buffers` and `System.Runtime.CompilerServices` have been removed. The `InitializeAbsoluteUriStringSpanAction` delegate and `InitializeAbsoluteUriString` method, as well as the `CopyTextToBuffer` method, have been removed. A new struct `UriComponents` has been introduced to encapsulate the components of a URI: `Scheme`, `Host`, `PathBase`, `Path`, `Query`, and `Fragment`. The `string.Create` method call has been updated to use a new lambda function that leverages the `UriComponents` struct to copy each component of the URI into the buffer, adjusting the buffer slice as needed. This function also handles the case where both `PathBase` and `Path` components have a slash, removing the trailing slash from `PathBase` to avoid duplication. Finally, the `BuildAbsolute` method has been updated to use the new `UriComponents` struct and the updated `string.Create` call.
1 parent 197c491 commit 357eca4

File tree

1 file changed

+67
-40
lines changed

1 file changed

+67
-40
lines changed

src/Http/Http.Extensions/src/UriHelper.cs

Lines changed: 67 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Buffers;
54
using System.Diagnostics.CodeAnalysis;
6-
using System.Runtime.CompilerServices;
75
using System.Text;
86

97
namespace Microsoft.AspNetCore.Http.Extensions;
@@ -17,7 +15,6 @@ public static class UriHelper
1715
private const char Hash = '#';
1816
private const char QuestionMark = '?';
1917
private static readonly string SchemeDelimiter = Uri.SchemeDelimiter;
20-
private static readonly SpanAction<char, (string scheme, string host, string pathBase, string path, string query, string fragment)> InitializeAbsoluteUriStringSpanAction = new(InitializeAbsoluteUriString);
2118

2219
/// <summary>
2320
/// Combines the given URI components into a string that is properly encoded for use in HTTP headers.
@@ -90,7 +87,59 @@ public static string BuildAbsolute(
9087
length--;
9188
}
9289

93-
return string.Create(length, (scheme, hostText, pathBaseText, pathText, queryText, fragmentText), InitializeAbsoluteUriStringSpanAction);
90+
return string.Create(
91+
length,
92+
new UriComponents(scheme, hostText, pathBaseText, pathText, queryText, fragmentText),
93+
static (buffer, uriComponents) =>
94+
{
95+
if (uriComponents.Scheme.Length > 0)
96+
{
97+
uriComponents.Scheme.AsSpan().CopyTo(buffer);
98+
buffer = buffer.Slice(uriComponents.Scheme.Length);
99+
}
100+
101+
Uri.SchemeDelimiter.CopyTo(buffer);
102+
buffer = buffer.Slice(Uri.SchemeDelimiter.Length);
103+
104+
if (uriComponents.Host.Length > 0)
105+
{
106+
uriComponents.Host.AsSpan().CopyTo(buffer);
107+
buffer = buffer.Slice(uriComponents.Host.Length);
108+
}
109+
110+
var pathBaseSpan = uriComponents.PathBase.AsSpan();
111+
112+
if (uriComponents.Path.Length > 0 && pathBaseSpan.Length > 0 && pathBaseSpan[^1] == '/')
113+
{
114+
// If the path string has a trailing slash and the other string has a leading slash, we need
115+
// to trim one of them.
116+
// Trim the last slash from pathBase. The total length was decremented before the call to string.Create.
117+
pathBaseSpan = pathBaseSpan[..^1];
118+
}
119+
120+
if (pathBaseSpan.Length > 0)
121+
{
122+
pathBaseSpan.CopyTo(buffer);
123+
buffer = buffer.Slice(pathBaseSpan.Length);
124+
}
125+
126+
if (uriComponents.Path.Length > 0)
127+
{
128+
uriComponents.Path.CopyTo(buffer);
129+
buffer = buffer.Slice(uriComponents.Path.Length);
130+
}
131+
132+
if (uriComponents.Query.Length > 0)
133+
{
134+
uriComponents.Query.CopyTo(buffer);
135+
buffer = buffer.Slice(uriComponents.Query.Length);
136+
}
137+
138+
if (uriComponents.Fragment.Length > 0)
139+
{
140+
uriComponents.Fragment.CopyTo(buffer);
141+
}
142+
});
94143
}
95144

96145
/// <summary>
@@ -224,45 +273,23 @@ public static string GetDisplayUrl(this HttpRequest request)
224273
.ToString();
225274
}
226275

227-
/// <summary>
228-
/// Copies the specified <paramref name="text"/> to the specified <paramref name="buffer"/> starting at the specified <paramref name="index"/>.
229-
/// </summary>
230-
/// <param name="buffer">The buffer to copy text to.</param>
231-
/// <param name="index">The buffer start index.</param>
232-
/// <param name="text">The text to copy.</param>
233-
/// <returns></returns>
234-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
235-
private static int CopyTextToBuffer(Span<char> buffer, int index, ReadOnlySpan<char> text)
236-
{
237-
text.CopyTo(buffer.Slice(index, text.Length));
238-
return index + text.Length;
239-
}
240-
241-
/// <summary>
242-
/// Initializes the URI <see cref="string"/> for <see cref="BuildAbsolute(string, HostString, PathString, PathString, QueryString, FragmentString)"/>.
243-
/// </summary>
244-
/// <param name="buffer">The URI <see cref="string"/>'s <see cref="char"/> buffer.</param>
245-
/// <param name="uriParts">The URI parts.</param>
246-
private static void InitializeAbsoluteUriString(Span<char> buffer, (string scheme, string host, string pathBase, string path, string query, string fragment) uriParts)
276+
private readonly struct UriComponents
247277
{
248-
var index = 0;
249-
250-
var pathBaseSpan = uriParts.pathBase.AsSpan();
278+
public readonly string Scheme;
279+
public readonly string Host;
280+
public readonly string PathBase;
281+
public readonly string Path;
282+
public readonly string Query;
283+
public readonly string Fragment;
251284

252-
if (uriParts.path.Length > 0 && pathBaseSpan.Length > 0 && pathBaseSpan[^1] == '/')
285+
public UriComponents(string scheme, string host, string pathBase, string path, string query, string fragment)
253286
{
254-
// If the path string has a trailing slash and the other string has a leading slash, we need
255-
// to trim one of them.
256-
// Trim the last slahs from pathBase. The total length was decremented before the call to string.Create.
257-
pathBaseSpan = pathBaseSpan[..^1];
287+
Scheme = scheme;
288+
Host = host;
289+
PathBase = pathBase;
290+
Path = path;
291+
Query = query;
292+
Fragment = fragment;
258293
}
259-
260-
index = CopyTextToBuffer(buffer, index, uriParts.scheme.AsSpan());
261-
index = CopyTextToBuffer(buffer, index, Uri.SchemeDelimiter.AsSpan());
262-
index = CopyTextToBuffer(buffer, index, uriParts.host.AsSpan());
263-
index = CopyTextToBuffer(buffer, index, pathBaseSpan);
264-
index = CopyTextToBuffer(buffer, index, uriParts.path.AsSpan());
265-
index = CopyTextToBuffer(buffer, index, uriParts.query.AsSpan());
266-
_ = CopyTextToBuffer(buffer, index, uriParts.fragment.AsSpan());
267294
}
268295
}

0 commit comments

Comments
 (0)