Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/libraries/System.Net.Primitives/ref/System.Net.Primitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public IPAddress(System.ReadOnlySpan<byte> address, long scopeid) { }
static bool System.IUtf8SpanParsable<System.Net.IPAddress>.TryParse(System.ReadOnlySpan<byte> utf8Text, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Net.IPAddress? result) { throw null; }
public bool TryWriteBytes(System.Span<byte> destination, out int bytesWritten) { throw null; }
}
public partial class IPEndPoint : System.Net.EndPoint
public partial class IPEndPoint : System.Net.EndPoint, ISpanFormattable, ISpanParsable<IPEndPoint>, IUtf8SpanFormattable, IUtf8SpanParsable<IPEndPoint>
{
public const int MaxPort = 65535;
public const int MinPort = 0;
Expand All @@ -301,6 +301,19 @@ public IPEndPoint(System.Net.IPAddress address, int port) { }
public override string ToString() { throw null; }
public static bool TryParse(System.ReadOnlySpan<char> s, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Net.IPEndPoint? result) { throw null; }
public static bool TryParse(string s, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Net.IPEndPoint? result) { throw null; }
public static IPEndPoint Parse(ReadOnlySpan<byte> utf8Text) { throw null; }
static IPEndPoint ISpanParsable<IPEndPoint>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider) { throw null; }
static IPEndPoint IParsable<IPEndPoint>.Parse(string s, IFormatProvider? provider) { throw null; }
static IPEndPoint IUtf8SpanParsable<IPEndPoint>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) { throw null; }
public static bool TryParse(ReadOnlySpan<byte> utf8Text, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out IPEndPoint? result) { throw null; }
static bool ISpanParsable<IPEndPoint>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out IPEndPoint? result) { throw null; }
static bool IParsable<IPEndPoint>.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out IPEndPoint? result) { throw null; }
static bool IUtf8SpanParsable<IPEndPoint>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out IPEndPoint? result) { throw null; }
string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { throw null; }
public bool TryFormat(Span<char> destination, out int charsWritten) { throw null; }
public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten) { throw null; }
bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) { throw null; }
bool IUtf8SpanFormattable.TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider) { throw null; }
}
public readonly partial struct IPNetwork : System.IEquatable<System.Net.IPNetwork>, System.IFormattable, System.IParsable<System.Net.IPNetwork>, System.ISpanFormattable, System.ISpanParsable<System.Net.IPNetwork>, System.IUtf8SpanFormattable, System.IUtf8SpanParsable<System.Net.IPNetwork>
{
Expand Down
101 changes: 90 additions & 11 deletions src/libraries/System.Net.Primitives/src/System/Net/IPEndPoint.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Sockets;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text.Unicode;

namespace System.Net
{
/// <summary>
/// Provides an IP address.
/// </summary>
public class IPEndPoint : EndPoint
public class IPEndPoint : EndPoint, ISpanFormattable, ISpanParsable<IPEndPoint>, IUtf8SpanFormattable, IUtf8SpanParsable<IPEndPoint>
{
/// <summary>
/// Specifies the minimum acceptable value for the <see cref='System.Net.IPEndPoint.Port'/> property.
Expand Down Expand Up @@ -92,41 +96,65 @@ public static bool TryParse(string s, [NotNullWhen(true)] out IPEndPoint? result
return TryParse(s.AsSpan(), out result);
}

public static bool TryParse(ReadOnlySpan<char> s, [NotNullWhen(true)] out IPEndPoint? result)
internal static bool InternalTryParse<TChar>(ReadOnlySpan<TChar> s, [NotNullWhen(true)] out IPEndPoint? result)
where TChar : unmanaged, IBinaryInteger<TChar>
{
Debug.Assert(typeof(TChar) == typeof(byte) || typeof(TChar) == typeof(char));

int addressLength = s.Length; // If there's no port then send the entire string to the address parser
int lastColonPos = s.LastIndexOf(':');
int lastColonPos = s.LastIndexOf(TChar.CreateTruncating(':'));

// Look to see if this is an IPv6 address with a port.
if (lastColonPos > 0)
{
if (s[lastColonPos - 1] == ']')
if (s[lastColonPos - 1] == TChar.CreateTruncating(']'))
{
addressLength = lastColonPos;
}
// Look to see if this is IPv4 with a port (IPv6 will have another colon)
else if (s.Slice(0, lastColonPos).LastIndexOf(':') == -1)
else if (s.Slice(0, lastColonPos).LastIndexOf(TChar.CreateTruncating(':')) == -1)
{
addressLength = lastColonPos;
}
}

if (IPAddress.TryParse(s.Slice(0, addressLength), out IPAddress? address))
IPAddress? address = IPAddressParser.Parse(s.Slice(0, addressLength), true);
if (address is not null)
{
uint port = 0;
if (addressLength == s.Length ||
(uint.TryParse(s.Slice(addressLength + 1), NumberStyles.None, CultureInfo.InvariantCulture, out port) && port <= MaxPort))

if (addressLength == s.Length)
{
result = new IPEndPoint(address, (int)port);
result = new IPEndPoint(address, 0);
return true;
}
else
{
uint port;
ReadOnlySpan<TChar> portSpan = s.Slice(addressLength + 1);
bool isConvertedToInt;

if (typeof(TChar) == typeof(byte))
{
isConvertedToInt = uint.TryParse(MemoryMarshal.Cast<TChar, byte>(portSpan), NumberStyles.None, CultureInfo.InvariantCulture, out port);
}
else
{
isConvertedToInt = uint.TryParse(MemoryMarshal.Cast<TChar, char>(portSpan), NumberStyles.None, CultureInfo.InvariantCulture, out port);
}

if (isConvertedToInt && port <= MaxPort)
{
result = new IPEndPoint(address, (int)port);
return true;
}
}
}

result = null;
return false;
}

public static bool TryParse(ReadOnlySpan<char> s, [NotNullWhen(true)] out IPEndPoint? result) => InternalTryParse(s, out result);
Copy link
Member

@MihaZupan MihaZupan Aug 25, 2025

Choose a reason for hiding this comment

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

Please also include /// comments on all the new APIs (they seed the api docs)


public static IPEndPoint Parse(string s)
{
ArgumentNullException.ThrowIfNull(s);
Expand Down Expand Up @@ -178,5 +206,56 @@ public override int GetHashCode()
{
return _address.GetHashCode() ^ _port;
}

public static IPEndPoint Parse(ReadOnlySpan<byte> utf8Text)
{
if (TryParse(utf8Text, out IPEndPoint? result))
{
return result;
}

throw new FormatException(SR.bad_endpoint_string);
}

static IPEndPoint ISpanParsable<IPEndPoint>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s);

static IPEndPoint IParsable<IPEndPoint>.Parse(string s, IFormatProvider? provider) => Parse(s);

static IPEndPoint IUtf8SpanParsable<IPEndPoint>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text);

public static bool TryParse(ReadOnlySpan<byte> utf8Text, [NotNullWhen(true)] out IPEndPoint? result) => InternalTryParse(utf8Text, out result);

static bool ISpanParsable<IPEndPoint>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [NotNullWhen(true)] out IPEndPoint? result) => TryParse(s, out result);

static bool IParsable<IPEndPoint>.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [NotNullWhen(true)] out IPEndPoint? result)
{
if (s is null)
{
result = default;
return false;
}

return TryParse(s, out result);
}

static bool IUtf8SpanParsable<IPEndPoint>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, [NotNullWhen(true)] out IPEndPoint? result) => TryParse(utf8Text, out result);

string IFormattable.ToString(string? format, IFormatProvider? formatProvider) => ToString();

public bool TryFormat(Span<char> destination, out int charsWritten) =>
_address.AddressFamily == AddressFamily.InterNetworkV6 ?
destination.TryWrite(CultureInfo.InvariantCulture, $"[{_address}]:{_port}", out charsWritten) :
destination.TryWrite(CultureInfo.InvariantCulture, $"{_address}:{_port}", out charsWritten);

public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten) =>
_address.AddressFamily == AddressFamily.InterNetworkV6 ?
Utf8.TryWrite(utf8Destination, CultureInfo.InvariantCulture, $"[{_address}]:{_port}", out bytesWritten) :
Utf8.TryWrite(utf8Destination, CultureInfo.InvariantCulture, $"{_address}:{_port}", out bytesWritten);

bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) =>
TryFormat(destination, out charsWritten);

bool IUtf8SpanFormattable.TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider) =>
TryFormat(utf8Destination, out bytesWritten);
}
}
Loading
Loading