Skip to content
Sanam edited this page Sep 23, 2026 · 1 revision

public ref partial struct AdaUrl : IDisposable

A parsed URL. It has two parts: static methods that parse, answer and free inside one call, and an instance for reading several properties or changing the URL.

Being a ref struct, an instance cannot be stored in a field, captured by a lambda, or used across await. Use AdaUrlHandle for that.

Static methods

None of these need disposing, and none allocate with UTF-8 input.

Method Returns
CanParse(ReadOnlySpan<byte> utf8) true if the input is a valid URL
CanParse(ReadOnlySpan<byte> utf8, ReadOnlySpan<byte> baseUrl) true if the input resolves against the base
CanParse(ReadOnlySpan<char> input) Same, from UTF-16
TryNormalize(ReadOnlySpan<byte> utf8, Span<byte> destination, out int written) The normalised URL
TryGetHostname(ReadOnlySpan<byte> utf8, Span<byte> destination, out int written) The hostname, in ASCII
TryGetOrigin(ReadOnlySpan<byte> utf8, Span<byte> destination, out int written) The origin

The Try methods return false when the input is invalid or the buffer is too small.

AdaUrl.CanParse("https://example.com/"u8);                  // true
AdaUrl.CanParse("not a url"u8);                             // false
AdaUrl.CanParse("/docs"u8, "https://example.com"u8);        // true

Span<byte> buffer = stackalloc byte[256];

AdaUrl.TryNormalize("HTTPS://Example.COM/a/../b"u8, buffer, out int written);
// https://example.com/b

AdaUrl.TryGetHostname("https://user@Bücher.example:8080/x"u8, buffer, out written);
// xn--bcher-kva.example

AdaUrl.TryGetOrigin("https://example.com:8443/a?b"u8, buffer, out written);
// https://example.com:8443

TryGetHostname is the one to use for an allow list. It returns the hostname after IDNA, so compare it against ASCII entries.

Parsing

Method On invalid input
TryParse(ReadOnlySpan<byte> utf8, out AdaUrl url) Returns false
TryParse(ReadOnlySpan<byte> utf8, ReadOnlySpan<byte> baseUrl, out AdaUrl url) Returns false
TryParse(ReadOnlySpan<char> input, out AdaUrl url) Returns false, also for a lone surrogate
Parse(ReadOnlySpan<byte> utf8) Throws FormatException
Parse(ReadOnlySpan<char> input) Throws FormatException
using var url = AdaUrl.Parse("https://user:pass@example.com:8443/a/b?q=1#frag"u8);

if (AdaUrl.TryParse("../c?x=1"u8, "https://example.com/a/b/"u8, out AdaUrl resolved))
{
    using (resolved)
    {
        // resolved.Href is https://example.com/a/c?x=1
    }
}

Properties

Every text property returns a borrowed ReadOnlySpan<byte>. See the rules on Home. Values below are for https://user:pass@example.com:8443/a/b?q=1#frag.

Property Value Notes
Href https://user:pass@example.com:8443/a/b?q=1#frag The whole URL, serialised
Protocol https: Includes the colon
Username user
Password pass
Host example.com:8443 Includes the port
Hostname example.com Without the port
Port 8443 Empty when it is the scheme's default
Pathname /a/b
Search ?q=1 Includes ? when not empty
Hash #frag Includes # when not empty
HostType AdaHostType.Domain Domain, IPv4 or IPv6, see AdaUrlComponents
SchemeType AdaSchemeType.Https See AdaUrlComponents
Components AdaUrlComponents Byte offsets into Href
IsValid true false for a default or disposed instance

http://example.com/ has an empty Port and HasPort is false, because 80 is the default.

Flags

Property true when
HasCredentials A username or password is present
HasHostname A hostname is present
HasEmptyHostname A hostname is present and empty, as in file:///C:/tmp/a.txt
HasPort An explicit, non default port is present
HasPassword A password is present
HasNonEmptyUsername The username is not empty
HasNonEmptyPassword The password is not empty
HasSearch A query is present
HasHash A fragment is present

Origin

TryGetOrigin(Span<byte> destination, out int written) writes the origin, for example https://example.com:8443. Returns false when the buffer is too small.

Changing a URL

Setters follow the WHATWG setter rules. Any setter invalidates spans read earlier from the same instance.

Method Returns false when
TrySetHref(ReadOnlySpan<byte>) The value is not a valid URL. The URL is unchanged.
TrySetProtocol(ReadOnlySpan<byte>) The scheme is rejected
TrySetUsername(ReadOnlySpan<byte>) The URL cannot have credentials
TrySetPassword(ReadOnlySpan<byte>) The URL cannot have credentials
TrySetHost(ReadOnlySpan<byte>) The host is rejected. Sets the port too if the value has one.
TrySetHostname(ReadOnlySpan<byte>) The hostname is rejected. The port is kept.
TrySetPort(ReadOnlySpan<byte>) The port is not a number in range
TrySetPathname(ReadOnlySpan<byte>) The path is rejected
Method Effect
SetSearch(ReadOnlySpan<byte>) Replaces the query. A leading ? is optional.
SetHash(ReadOnlySpan<byte>) Replaces the fragment. A leading # is optional.
ClearSearch() Removes the query
ClearHash() Removes the fragment
ClearPort() Removes the port
using var url = AdaUrl.Parse("https://example.com/path?a=1#top"u8);

url.TrySetHostname("example.org"u8);   // https://example.org/path?a=1#top
url.TrySetHost("example.net:8080"u8);  // https://example.net:8080/path?a=1#top
url.TrySetPort("abc"u8);               // false, URL unchanged
url.TrySetProtocol("http"u8);          // http://example.net:8080/path?a=1#top
url.SetSearch("b=2"u8);                // http://example.net:8080/path?b=2#top
url.ClearPort();                       // http://example.net/path?b=2#top

Reusing one instance in a loop

TrySetHref re-parses into the instance you already have, which skips a native allocation per URL. It matters most on Windows. See performance.

using var url = AdaUrl.Parse(urls[0]);
foreach (byte[] next in urls)
{
    if (!url.TrySetHref(next)) continue;
    Consume(url.Hostname);
}

Dispose

Dispose() frees the native URL. Calling it twice is safe.

Clone this wiki locally