// Package hashable provides handy utility types for making unhashable values
// hashable.
package hashable
import (
"net"
"net/netip"
)
// MACAddr is a hashable encoding of a MAC address.
type MACAddr uint64
// MACAddrFromSlice parses the 6-byte slice as a MAC-48 address.
// Note that a [net.HardwareAddr] can be passed directly as the []byte argument.
// If slice's length is not 6, MACAddrFromSlice returns 0, false.
func MACAddrFromSlice(slice net.HardwareAddr) (MACAddr, bool) {
if len(slice) != 6 {
return 0, false
}
return MACAddrFrom6([6]byte(slice)), true
}
// MACAddrFrom6 returns the address of the MAC-48 address
// given by the bytes in addr.
func MACAddrFrom6(addr [6]byte) MACAddr {
return MACAddr(addr[0])<<40 | MACAddr(addr[1])<<32 | MACAddr(addr[2])<<24 |
MACAddr(addr[3])<<16 | MACAddr(addr[4])<<8 | MACAddr(addr[5])
}
// ParseMAC parses s as an IEEE 802 MAC-48 address using one of the formats
// accepted by [net.ParseMAC].
func ParseMAC(s string) (MACAddr, error) {
hw, err := net.ParseMAC(s)
if err != nil {
return 0, err
}
mac, ok := MACAddrFromSlice(hw)
if !ok {
return 0, &net.AddrError{Err: "not a MAC-48 address", Addr: s}
}
return mac, nil
}
// AsSlice returns a MAC address in its 6-byte representation.
func (p MACAddr) AsSlice() []byte {
mac := [6]byte{
byte(p >> 40), byte(p >> 32), byte(p >> 24),
byte(p >> 16), byte(p >> 8), byte(p),
}
return mac[:]
}
// String returns net.HardwareAddr(p.AsSlice()).String().
func (p MACAddr) String() string {
return net.HardwareAddr(p.AsSlice()).String()
}
// IPMAC is a hashable tuple of an IP address and a MAC address suitable for use as a map key.
type IPMAC struct {
ip netip.Addr
mac MACAddr
}
// IPMACFrom returns an [IPMAC] with the provided IP and MAC addresses.
func IPMACFrom(ip netip.Addr, mac MACAddr) IPMAC {
return IPMAC{ip: ip, mac: mac}
}
func (i IPMAC) String() string {
return i.ip.String() + " " + i.mac.String()
}
func (i IPMAC) IP() netip.Addr {
return i.ip
}
func (i IPMAC) MAC() MACAddr {
return i.mac
}