| File: System\Net\NetworkInformation\PhysicalAddress.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.NetworkInformation\src\System.Net.NetworkInformation.csproj (System.Net.NetworkInformation) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; namespace System.Net.NetworkInformation { public class PhysicalAddress { private readonly byte[] _address; private int _hash; public static readonly PhysicalAddress None = new PhysicalAddress(Array.Empty<byte>()); public PhysicalAddress(byte[] address) { _address = address; } public override int GetHashCode() { if (_hash == 0) { int hash = 0; int i; int size = _address.Length & ~3; for (i = 0; i < size; i += 4) { hash ^= BinaryPrimitives.ReadInt32LittleEndian(_address.AsSpan(i)); } if ((_address.Length & 3) != 0) { int remnant = 0; int shift = 0; for (; i < _address.Length; ++i) { remnant |= ((int)_address[i]) << shift; shift += 8; } hash ^= remnant; } if (hash == 0) { hash = 1; } _hash = hash; } return _hash; } public override bool Equals([NotNullWhen(true)] object? comparand) => comparand is PhysicalAddress other && _address.Length == other._address.Length && GetHashCode() == other.GetHashCode() && _address.AsSpan().SequenceEqual(other._address); public override string ToString() { return Convert.ToHexString(_address.AsSpan()); } public byte[] GetAddressBytes() { return (byte[])_address.Clone(); } public static PhysicalAddress Parse(string? address) => address != null ? Parse(address.AsSpan()) : None; public static PhysicalAddress Parse(ReadOnlySpan<char> address) { if (!TryParse(address, out PhysicalAddress? value)) throw new FormatException(SR.Format(SR.net_bad_mac_address, new string(address))); return value; } public static bool TryParse(string? address, [NotNullWhen(true)] out PhysicalAddress? value) { if (address == null) { value = None; return true; } return TryParse(address.AsSpan(), out value); } public static bool TryParse(ReadOnlySpan<char> address, [NotNullWhen(true)] out PhysicalAddress? value) { int validSegmentLength; char? delimiter = null; byte[] buffer; value = null; if (address.Contains('-')) { if ((address.Length + 1) % 3 != 0) { return false; } delimiter = '-'; buffer = new byte[(address.Length + 1) / 3]; // allow any length that's a multiple of 3 validSegmentLength = 2; } else if (address.Contains(':')) { delimiter = ':'; if (!TryGetValidSegmentLength(address, ':', out validSegmentLength)) { return false; } if (validSegmentLength != 2 && validSegmentLength != 4) { return false; } buffer = new byte[6]; } else if (address.Contains('.')) { delimiter = '.'; if (!TryGetValidSegmentLength(address, '.', out validSegmentLength)) { return false; } if (validSegmentLength != 4) { return false; } buffer = new byte[6]; } else { if (address.Length % 2 > 0) { return false; } validSegmentLength = address.Length; buffer = new byte[address.Length / 2]; } int validCount = 0; int j = 0; for (int i = 0; i < address.Length; i++) { int character = address[i]; int tmp; if ((tmp = HexConverter.FromChar(character)) == 0xFF) { if (delimiter == character && validCount == validSegmentLength) { validCount = 0; continue; } return false; } character = tmp; // we had too many characters after the last delimiter if (validCount >= validSegmentLength) { return false; } if (validCount % 2 == 0) { buffer[j] = (byte)(character << 4); } else { buffer[j++] |= (byte)character; } validCount++; } // we had too few characters after the last delimiter if (validCount < validSegmentLength) { return false; } value = new PhysicalAddress(buffer); return true; } private static bool TryGetValidSegmentLength(ReadOnlySpan<char> address, char delimiter, out int value) { value = -1; int segments = 1; int validSegmentLength = 0; for (int i = 0; i < address.Length; i++) { if (address[i] == delimiter) { if (validSegmentLength == 0) { validSegmentLength = i; } else if ((i - (segments - 1)) % validSegmentLength != 0) { // segments - 1 = num of delimiters. Return false if new segment isn't the validSegmentLength return false; } segments++; } } if (segments * validSegmentLength != 12) { return false; } value = validSegmentLength; return true; } } }