Skip to content

Commit

Permalink
Refactor UriHelper.cs to use a static lambda and UriComponents struct
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
paulomorgado committed Nov 15, 2024
1 parent eb68e01 commit 65b6834
Showing 1 changed file with 49 additions and 40 deletions.
89 changes: 49 additions & 40 deletions src/Http/Http.Extensions/src/UriHelper.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;

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

/// <summary>
/// Combines the given URI components into a string that is properly encoded for use in HTTP headers.
Expand Down Expand Up @@ -90,7 +87,41 @@ public static string BuildAbsolute(
length--;
}

return string.Create(length, (scheme, hostText, pathBaseText, pathText, queryText, fragmentText), InitializeAbsoluteUriStringSpanAction);
return string.Create(
length,
new UriComponents(scheme, hostText, pathBaseText, pathText, queryText, fragmentText),
static (buffer, uriComponents) =>
{
uriComponents.Scheme.AsSpan().CopyTo(buffer);
buffer = buffer.Slice(uriComponents.Scheme.Length);

Uri.SchemeDelimiter.CopyTo(buffer);
buffer = buffer.Slice(Uri.SchemeDelimiter.Length);

uriComponents.Host.AsSpan().CopyTo(buffer);
buffer = buffer.Slice(uriComponents.Host.Length);

var pathBaseSpan = uriComponents.PathBase.AsSpan();

if (uriComponents.Path.Length > 0 && pathBaseSpan.Length > 0 && pathBaseSpan[^1] == '/')
{
// If the path string has a trailing slash and the other string has a leading slash, we need
// to trim one of them.
// Trim the last slash from pathBase. The total length was decremented before the call to string.Create.
pathBaseSpan = pathBaseSpan[..^1];
}

pathBaseSpan.CopyTo(buffer);
buffer = buffer.Slice(pathBaseSpan.Length);

uriComponents.Path.CopyTo(buffer);
buffer = buffer.Slice(uriComponents.Path.Length);

uriComponents.Query.CopyTo(buffer);
buffer = buffer.Slice(uriComponents.Query.Length);

uriComponents.Fragment.CopyTo(buffer);
});
}

/// <summary>
Expand Down Expand Up @@ -224,45 +255,23 @@ public static string GetDisplayUrl(this HttpRequest request)
.ToString();
}

/// <summary>
/// Copies the specified <paramref name="text"/> to the specified <paramref name="buffer"/> starting at the specified <paramref name="index"/>.
/// </summary>
/// <param name="buffer">The buffer to copy text to.</param>
/// <param name="index">The buffer start index.</param>
/// <param name="text">The text to copy.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int CopyTextToBuffer(Span<char> buffer, int index, ReadOnlySpan<char> text)
{
text.CopyTo(buffer.Slice(index, text.Length));
return index + text.Length;
}

/// <summary>
/// Initializes the URI <see cref="string"/> for <see cref="BuildAbsolute(string, HostString, PathString, PathString, QueryString, FragmentString)"/>.
/// </summary>
/// <param name="buffer">The URI <see cref="string"/>'s <see cref="char"/> buffer.</param>
/// <param name="uriParts">The URI parts.</param>
private static void InitializeAbsoluteUriString(Span<char> buffer, (string scheme, string host, string pathBase, string path, string query, string fragment) uriParts)
private readonly struct UriComponents
{
var index = 0;

var pathBaseSpan = uriParts.pathBase.AsSpan();
public readonly string Scheme;
public readonly string Host;
public readonly string PathBase;
public readonly string Path;
public readonly string Query;
public readonly string Fragment;

if (uriParts.path.Length > 0 && pathBaseSpan.Length > 0 && pathBaseSpan[^1] == '/')
public UriComponents(string scheme, string host, string pathBase, string path, string query, string fragment)
{
// If the path string has a trailing slash and the other string has a leading slash, we need
// to trim one of them.
// Trim the last slahs from pathBase. The total length was decremented before the call to string.Create.
pathBaseSpan = pathBaseSpan[..^1];
Scheme = scheme;
Host = host;
PathBase = pathBase;
Path = path;
Query = query;
Fragment = fragment;
}

index = CopyTextToBuffer(buffer, index, uriParts.scheme.AsSpan());
index = CopyTextToBuffer(buffer, index, Uri.SchemeDelimiter.AsSpan());
index = CopyTextToBuffer(buffer, index, uriParts.host.AsSpan());
index = CopyTextToBuffer(buffer, index, pathBaseSpan);
index = CopyTextToBuffer(buffer, index, uriParts.path.AsSpan());
index = CopyTextToBuffer(buffer, index, uriParts.query.AsSpan());
_ = CopyTextToBuffer(buffer, index, uriParts.fragment.AsSpan());
}
}

0 comments on commit 65b6834

Please sign in to comment.