Skip to content

Comments

Use IndexOfAny/SearchValues in more places around Uri#124433

Merged
MihaZupan merged 4 commits intodotnet:mainfrom
MihaZupan:uri-userInfoSearch
Feb 17, 2026
Merged

Use IndexOfAny/SearchValues in more places around Uri#124433
MihaZupan merged 4 commits intodotnet:mainfrom
MihaZupan:uri-userInfoSearch

Conversation

@MihaZupan
Copy link
Member

@MihaZupan MihaZupan commented Feb 14, 2026

EgorBot/runtime-utils#618 (comment)

Method Toolchain Mean Error Ratio
NoUserInfo_ShortHost Main 53.25 ns 0.091 ns 1.00
NoUserInfo_ShortHost PR 53.30 ns 0.188 ns 1.00
NoUserInfo_LongHost Main 166.87 ns 0.223 ns 1.00
NoUserInfo_LongHost PR 120.22 ns 0.394 ns 0.72

https://github.com/dotnet/runtime/pull/124433/changes?w=1

@MihaZupan MihaZupan added this to the 11.0.0 milestone Feb 14, 2026
@MihaZupan MihaZupan self-assigned this Feb 14, 2026
Copilot AI review requested due to automatic review settings February 14, 2026 23:41
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@MihaZupan
Copy link
Member Author

@EgorBot

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);

public class Bench
{
    private string _noUserInfoShortHost = default!;
    private string _noUserInfoLongHost = default!;
    private string _withUserInfoShortHost = default!;
    private string _withUserInfoLongHost = default!;
    private string _basicHostShortPath = default!;
    private string _basicHostLongPath = default!;
    private Uri _baseUri = default!;
    private string _relativeWithScheme = default!;
    private string _relativeNoScheme = default!;

    [GlobalSetup]
    public void Setup()
    {
        _noUserInfoShortHost = "http://host/path";
        _noUserInfoLongHost = "http://" + new string('a', 64) + ".example.com/path";
        _withUserInfoShortHost = "http://user:pass@host/path";
        _withUserInfoLongHost = "http://user:pass@" + new string('a', 64) + ".example.com/path";
        _basicHostShortPath = "myscheme://host/path";
        _basicHostLongPath = "myscheme://" + new string('a', 64) + ".example.com/path?query#fragment";
        _baseUri = new Uri("http://base.example.com/basepath/");
        _relativeWithScheme = "http://other.example.com/otherpath";
        _relativeNoScheme = "relative/path?query#fragment";

        UriParser.Register(new GenericUriParser(GenericUriParserOptions.Default), "myscheme", -1);
    }

    [Benchmark]
    public Uri NoUserInfo_ShortHost() => new Uri(_noUserInfoShortHost);

    [Benchmark]
    public Uri NoUserInfo_LongHost() => new Uri(_noUserInfoLongHost);

    [Benchmark]
    public Uri WithUserInfo_ShortHost() => new Uri(_withUserInfoShortHost);

    [Benchmark]
    public Uri WithUserInfo_LongHost() => new Uri(_withUserInfoLongHost);

    [Benchmark]
    public Uri BasicHost_ShortPath() => new Uri(_basicHostShortPath);

    [Benchmark]
    public Uri BasicHost_LongPath() => new Uri(_basicHostLongPath);

    [Benchmark]
    public Uri CombinedString_WithScheme() => new Uri(_baseUri, _relativeWithScheme);

    [Benchmark]
    public Uri CombinedString_NoScheme() => new Uri(_baseUri, _relativeNoScheme);
}

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request optimizes URI parsing by replacing character-by-character loops with SearchValues.IndexOfAny for improved performance when searching for delimiters. The changes focus on three key areas of URI parsing: scheme detection in relative URIs, user info parsing in authority components, and basic hostname end detection.

Changes:

  • Replaces manual loop with IndexOfAny for finding scheme delimiters in relative URI resolution
  • Introduces SearchValues for user info end characters and refactors user info parsing logic
  • Simplifies basic hostname end detection using IndexOfAny instead of manual iteration

@MihaZupan MihaZupan marked this pull request as ready for review February 15, 2026 02:02
Copilot AI review requested due to automatic review settings February 15, 2026 02:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@MihaZupan MihaZupan enabled auto-merge (squash) February 16, 2026 15:14
@MihaZupan MihaZupan merged commit 868a890 into dotnet:main Feb 17, 2026
97 checks passed
break;
var vsb = new ValueStringBuilder(stackalloc char[StackallocThreshold]);
IriHelper.EscapeUnescapeIri(ref vsb, slice.Slice(0, userInfoLength), isQuery: false);
newHost = string.Concat(newHost, vsb.AsSpan());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could put newHost in vsb first.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we'd just be copying the original newHost around an extra time in that case. We do this sort of thing in more places to avoid copying the prefix again.

In the long term my hope is that this substring allocation will get removed anyway and we'd only allocate the new _string once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants