// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; internal static partial class Interop { internal static partial class Sys { internal const int IPv4AddressBytes = 4; internal const int IPv6AddressBytes = 16; internal const int MAX_IP_ADDRESS_BYTES = 16; internal const int INET_ADDRSTRLEN = 22; internal const int INET6_ADDRSTRLEN = 65; // NOTE: `_isIPv6` cannot be of type `bool` because `bool` is not a blittable type and this struct is // embedded in other structs for interop purposes. [StructLayout(LayoutKind.Sequential)] internal unsafe struct IPAddress : IEquatable<IPAddress> { public bool IsIPv6 { get { return _isIPv6 != 0; } set { _isIPv6 = value ? 1u : 0u; } } internal fixed byte Address[MAX_IP_ADDRESS_BYTES]; // Buffer to fit an IPv4 or IPv6 address private uint _isIPv6; // Non-zero if this is an IPv6 address; zero for IPv4. internal uint ScopeId; // Scope ID (IPv6 only) public override int GetHashCode() { HashCode h = default; h.AddBytes(MemoryMarshal.CreateReadOnlySpan(ref Address[0], IsIPv6 ? IPv6AddressBytes : IPv4AddressBytes)); return h.ToHashCode(); } public override bool Equals([NotNullWhen(true)] object? obj) => obj is IPAddress other && Equals(other); public bool Equals(IPAddress other) { int addressByteCount; if (IsIPv6) { if (!other.IsIPv6) { return false; } if (ScopeId != other.ScopeId) { return false; } addressByteCount = IPv6AddressBytes; } else { if (other.IsIPv6) { return false; } addressByteCount = IPv4AddressBytes; } return MemoryMarshal.CreateReadOnlySpan(ref Address[0], addressByteCount).SequenceEqual( new ReadOnlySpan<byte>(other.Address, addressByteCount)); } } } }