Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: Implement IUtf8SpanParsable on IPAddress & IPNetwork #103111

Open
edwardneal opened this issue Jun 6, 2024 · 5 comments · May be fixed by #102144
Open

[API Proposal]: Implement IUtf8SpanParsable on IPAddress & IPNetwork #103111

edwardneal opened this issue Jun 6, 2024 · 5 comments · May be fixed by #102144
Labels
api-approved API was approved in API review, it can be implemented area-System.Net
Milestone

Comments

@edwardneal
Copy link
Contributor

edwardneal commented Jun 6, 2024

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.

@edwardneal edwardneal added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jun 6, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jun 6, 2024
Copy link
Contributor

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

@antonfirsov antonfirsov added this to the Future milestone Jun 6, 2024
@antonfirsov antonfirsov added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed untriaged New issue has not been triaged by the area owner labels Jun 6, 2024
@antonfirsov
Copy link
Member

antonfirsov commented 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.

@antonfirsov antonfirsov modified the milestones: Future, 9.0.0 Jun 6, 2024
@teo-tsirpanis teo-tsirpanis removed the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jun 6, 2024
@bartonjs
Copy link
Member

bartonjs commented Jun 13, 2024

Video

  • IPAddress doesn't respect the IFormatProvider, so should use the explicit implementation pattern.
  • Otherwise, looks good as proposed.
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);
}

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jun 13, 2024
@karelz
Copy link
Member

karelz commented Jun 25, 2024

@MihaZupan is it fully covered in PR #102144?

@MihaZupan
Copy link
Member

Yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-System.Net
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants