Skip to content

Commit 2c7a6f8

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 2c7a6f8

File tree

1 file changed

+49
-40
lines changed

1 file changed

+49
-40
lines changed

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

Lines changed: 49 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,41 @@ 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+
uriComponents.Scheme.AsSpan().CopyTo(buffer);
96+
buffer = buffer.Slice(uriComponents.Scheme.Length);
97+
98+
Uri.SchemeDelimiter.CopyTo(buffer);
99+
buffer = buffer.Slice(Uri.SchemeDelimiter.Length);
100+
101+
uriComponents.Host.AsSpan().CopyTo(buffer);
102+
buffer = buffer.Slice(uriComponents.Host.Length);
103+
104+
var pathBaseSpan = uriComponents.PathBase.AsSpan();
105+
106+
if (uriComponents.Path.Length > 0 && pathBaseSpan.Length > 0 && pathBaseSpan[^1] == '/')
107+
{
108+
// If the path string has a trailing slash and the other string has a leading slash, we need
109+
// to trim one of them.
110+
// Trim the last slash from pathBase. The total length was decremented before the call to string.Create.
111+
pathBaseSpan = pathBaseSpan[..^1];
112+
}
113+
114+
pathBaseSpan.CopyTo(buffer);
115+
buffer = buffer.Slice(pathBaseSpan.Length);
116+
117+
uriComponents.Path.CopyTo(buffer);
118+
buffer = buffer.Slice(uriComponents.Path.Length);
119+
120+
uriComponents.Query.CopyTo(buffer);
121+
buffer = buffer.Slice(uriComponents.Query.Length);
122+
123+
uriComponents.Fragment.CopyTo(buffer);
124+
});
94125
}
95126

96127
/// <summary>
@@ -224,45 +255,23 @@ public static string GetDisplayUrl(this HttpRequest request)
224255
.ToString();
225256
}
226257

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)
258+
private readonly struct UriComponents
247259
{
248-
var index = 0;
249-
250-
var pathBaseSpan = uriParts.pathBase.AsSpan();
260+
public readonly string Scheme;
261+
public readonly string Host;
262+
public readonly string PathBase;
263+
public readonly string Path;
264+
public readonly string Query;
265+
public readonly string Fragment;
251266

252-
if (uriParts.path.Length > 0 && pathBaseSpan.Length > 0 && pathBaseSpan[^1] == '/')
267+
public UriComponents(string scheme, string host, string pathBase, string path, string query, string fragment)
253268
{
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];
269+
Scheme = scheme;
270+
Host = host;
271+
PathBase = pathBase;
272+
Path = path;
273+
Query = query;
274+
Fragment = fragment;
258275
}
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());
267276
}
268277
}

0 commit comments

Comments
 (0)