5
5
using System . Diagnostics ;
6
6
using System . Diagnostics . CodeAnalysis ;
7
7
using System . Net . Sockets ;
8
+ using System . Numerics ;
8
9
using System . Runtime . CompilerServices ;
9
10
using System . Runtime . InteropServices ;
10
11
using System . Runtime . Intrinsics ;
@@ -18,7 +19,7 @@ namespace System.Net
18
19
/// Provides an Internet Protocol (IP) address.
19
20
/// </para>
20
21
/// </devdoc>
21
- public class IPAddress : ISpanFormattable , ISpanParsable < IPAddress >
22
+ public class IPAddress : ISpanFormattable , ISpanParsable < IPAddress > , IUtf8SpanFormattable
22
23
{
23
24
public static readonly IPAddress Any = new ReadOnlyIPAddress ( new byte [ ] { 0 , 0 , 0 , 0 } ) ;
24
25
public static readonly IPAddress Loopback = new ReadOnlyIPAddress ( new byte [ ] { 127 , 0 , 0 , 1 } ) ;
@@ -375,7 +376,7 @@ public long ScopeId
375
376
// Not valid for IPv4 addresses
376
377
if ( IsIPv4 )
377
378
{
378
- throw new SocketException ( SocketError . OperationNotSupported ) ;
379
+ ThrowSocketOperationNotSupported ( ) ;
379
380
}
380
381
381
382
return PrivateScopeId ;
@@ -385,7 +386,7 @@ public long ScopeId
385
386
// Not valid for IPv4 addresses
386
387
if ( IsIPv4 )
387
388
{
388
- throw new SocketException ( SocketError . OperationNotSupported ) ;
389
+ ThrowSocketOperationNotSupported ( ) ;
389
390
}
390
391
391
392
// Consider: Since scope is only valid for link-local and site-local
@@ -403,27 +404,74 @@ public long ScopeId
403
404
/// or standard IPv6 representation.
404
405
/// </para>
405
406
/// </devdoc>
406
- public override string ToString ( ) =>
407
- _toString ??= IsIPv4 ?
408
- IPAddressParser . IPv4AddressToString ( PrivateAddress ) :
409
- IPAddressParser . IPv6AddressToString ( _numbers , PrivateScopeId ) ;
407
+ public override string ToString ( )
408
+ {
409
+ string ? toString = _toString ;
410
+ if ( toString is null )
411
+ {
412
+ Span < char > span = stackalloc char [ IPAddressParser . MaxIPv6StringLength ] ;
413
+ int length = IsIPv4 ?
414
+ IPAddressParser . FormatIPv4Address ( _addressOrScopeId , span ) :
415
+ IPAddressParser . FormatIPv6Address ( _numbers , _addressOrScopeId , span ) ;
416
+ _toString = toString = new string ( span . Slice ( 0 , length ) ) ;
417
+ }
418
+
419
+ return toString ;
420
+ }
410
421
411
422
/// <inheritdoc/>
412
423
string IFormattable . ToString ( string ? format , IFormatProvider ? formatProvider ) =>
413
424
// format and provider are explicitly ignored
414
425
ToString ( ) ;
415
426
416
- public bool TryFormat ( Span < char > destination , out int charsWritten )
417
- {
418
- return IsIPv4 ?
419
- IPAddressParser . IPv4AddressToString ( PrivateAddress , destination , out charsWritten ) :
420
- IPAddressParser . IPv6AddressToString ( _numbers , PrivateScopeId , destination , out charsWritten ) ;
421
- }
427
+ public bool TryFormat ( Span < char > destination , out int charsWritten ) =>
428
+ TryFormatCore ( destination , out charsWritten ) ;
422
429
423
430
/// <inheritdoc/>
424
431
bool ISpanFormattable . TryFormat ( Span < char > destination , out int charsWritten , ReadOnlySpan < char > format , IFormatProvider ? provider ) =>
425
432
// format and provider are explicitly ignored
426
- TryFormat ( destination , out charsWritten ) ;
433
+ TryFormatCore ( destination , out charsWritten ) ;
434
+
435
+ /// <inheritdoc/>
436
+ bool IUtf8SpanFormattable . TryFormat ( Span < byte > utf8Destination , out int bytesWritten , ReadOnlySpan < char > format , IFormatProvider ? provider ) =>
437
+ // format and provider are explicitly ignored
438
+ TryFormatCore ( utf8Destination , out bytesWritten ) ;
439
+
440
+ private bool TryFormatCore < TChar > ( Span < TChar > destination , out int charsWritten ) where TChar : unmanaged, IBinaryInteger < TChar >
441
+ {
442
+ if ( IsIPv4 )
443
+ {
444
+ if ( destination . Length >= IPAddressParser . MaxIPv4StringLength )
445
+ {
446
+ charsWritten = IPAddressParser . FormatIPv4Address ( _addressOrScopeId , destination ) ;
447
+ return true ;
448
+ }
449
+ }
450
+ else
451
+ {
452
+ if ( destination . Length >= IPAddressParser . MaxIPv6StringLength )
453
+ {
454
+ charsWritten = IPAddressParser . FormatIPv6Address ( _numbers , _addressOrScopeId , destination ) ;
455
+ return true ;
456
+ }
457
+ }
458
+
459
+ Span < TChar > tmpDestination = stackalloc TChar [ IPAddressParser . MaxIPv6StringLength ] ;
460
+ Debug . Assert ( tmpDestination . Length >= IPAddressParser . MaxIPv4StringLength ) ;
461
+
462
+ int written = IsIPv4 ?
463
+ IPAddressParser . FormatIPv4Address ( PrivateAddress , tmpDestination ) :
464
+ IPAddressParser . FormatIPv6Address ( _numbers , PrivateScopeId , tmpDestination ) ;
465
+
466
+ if ( tmpDestination . Slice ( 0 , written ) . TryCopyTo ( destination ) )
467
+ {
468
+ charsWritten = written ;
469
+ return true ;
470
+ }
471
+
472
+ charsWritten = 0 ;
473
+ return false ;
474
+ }
427
475
428
476
public static long HostToNetworkOrder ( long host )
429
477
{
@@ -551,37 +599,28 @@ public long Address
551
599
{
552
600
get
553
601
{
554
- //
555
- // IPv6 Changes: Can't do this for IPv6, so throw an exception.
556
- //
557
- //
558
602
if ( AddressFamily == AddressFamily . InterNetworkV6 )
559
603
{
560
- throw new SocketException ( SocketError . OperationNotSupported ) ;
561
- }
562
- else
563
- {
564
- return PrivateAddress ;
604
+ ThrowSocketOperationNotSupported ( ) ;
565
605
}
606
+
607
+ return PrivateAddress ;
566
608
}
567
609
set
568
610
{
569
- //
570
- // IPv6 Changes: Can't do this for IPv6 addresses
571
611
if ( AddressFamily == AddressFamily . InterNetworkV6 )
572
612
{
573
- throw new SocketException ( SocketError . OperationNotSupported ) ;
613
+ ThrowSocketOperationNotSupported ( ) ;
574
614
}
575
- else
615
+
616
+ if ( PrivateAddress != value )
576
617
{
577
- if ( PrivateAddress != value )
618
+ if ( this is ReadOnlyIPAddress )
578
619
{
579
- if ( this is ReadOnlyIPAddress )
580
- {
581
- throw new SocketException ( SocketError . OperationNotSupported ) ;
582
- }
583
- PrivateAddress = unchecked ( ( uint ) value ) ;
620
+ ThrowSocketOperationNotSupported ( ) ;
584
621
}
622
+
623
+ PrivateAddress = unchecked ( ( uint ) value ) ;
585
624
}
586
625
}
587
626
}
@@ -677,6 +716,9 @@ public IPAddress MapToIPv4()
677
716
[ DoesNotReturn ]
678
717
private static byte [ ] ThrowAddressNullException ( ) => throw new ArgumentNullException ( "address" ) ;
679
718
719
+ [ DoesNotReturn ]
720
+ private static void ThrowSocketOperationNotSupported ( ) => throw new SocketException ( SocketError . OperationNotSupported ) ;
721
+
680
722
private sealed class ReadOnlyIPAddress : IPAddress
681
723
{
682
724
public ReadOnlyIPAddress ( ReadOnlySpan < byte > newAddress ) : base ( newAddress )
0 commit comments