Repository navigation
[API Proposal]: Implement IUtf8SpanParsable on IPAddress & IPNetwork #103111
Description
Background and motivation
This continues the work in #81500, expanding the existing UTF8 support to IPAddress and IPNetwork. I'm trying to tackle the low-hanging from from the list of expected types.
cc @tannergooding and linking this to the earlier PR #102144 - I can see the issue's gone to System.Net rather than System.Runtime.
API Proposal
The original issue expected IUtf8SpanParsable to be implemented implicitly, and I've done this on IPAddress. I've implemented it explicitly on IPNetwork though, adding an additional pair of Parse and TryParse overloads. This is because IPNetwork also implements ISpanParsable<IPAddress> explicitly, and I wanted to keep the API surface consistent between the two interface implementations. The original justification for it to implement ISpanParsable<IPAddress> explicitly was that it doesn't support custom formatting (API review results here), and I believe the same rationale applies here.
namespace System.Net;
public partial class IPAddress
: ISpanFormattable, ISpanParsable<IPAddress>, IUtf8SpanFormattable
+ , IUtf8SpanParsable<IPAddress>
{
+ public static IPAddress Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider);
+ public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider,
+ [NotNullWhen(true)] out IPAddress? result);
}
public readonly partial struct IPNetwork
: ISpanFormattable, ISpanParsable<IPAddress>, IUtf8SpanFormattable
+ , IUtf8SpanParsable<IPNetwork>
{
static IPNetwork ISpanParsable<IPNetwork>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider);
public static IPNetwork Parse(ReadOnlySpan<char> s);
+ static IPNetwork IUtf8SpanParsable<IPNetwork>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider);
+ public static IPNetwork Parse(ReadOnlySpan<byte> utf8Text);
static bool ISpanParsable<IPNetwork>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out IPNetwork result);
public static bool TryParse(ReadOnlySpan<char> s, out IPNetwork result);
+ static bool IUtf8SpanParsable<IPNetwork>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider,
+ out IPNetwork result);
+ public static bool TryParse(ReadOnlySpan<byte> utf8Text,
+ [MaybeNullWhen(false)] out IPNetwork? result);
}API Usage
ReadOnlySpan<byte> utf8Localhost = "127.0.0.1"u8;
ReadOnlySpan<byte> utf8LocalhostNetwork = "127.0.0.0/8"u8;
IPAddress parsedLocalhost = IPAddress.Parse(utf8Localhost, null);
IPNetwork parsedLocalhostNetwork = IPNetwork.Parse(utf8LocalhostNetwork);
bool successfullyParsed;
successfullyParsed = IPAddress.TryParse(utf8Localhost, null, out parsedLocalhost);
successfullyParsed = IPNetwork.TryParse(utf8LocalhostNetwork, null, out parsedLocalhostNetwork);Alternative Designs
No response
Risks
At present, all parsing of IPAddresses from ReadOnlySpan<char> is handled by IPAddressParser, IPv4AddressHelper and IPv6AddressHelper. This works completely with chars and performs minimal allocations. I'd prefer to make these classes generic, but their codebases are also used privately in Uri parsing and by System.Net.Quic. The work required would also replace some of the of references to char* with ReadOnlySpan<char> in Uri (and might slightly improve GC performance by eliminating a few cases where we pin strings), but the changes cut across more projects.
I could alternatively implement the interface by just calling Encoding.UTF8.GetChars on the UTF8 input and passing through to ISpanParsable<IPAddress>.Parse, leaving the underlying IP address parsing as char-only. The longest possible IP address would fit in a 64-character stackalloc'd buffer, so I don't think this approach would necessarily need to allocate - it's just a tradeoff between the wider impact of changing IPAddressParser and any efficiency loss from a separate transcoding stage.
Activity
dotnet-policy-service commentedon Jun 6, 2024
Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.
antonfirsov commentedon Jun 6, 2024
Looks good as proposed, so marked
api-ready-for-review.Triaging to Future since it's not critical for 9.0.Actually, since there is a PR open let's mark it in 9.0 in order to get the API reviewed faster.@dotnet/ncl please scream if you disagree with either of these decisions.
bartonjs commentedon Jun 13, 2024
Video
namespace System.Net; public partial class IPAddress : ISpanFormattable, ISpanParsable<IPAddress>, IUtf8SpanFormattable + , IUtf8SpanParsable<IPAddress> { + static IPAddress IUtf8SpanParsable<IPAddress>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider); + public static IPAddress Parse(ReadOnlySpan<byte> utf8Text); + static bool IUtf8SpanParsable<IPAddress>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, + [NotNullWhen(true)] out IPAddress? result); + public static bool TryParse(ReadOnlySpan<byte> utf8Text, [NotNullWhen(true)] out IPAddress? result); } public readonly partial struct IPNetwork : ISpanFormattable, ISpanParsable<IPAddress>, IUtf8SpanFormattable + , IUtf8SpanParsable<IPNetwork> { static IPNetwork ISpanParsable<IPNetwork>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider); public static IPNetwork Parse(ReadOnlySpan<char> s); + static IPNetwork IUtf8SpanParsable<IPNetwork>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider); + public static IPNetwork Parse(ReadOnlySpan<byte> utf8Text); static bool ISpanParsable<IPNetwork>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out IPNetwork result); public static bool TryParse(ReadOnlySpan<char> s, out IPNetwork result); + static bool IUtf8SpanParsable<IPNetwork>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, + out IPNetwork result); + public static bool TryParse(ReadOnlySpan<byte> utf8Text, + [MaybeNullWhen(false)] out IPNetwork? result); }karelz commentedon Jun 25, 2024
@MihaZupan is it fully covered in PR #102144?
MihaZupan commentedon Jul 2, 2024
Yes
karelz commentedon Jul 23, 2024
Looks like this feature missed the Platform feature complete deadline yesterday. Moving it to .NET 10.