# Source code for netaddr.strategy.eui48

    #-----------------------------------------------------------------------------
    #   Copyright (c) 2008-2015, David P. D. Moss. All rights reserved.
    #
    #   Released under the BSD license. See the LICENSE file for details.
    #-----------------------------------------------------------------------------
    """
    IEEE 48-bit EUI (MAC address) logic.

    Supports numerous MAC string formats including Cisco's triple hextet as well
    as bare MACs containing no delimiters.
    """
    import struct as _struct
    import re as _re

    #   Check whether we need to use fallback code or not.
    try:
        from socket import AF_LINK
    except ImportError:
        AF_LINK = 48

    from netaddr.core import AddrFormatError
    from netaddr.compat import _is_str
    from netaddr.strategy import (
        valid_words as _valid_words, int_to_words as _int_to_words,
        words_to_int as _words_to_int, valid_bits as _valid_bits,
        bits_to_int as _bits_to_int, int_to_bits as _int_to_bits,
        valid_bin as _valid_bin, int_to_bin as _int_to_bin,
        bin_to_int as _bin_to_int)

    #: The width (in bits) of this address type.
    width = 48

    #: The AF_* constant value of this address type.
    family = AF_LINK

    #: A friendly string name address type.
    family_name = 'MAC'

    #: The version of this address type.
    version = 48

    #: The maximum integer value that can be represented by this address type.
    max_int = 2 ** width - 1

    #-----------------------------------------------------------------------------
    #   Dialect classes.
    #-----------------------------------------------------------------------------

    class mac_eui48(object):
        """A standard IEEE EUI-48 dialect class."""
        #: The individual word size (in bits) of this address type.
        word_size = 8

        #: The number of words in this address type.
        num_words = width // word_size

        #: The maximum integer value for an individual word in this address type.
        max_word = 2 ** word_size - 1

        #: The separator character used between each word.
        word_sep = '-'

        #: The format string to be used when converting words to string values.
        word_fmt = '%.2X'

        #: The number base to be used when interpreting word values as integers.
        word_base = 16

    class mac_unix(mac_eui48):
        """A UNIX-style MAC address dialect class."""
        word_size = 8
        num_words = width // word_size
        word_sep = ':'
        word_fmt = '%x'
        word_base = 16

    class mac_unix_expanded(mac_unix):
        """A UNIX-style MAC address dialect class with leading zeroes."""
        word_fmt = '%.2x'

    class mac_cisco(mac_eui48):
        """A Cisco 'triple hextet' MAC address dialect class."""
        word_size = 16
        num_words = width // word_size
        word_sep = '.'
        word_fmt = '%.4x'
        word_base = 16

    class mac_bare(mac_eui48):
        """A bare (no delimiters) MAC address dialect class."""
        word_size = 48
        num_words = width // word_size
        word_sep = ''
        word_fmt = '%.12X'
        word_base = 16

    class mac_pgsql(mac_eui48):
        """A PostgreSQL style (2 x 24-bit words) MAC address dialect class."""
        word_size = 24
        num_words = width // word_size
        word_sep = ':'
        word_fmt = '%.6x'
        word_base = 16

    #: The default dialect to be used when not specified by the user.
    DEFAULT_DIALECT = mac_eui48

    #-----------------------------------------------------------------------------
    #: Regular expressions to match all supported MAC address formats.
    RE_MAC_FORMATS = (
        #   2 bytes x 6 (UNIX, Windows, EUI-48)
        '^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$',
        '^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$',

        #   4 bytes x 3 (Cisco)
        '^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$',
        '^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$',
        '^' + '\.'.join(['([0-9A-F]{1,4})'] * 3) + '$',

        #   6 bytes x 2 (PostgreSQL)
        '^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$',
        '^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$',

        #   12 bytes (bare, no delimiters)
        '^(' + ''.join(['[0-9A-F]'] * 12) + ')$',
        '^(' + ''.join(['[0-9A-F]'] * 11) + ')$',
    )
    #   For efficiency, each string regexp converted in place to its compiled
    #   counterpart.
    RE_MAC_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_MAC_FORMATS]

    def valid_str(addr):
        """
        :param addr: An IEEE EUI-48 (MAC) address in string form.

        :return: ``True`` if MAC address string is valid, ``False`` otherwise.
        """
        for regexp in RE_MAC_FORMATS:
            try:
                match_result = regexp.findall(addr)
                if len(match_result) != 0:
                    return True
            except TypeError:
                pass

        return False

    def str_to_int(addr):
        """
        :param addr: An IEEE EUI-48 (MAC) address in string form.

        :return: An unsigned integer that is equivalent to value represented
            by EUI-48/MAC string address formatted according to the dialect
            settings.
        """
        words = []
        if _is_str(addr):
            found_match = False
            for regexp in RE_MAC_FORMATS:
                match_result = regexp.findall(addr)
                if len(match_result) != 0:
                    found_match = True
                    if isinstance(match_result[0], tuple):
                        words = match_result[0]
                    else:
                        words = (match_result[0],)
                    break
            if not found_match:
                raise AddrFormatError('%r is not a supported MAC format!' % addr)
        else:
            raise TypeError('%r is not str() or unicode()!' % addr)

        int_val = None

        if len(words) == 6:
            #   2 bytes x 6 (UNIX, Windows, EUI-48)
            int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
        elif len(words) == 3:
            #   4 bytes x 3 (Cisco)
            int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16)
        elif len(words) == 2:
            #   6 bytes x 2 (PostgreSQL)
            int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16)
        elif len(words) == 1:
            #   12 bytes (bare, no delimiters)
            int_val = int('%012x' % int(words[0], 16), 16)
        else:
            raise AddrFormatError('unexpected word count in MAC address %r!' % addr)

        return int_val

    def int_to_str(int_val, dialect=None):
        """
        :param int_val: An unsigned integer.

        :param dialect: (optional) a Python class defining formatting options.

        :return: An IEEE EUI-48 (MAC) address string that is equivalent to
            unsigned integer formatted according to the dialect settings.
        """
        if dialect is None:
            dialect = mac_eui48

        words = int_to_words(int_val, dialect)
        tokens = [dialect.word_fmt % i for i in words]
        addr = dialect.word_sep.join(tokens)

        return addr

    def int_to_packed(int_val):
        """
        :param int_val: the integer to be packed.

        :return: a packed string that is equivalent to value represented by an
        unsigned integer.
        """
        return _struct.pack(">HI", int_val >> 32, int_val & 0xffffffff)

    def packed_to_int(packed_int):
        """
        :param packed_int: a packed string containing an unsigned integer.
            It is assumed that string is packed in network byte order.

        :return: An unsigned integer equivalent to value of network address
            represented by packed binary string.
        """
        words = list(_struct.unpack('>6B', packed_int))

        int_val = 0
        for i, num in enumerate(reversed(words)):
            word = num
            word = word << 8 * i
            int_val = int_val | word

        return int_val

    def valid_words(words, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _valid_words(words, dialect.word_size, dialect.num_words)

    def int_to_words(int_val, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _int_to_words(int_val, dialect.word_size, dialect.num_words)

    def words_to_int(words, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _words_to_int(words, dialect.word_size, dialect.num_words)

    def valid_bits(bits, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _valid_bits(bits, width, dialect.word_sep)

    def bits_to_int(bits, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _bits_to_int(bits, width, dialect.word_sep)

    def int_to_bits(int_val, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _int_to_bits(
            int_val, dialect.word_size, dialect.num_words, dialect.word_sep)

    def valid_bin(bin_val, dialect=None):
        if dialect is None:
            dialect = DEFAULT_DIALECT
        return _valid_bin(bin_val, width)

    def int_to_bin(int_val):
        return _int_to_bin(int_val, width)

    def bin_to_int(bin_val):
        return _bin_to_int(bin_val, width)