17db96d56Sopenharmony_ci#! /usr/bin/env python3
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci"""Base16, Base32, Base64 (RFC 3548), Base85 and Ascii85 data encodings"""
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ci# Modified 04-Oct-1995 by Jack Jansen to use binascii module
67db96d56Sopenharmony_ci# Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support
77db96d56Sopenharmony_ci# Modified 22-May-2007 by Guido van Rossum to use bytes everywhere
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ciimport re
107db96d56Sopenharmony_ciimport struct
117db96d56Sopenharmony_ciimport binascii
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci__all__ = [
157db96d56Sopenharmony_ci    # Legacy interface exports traditional RFC 2045 Base64 encodings
167db96d56Sopenharmony_ci    'encode', 'decode', 'encodebytes', 'decodebytes',
177db96d56Sopenharmony_ci    # Generalized interface for other encodings
187db96d56Sopenharmony_ci    'b64encode', 'b64decode', 'b32encode', 'b32decode',
197db96d56Sopenharmony_ci    'b32hexencode', 'b32hexdecode', 'b16encode', 'b16decode',
207db96d56Sopenharmony_ci    # Base85 and Ascii85 encodings
217db96d56Sopenharmony_ci    'b85encode', 'b85decode', 'a85encode', 'a85decode',
227db96d56Sopenharmony_ci    # Standard Base64 encoding
237db96d56Sopenharmony_ci    'standard_b64encode', 'standard_b64decode',
247db96d56Sopenharmony_ci    # Some common Base64 alternatives.  As referenced by RFC 3458, see thread
257db96d56Sopenharmony_ci    # starting at:
267db96d56Sopenharmony_ci    #
277db96d56Sopenharmony_ci    # http://zgp.org/pipermail/p2p-hackers/2001-September/000316.html
287db96d56Sopenharmony_ci    'urlsafe_b64encode', 'urlsafe_b64decode',
297db96d56Sopenharmony_ci    ]
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_cibytes_types = (bytes, bytearray)  # Types acceptable as binary data
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_cidef _bytes_from_decode_data(s):
357db96d56Sopenharmony_ci    if isinstance(s, str):
367db96d56Sopenharmony_ci        try:
377db96d56Sopenharmony_ci            return s.encode('ascii')
387db96d56Sopenharmony_ci        except UnicodeEncodeError:
397db96d56Sopenharmony_ci            raise ValueError('string argument should contain only ASCII characters')
407db96d56Sopenharmony_ci    if isinstance(s, bytes_types):
417db96d56Sopenharmony_ci        return s
427db96d56Sopenharmony_ci    try:
437db96d56Sopenharmony_ci        return memoryview(s).tobytes()
447db96d56Sopenharmony_ci    except TypeError:
457db96d56Sopenharmony_ci        raise TypeError("argument should be a bytes-like object or ASCII "
467db96d56Sopenharmony_ci                        "string, not %r" % s.__class__.__name__) from None
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_ci
497db96d56Sopenharmony_ci# Base64 encoding/decoding uses binascii
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_cidef b64encode(s, altchars=None):
527db96d56Sopenharmony_ci    """Encode the bytes-like object s using Base64 and return a bytes object.
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_ci    Optional altchars should be a byte string of length 2 which specifies an
557db96d56Sopenharmony_ci    alternative alphabet for the '+' and '/' characters.  This allows an
567db96d56Sopenharmony_ci    application to e.g. generate url or filesystem safe Base64 strings.
577db96d56Sopenharmony_ci    """
587db96d56Sopenharmony_ci    encoded = binascii.b2a_base64(s, newline=False)
597db96d56Sopenharmony_ci    if altchars is not None:
607db96d56Sopenharmony_ci        assert len(altchars) == 2, repr(altchars)
617db96d56Sopenharmony_ci        return encoded.translate(bytes.maketrans(b'+/', altchars))
627db96d56Sopenharmony_ci    return encoded
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ci
657db96d56Sopenharmony_cidef b64decode(s, altchars=None, validate=False):
667db96d56Sopenharmony_ci    """Decode the Base64 encoded bytes-like object or ASCII string s.
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_ci    Optional altchars must be a bytes-like object or ASCII string of length 2
697db96d56Sopenharmony_ci    which specifies the alternative alphabet used instead of the '+' and '/'
707db96d56Sopenharmony_ci    characters.
717db96d56Sopenharmony_ci
727db96d56Sopenharmony_ci    The result is returned as a bytes object.  A binascii.Error is raised if
737db96d56Sopenharmony_ci    s is incorrectly padded.
747db96d56Sopenharmony_ci
757db96d56Sopenharmony_ci    If validate is False (the default), characters that are neither in the
767db96d56Sopenharmony_ci    normal base-64 alphabet nor the alternative alphabet are discarded prior
777db96d56Sopenharmony_ci    to the padding check.  If validate is True, these non-alphabet characters
787db96d56Sopenharmony_ci    in the input result in a binascii.Error.
797db96d56Sopenharmony_ci    For more information about the strict base64 check, see:
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    https://docs.python.org/3.11/library/binascii.html#binascii.a2b_base64
827db96d56Sopenharmony_ci    """
837db96d56Sopenharmony_ci    s = _bytes_from_decode_data(s)
847db96d56Sopenharmony_ci    if altchars is not None:
857db96d56Sopenharmony_ci        altchars = _bytes_from_decode_data(altchars)
867db96d56Sopenharmony_ci        assert len(altchars) == 2, repr(altchars)
877db96d56Sopenharmony_ci        s = s.translate(bytes.maketrans(altchars, b'+/'))
887db96d56Sopenharmony_ci    return binascii.a2b_base64(s, strict_mode=validate)
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci
917db96d56Sopenharmony_cidef standard_b64encode(s):
927db96d56Sopenharmony_ci    """Encode bytes-like object s using the standard Base64 alphabet.
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_ci    The result is returned as a bytes object.
957db96d56Sopenharmony_ci    """
967db96d56Sopenharmony_ci    return b64encode(s)
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_cidef standard_b64decode(s):
997db96d56Sopenharmony_ci    """Decode bytes encoded with the standard Base64 alphabet.
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_ci    Argument s is a bytes-like object or ASCII string to decode.  The result
1027db96d56Sopenharmony_ci    is returned as a bytes object.  A binascii.Error is raised if the input
1037db96d56Sopenharmony_ci    is incorrectly padded.  Characters that are not in the standard alphabet
1047db96d56Sopenharmony_ci    are discarded prior to the padding check.
1057db96d56Sopenharmony_ci    """
1067db96d56Sopenharmony_ci    return b64decode(s)
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ci
1097db96d56Sopenharmony_ci_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_')
1107db96d56Sopenharmony_ci_urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/')
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_cidef urlsafe_b64encode(s):
1137db96d56Sopenharmony_ci    """Encode bytes using the URL- and filesystem-safe Base64 alphabet.
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci    Argument s is a bytes-like object to encode.  The result is returned as a
1167db96d56Sopenharmony_ci    bytes object.  The alphabet uses '-' instead of '+' and '_' instead of
1177db96d56Sopenharmony_ci    '/'.
1187db96d56Sopenharmony_ci    """
1197db96d56Sopenharmony_ci    return b64encode(s).translate(_urlsafe_encode_translation)
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_cidef urlsafe_b64decode(s):
1227db96d56Sopenharmony_ci    """Decode bytes using the URL- and filesystem-safe Base64 alphabet.
1237db96d56Sopenharmony_ci
1247db96d56Sopenharmony_ci    Argument s is a bytes-like object or ASCII string to decode.  The result
1257db96d56Sopenharmony_ci    is returned as a bytes object.  A binascii.Error is raised if the input
1267db96d56Sopenharmony_ci    is incorrectly padded.  Characters that are not in the URL-safe base-64
1277db96d56Sopenharmony_ci    alphabet, and are not a plus '+' or slash '/', are discarded prior to the
1287db96d56Sopenharmony_ci    padding check.
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci    The alphabet uses '-' instead of '+' and '_' instead of '/'.
1317db96d56Sopenharmony_ci    """
1327db96d56Sopenharmony_ci    s = _bytes_from_decode_data(s)
1337db96d56Sopenharmony_ci    s = s.translate(_urlsafe_decode_translation)
1347db96d56Sopenharmony_ci    return b64decode(s)
1357db96d56Sopenharmony_ci
1367db96d56Sopenharmony_ci
1377db96d56Sopenharmony_ci
1387db96d56Sopenharmony_ci# Base32 encoding/decoding must be done in Python
1397db96d56Sopenharmony_ci_B32_ENCODE_DOCSTRING = '''
1407db96d56Sopenharmony_ciEncode the bytes-like objects using {encoding} and return a bytes object.
1417db96d56Sopenharmony_ci'''
1427db96d56Sopenharmony_ci_B32_DECODE_DOCSTRING = '''
1437db96d56Sopenharmony_ciDecode the {encoding} encoded bytes-like object or ASCII string s.
1447db96d56Sopenharmony_ci
1457db96d56Sopenharmony_ciOptional casefold is a flag specifying whether a lowercase alphabet is
1467db96d56Sopenharmony_ciacceptable as input.  For security purposes, the default is False.
1477db96d56Sopenharmony_ci{extra_args}
1487db96d56Sopenharmony_ciThe result is returned as a bytes object.  A binascii.Error is raised if
1497db96d56Sopenharmony_cithe input is incorrectly padded or if there are non-alphabet
1507db96d56Sopenharmony_cicharacters present in the input.
1517db96d56Sopenharmony_ci'''
1527db96d56Sopenharmony_ci_B32_DECODE_MAP01_DOCSTRING = '''
1537db96d56Sopenharmony_ciRFC 3548 allows for optional mapping of the digit 0 (zero) to the
1547db96d56Sopenharmony_ciletter O (oh), and for optional mapping of the digit 1 (one) to
1557db96d56Sopenharmony_cieither the letter I (eye) or letter L (el).  The optional argument
1567db96d56Sopenharmony_cimap01 when not None, specifies which letter the digit 1 should be
1577db96d56Sopenharmony_cimapped to (when map01 is not None, the digit 0 is always mapped to
1587db96d56Sopenharmony_cithe letter O).  For security purposes the default is None, so that
1597db96d56Sopenharmony_ci0 and 1 are not allowed in the input.
1607db96d56Sopenharmony_ci'''
1617db96d56Sopenharmony_ci_b32alphabet = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
1627db96d56Sopenharmony_ci_b32hexalphabet = b'0123456789ABCDEFGHIJKLMNOPQRSTUV'
1637db96d56Sopenharmony_ci_b32tab2 = {}
1647db96d56Sopenharmony_ci_b32rev = {}
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_cidef _b32encode(alphabet, s):
1677db96d56Sopenharmony_ci    global _b32tab2
1687db96d56Sopenharmony_ci    # Delay the initialization of the table to not waste memory
1697db96d56Sopenharmony_ci    # if the function is never called
1707db96d56Sopenharmony_ci    if alphabet not in _b32tab2:
1717db96d56Sopenharmony_ci        b32tab = [bytes((i,)) for i in alphabet]
1727db96d56Sopenharmony_ci        _b32tab2[alphabet] = [a + b for a in b32tab for b in b32tab]
1737db96d56Sopenharmony_ci        b32tab = None
1747db96d56Sopenharmony_ci
1757db96d56Sopenharmony_ci    if not isinstance(s, bytes_types):
1767db96d56Sopenharmony_ci        s = memoryview(s).tobytes()
1777db96d56Sopenharmony_ci    leftover = len(s) % 5
1787db96d56Sopenharmony_ci    # Pad the last quantum with zero bits if necessary
1797db96d56Sopenharmony_ci    if leftover:
1807db96d56Sopenharmony_ci        s = s + b'\0' * (5 - leftover)  # Don't use += !
1817db96d56Sopenharmony_ci    encoded = bytearray()
1827db96d56Sopenharmony_ci    from_bytes = int.from_bytes
1837db96d56Sopenharmony_ci    b32tab2 = _b32tab2[alphabet]
1847db96d56Sopenharmony_ci    for i in range(0, len(s), 5):
1857db96d56Sopenharmony_ci        c = from_bytes(s[i: i + 5])              # big endian
1867db96d56Sopenharmony_ci        encoded += (b32tab2[c >> 30] +           # bits 1 - 10
1877db96d56Sopenharmony_ci                    b32tab2[(c >> 20) & 0x3ff] + # bits 11 - 20
1887db96d56Sopenharmony_ci                    b32tab2[(c >> 10) & 0x3ff] + # bits 21 - 30
1897db96d56Sopenharmony_ci                    b32tab2[c & 0x3ff]           # bits 31 - 40
1907db96d56Sopenharmony_ci                   )
1917db96d56Sopenharmony_ci    # Adjust for any leftover partial quanta
1927db96d56Sopenharmony_ci    if leftover == 1:
1937db96d56Sopenharmony_ci        encoded[-6:] = b'======'
1947db96d56Sopenharmony_ci    elif leftover == 2:
1957db96d56Sopenharmony_ci        encoded[-4:] = b'===='
1967db96d56Sopenharmony_ci    elif leftover == 3:
1977db96d56Sopenharmony_ci        encoded[-3:] = b'==='
1987db96d56Sopenharmony_ci    elif leftover == 4:
1997db96d56Sopenharmony_ci        encoded[-1:] = b'='
2007db96d56Sopenharmony_ci    return bytes(encoded)
2017db96d56Sopenharmony_ci
2027db96d56Sopenharmony_cidef _b32decode(alphabet, s, casefold=False, map01=None):
2037db96d56Sopenharmony_ci    global _b32rev
2047db96d56Sopenharmony_ci    # Delay the initialization of the table to not waste memory
2057db96d56Sopenharmony_ci    # if the function is never called
2067db96d56Sopenharmony_ci    if alphabet not in _b32rev:
2077db96d56Sopenharmony_ci        _b32rev[alphabet] = {v: k for k, v in enumerate(alphabet)}
2087db96d56Sopenharmony_ci    s = _bytes_from_decode_data(s)
2097db96d56Sopenharmony_ci    if len(s) % 8:
2107db96d56Sopenharmony_ci        raise binascii.Error('Incorrect padding')
2117db96d56Sopenharmony_ci    # Handle section 2.4 zero and one mapping.  The flag map01 will be either
2127db96d56Sopenharmony_ci    # False, or the character to map the digit 1 (one) to.  It should be
2137db96d56Sopenharmony_ci    # either L (el) or I (eye).
2147db96d56Sopenharmony_ci    if map01 is not None:
2157db96d56Sopenharmony_ci        map01 = _bytes_from_decode_data(map01)
2167db96d56Sopenharmony_ci        assert len(map01) == 1, repr(map01)
2177db96d56Sopenharmony_ci        s = s.translate(bytes.maketrans(b'01', b'O' + map01))
2187db96d56Sopenharmony_ci    if casefold:
2197db96d56Sopenharmony_ci        s = s.upper()
2207db96d56Sopenharmony_ci    # Strip off pad characters from the right.  We need to count the pad
2217db96d56Sopenharmony_ci    # characters because this will tell us how many null bytes to remove from
2227db96d56Sopenharmony_ci    # the end of the decoded string.
2237db96d56Sopenharmony_ci    l = len(s)
2247db96d56Sopenharmony_ci    s = s.rstrip(b'=')
2257db96d56Sopenharmony_ci    padchars = l - len(s)
2267db96d56Sopenharmony_ci    # Now decode the full quanta
2277db96d56Sopenharmony_ci    decoded = bytearray()
2287db96d56Sopenharmony_ci    b32rev = _b32rev[alphabet]
2297db96d56Sopenharmony_ci    for i in range(0, len(s), 8):
2307db96d56Sopenharmony_ci        quanta = s[i: i + 8]
2317db96d56Sopenharmony_ci        acc = 0
2327db96d56Sopenharmony_ci        try:
2337db96d56Sopenharmony_ci            for c in quanta:
2347db96d56Sopenharmony_ci                acc = (acc << 5) + b32rev[c]
2357db96d56Sopenharmony_ci        except KeyError:
2367db96d56Sopenharmony_ci            raise binascii.Error('Non-base32 digit found') from None
2377db96d56Sopenharmony_ci        decoded += acc.to_bytes(5)  # big endian
2387db96d56Sopenharmony_ci    # Process the last, partial quanta
2397db96d56Sopenharmony_ci    if l % 8 or padchars not in {0, 1, 3, 4, 6}:
2407db96d56Sopenharmony_ci        raise binascii.Error('Incorrect padding')
2417db96d56Sopenharmony_ci    if padchars and decoded:
2427db96d56Sopenharmony_ci        acc <<= 5 * padchars
2437db96d56Sopenharmony_ci        last = acc.to_bytes(5)  # big endian
2447db96d56Sopenharmony_ci        leftover = (43 - 5 * padchars) // 8  # 1: 4, 3: 3, 4: 2, 6: 1
2457db96d56Sopenharmony_ci        decoded[-5:] = last[:leftover]
2467db96d56Sopenharmony_ci    return bytes(decoded)
2477db96d56Sopenharmony_ci
2487db96d56Sopenharmony_ci
2497db96d56Sopenharmony_cidef b32encode(s):
2507db96d56Sopenharmony_ci    return _b32encode(_b32alphabet, s)
2517db96d56Sopenharmony_cib32encode.__doc__ = _B32_ENCODE_DOCSTRING.format(encoding='base32')
2527db96d56Sopenharmony_ci
2537db96d56Sopenharmony_cidef b32decode(s, casefold=False, map01=None):
2547db96d56Sopenharmony_ci    return _b32decode(_b32alphabet, s, casefold, map01)
2557db96d56Sopenharmony_cib32decode.__doc__ = _B32_DECODE_DOCSTRING.format(encoding='base32',
2567db96d56Sopenharmony_ci                                        extra_args=_B32_DECODE_MAP01_DOCSTRING)
2577db96d56Sopenharmony_ci
2587db96d56Sopenharmony_cidef b32hexencode(s):
2597db96d56Sopenharmony_ci    return _b32encode(_b32hexalphabet, s)
2607db96d56Sopenharmony_cib32hexencode.__doc__ = _B32_ENCODE_DOCSTRING.format(encoding='base32hex')
2617db96d56Sopenharmony_ci
2627db96d56Sopenharmony_cidef b32hexdecode(s, casefold=False):
2637db96d56Sopenharmony_ci    # base32hex does not have the 01 mapping
2647db96d56Sopenharmony_ci    return _b32decode(_b32hexalphabet, s, casefold)
2657db96d56Sopenharmony_cib32hexdecode.__doc__ = _B32_DECODE_DOCSTRING.format(encoding='base32hex',
2667db96d56Sopenharmony_ci                                                    extra_args='')
2677db96d56Sopenharmony_ci
2687db96d56Sopenharmony_ci
2697db96d56Sopenharmony_ci# RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns
2707db96d56Sopenharmony_ci# lowercase.  The RFC also recommends against accepting input case
2717db96d56Sopenharmony_ci# insensitively.
2727db96d56Sopenharmony_cidef b16encode(s):
2737db96d56Sopenharmony_ci    """Encode the bytes-like object s using Base16 and return a bytes object.
2747db96d56Sopenharmony_ci    """
2757db96d56Sopenharmony_ci    return binascii.hexlify(s).upper()
2767db96d56Sopenharmony_ci
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_cidef b16decode(s, casefold=False):
2797db96d56Sopenharmony_ci    """Decode the Base16 encoded bytes-like object or ASCII string s.
2807db96d56Sopenharmony_ci
2817db96d56Sopenharmony_ci    Optional casefold is a flag specifying whether a lowercase alphabet is
2827db96d56Sopenharmony_ci    acceptable as input.  For security purposes, the default is False.
2837db96d56Sopenharmony_ci
2847db96d56Sopenharmony_ci    The result is returned as a bytes object.  A binascii.Error is raised if
2857db96d56Sopenharmony_ci    s is incorrectly padded or if there are non-alphabet characters present
2867db96d56Sopenharmony_ci    in the input.
2877db96d56Sopenharmony_ci    """
2887db96d56Sopenharmony_ci    s = _bytes_from_decode_data(s)
2897db96d56Sopenharmony_ci    if casefold:
2907db96d56Sopenharmony_ci        s = s.upper()
2917db96d56Sopenharmony_ci    if re.search(b'[^0-9A-F]', s):
2927db96d56Sopenharmony_ci        raise binascii.Error('Non-base16 digit found')
2937db96d56Sopenharmony_ci    return binascii.unhexlify(s)
2947db96d56Sopenharmony_ci
2957db96d56Sopenharmony_ci#
2967db96d56Sopenharmony_ci# Ascii85 encoding/decoding
2977db96d56Sopenharmony_ci#
2987db96d56Sopenharmony_ci
2997db96d56Sopenharmony_ci_a85chars = None
3007db96d56Sopenharmony_ci_a85chars2 = None
3017db96d56Sopenharmony_ci_A85START = b"<~"
3027db96d56Sopenharmony_ci_A85END = b"~>"
3037db96d56Sopenharmony_ci
3047db96d56Sopenharmony_cidef _85encode(b, chars, chars2, pad=False, foldnuls=False, foldspaces=False):
3057db96d56Sopenharmony_ci    # Helper function for a85encode and b85encode
3067db96d56Sopenharmony_ci    if not isinstance(b, bytes_types):
3077db96d56Sopenharmony_ci        b = memoryview(b).tobytes()
3087db96d56Sopenharmony_ci
3097db96d56Sopenharmony_ci    padding = (-len(b)) % 4
3107db96d56Sopenharmony_ci    if padding:
3117db96d56Sopenharmony_ci        b = b + b'\0' * padding
3127db96d56Sopenharmony_ci    words = struct.Struct('!%dI' % (len(b) // 4)).unpack(b)
3137db96d56Sopenharmony_ci
3147db96d56Sopenharmony_ci    chunks = [b'z' if foldnuls and not word else
3157db96d56Sopenharmony_ci              b'y' if foldspaces and word == 0x20202020 else
3167db96d56Sopenharmony_ci              (chars2[word // 614125] +
3177db96d56Sopenharmony_ci               chars2[word // 85 % 7225] +
3187db96d56Sopenharmony_ci               chars[word % 85])
3197db96d56Sopenharmony_ci              for word in words]
3207db96d56Sopenharmony_ci
3217db96d56Sopenharmony_ci    if padding and not pad:
3227db96d56Sopenharmony_ci        if chunks[-1] == b'z':
3237db96d56Sopenharmony_ci            chunks[-1] = chars[0] * 5
3247db96d56Sopenharmony_ci        chunks[-1] = chunks[-1][:-padding]
3257db96d56Sopenharmony_ci
3267db96d56Sopenharmony_ci    return b''.join(chunks)
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_cidef a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
3297db96d56Sopenharmony_ci    """Encode bytes-like object b using Ascii85 and return a bytes object.
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci    foldspaces is an optional flag that uses the special short sequence 'y'
3327db96d56Sopenharmony_ci    instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This
3337db96d56Sopenharmony_ci    feature is not supported by the "standard" Adobe encoding.
3347db96d56Sopenharmony_ci
3357db96d56Sopenharmony_ci    wrapcol controls whether the output should have newline (b'\\n') characters
3367db96d56Sopenharmony_ci    added to it. If this is non-zero, each output line will be at most this
3377db96d56Sopenharmony_ci    many characters long.
3387db96d56Sopenharmony_ci
3397db96d56Sopenharmony_ci    pad controls whether the input is padded to a multiple of 4 before
3407db96d56Sopenharmony_ci    encoding. Note that the btoa implementation always pads.
3417db96d56Sopenharmony_ci
3427db96d56Sopenharmony_ci    adobe controls whether the encoded byte sequence is framed with <~ and ~>,
3437db96d56Sopenharmony_ci    which is used by the Adobe implementation.
3447db96d56Sopenharmony_ci    """
3457db96d56Sopenharmony_ci    global _a85chars, _a85chars2
3467db96d56Sopenharmony_ci    # Delay the initialization of tables to not waste memory
3477db96d56Sopenharmony_ci    # if the function is never called
3487db96d56Sopenharmony_ci    if _a85chars2 is None:
3497db96d56Sopenharmony_ci        _a85chars = [bytes((i,)) for i in range(33, 118)]
3507db96d56Sopenharmony_ci        _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]
3517db96d56Sopenharmony_ci
3527db96d56Sopenharmony_ci    result = _85encode(b, _a85chars, _a85chars2, pad, True, foldspaces)
3537db96d56Sopenharmony_ci
3547db96d56Sopenharmony_ci    if adobe:
3557db96d56Sopenharmony_ci        result = _A85START + result
3567db96d56Sopenharmony_ci    if wrapcol:
3577db96d56Sopenharmony_ci        wrapcol = max(2 if adobe else 1, wrapcol)
3587db96d56Sopenharmony_ci        chunks = [result[i: i + wrapcol]
3597db96d56Sopenharmony_ci                  for i in range(0, len(result), wrapcol)]
3607db96d56Sopenharmony_ci        if adobe:
3617db96d56Sopenharmony_ci            if len(chunks[-1]) + 2 > wrapcol:
3627db96d56Sopenharmony_ci                chunks.append(b'')
3637db96d56Sopenharmony_ci        result = b'\n'.join(chunks)
3647db96d56Sopenharmony_ci    if adobe:
3657db96d56Sopenharmony_ci        result += _A85END
3667db96d56Sopenharmony_ci
3677db96d56Sopenharmony_ci    return result
3687db96d56Sopenharmony_ci
3697db96d56Sopenharmony_cidef a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v'):
3707db96d56Sopenharmony_ci    """Decode the Ascii85 encoded bytes-like object or ASCII string b.
3717db96d56Sopenharmony_ci
3727db96d56Sopenharmony_ci    foldspaces is a flag that specifies whether the 'y' short sequence should be
3737db96d56Sopenharmony_ci    accepted as shorthand for 4 consecutive spaces (ASCII 0x20). This feature is
3747db96d56Sopenharmony_ci    not supported by the "standard" Adobe encoding.
3757db96d56Sopenharmony_ci
3767db96d56Sopenharmony_ci    adobe controls whether the input sequence is in Adobe Ascii85 format (i.e.
3777db96d56Sopenharmony_ci    is framed with <~ and ~>).
3787db96d56Sopenharmony_ci
3797db96d56Sopenharmony_ci    ignorechars should be a byte string containing characters to ignore from the
3807db96d56Sopenharmony_ci    input. This should only contain whitespace characters, and by default
3817db96d56Sopenharmony_ci    contains all whitespace characters in ASCII.
3827db96d56Sopenharmony_ci
3837db96d56Sopenharmony_ci    The result is returned as a bytes object.
3847db96d56Sopenharmony_ci    """
3857db96d56Sopenharmony_ci    b = _bytes_from_decode_data(b)
3867db96d56Sopenharmony_ci    if adobe:
3877db96d56Sopenharmony_ci        if not b.endswith(_A85END):
3887db96d56Sopenharmony_ci            raise ValueError(
3897db96d56Sopenharmony_ci                "Ascii85 encoded byte sequences must end "
3907db96d56Sopenharmony_ci                "with {!r}".format(_A85END)
3917db96d56Sopenharmony_ci                )
3927db96d56Sopenharmony_ci        if b.startswith(_A85START):
3937db96d56Sopenharmony_ci            b = b[2:-2]  # Strip off start/end markers
3947db96d56Sopenharmony_ci        else:
3957db96d56Sopenharmony_ci            b = b[:-2]
3967db96d56Sopenharmony_ci    #
3977db96d56Sopenharmony_ci    # We have to go through this stepwise, so as to ignore spaces and handle
3987db96d56Sopenharmony_ci    # special short sequences
3997db96d56Sopenharmony_ci    #
4007db96d56Sopenharmony_ci    packI = struct.Struct('!I').pack
4017db96d56Sopenharmony_ci    decoded = []
4027db96d56Sopenharmony_ci    decoded_append = decoded.append
4037db96d56Sopenharmony_ci    curr = []
4047db96d56Sopenharmony_ci    curr_append = curr.append
4057db96d56Sopenharmony_ci    curr_clear = curr.clear
4067db96d56Sopenharmony_ci    for x in b + b'u' * 4:
4077db96d56Sopenharmony_ci        if b'!'[0] <= x <= b'u'[0]:
4087db96d56Sopenharmony_ci            curr_append(x)
4097db96d56Sopenharmony_ci            if len(curr) == 5:
4107db96d56Sopenharmony_ci                acc = 0
4117db96d56Sopenharmony_ci                for x in curr:
4127db96d56Sopenharmony_ci                    acc = 85 * acc + (x - 33)
4137db96d56Sopenharmony_ci                try:
4147db96d56Sopenharmony_ci                    decoded_append(packI(acc))
4157db96d56Sopenharmony_ci                except struct.error:
4167db96d56Sopenharmony_ci                    raise ValueError('Ascii85 overflow') from None
4177db96d56Sopenharmony_ci                curr_clear()
4187db96d56Sopenharmony_ci        elif x == b'z'[0]:
4197db96d56Sopenharmony_ci            if curr:
4207db96d56Sopenharmony_ci                raise ValueError('z inside Ascii85 5-tuple')
4217db96d56Sopenharmony_ci            decoded_append(b'\0\0\0\0')
4227db96d56Sopenharmony_ci        elif foldspaces and x == b'y'[0]:
4237db96d56Sopenharmony_ci            if curr:
4247db96d56Sopenharmony_ci                raise ValueError('y inside Ascii85 5-tuple')
4257db96d56Sopenharmony_ci            decoded_append(b'\x20\x20\x20\x20')
4267db96d56Sopenharmony_ci        elif x in ignorechars:
4277db96d56Sopenharmony_ci            # Skip whitespace
4287db96d56Sopenharmony_ci            continue
4297db96d56Sopenharmony_ci        else:
4307db96d56Sopenharmony_ci            raise ValueError('Non-Ascii85 digit found: %c' % x)
4317db96d56Sopenharmony_ci
4327db96d56Sopenharmony_ci    result = b''.join(decoded)
4337db96d56Sopenharmony_ci    padding = 4 - len(curr)
4347db96d56Sopenharmony_ci    if padding:
4357db96d56Sopenharmony_ci        # Throw away the extra padding
4367db96d56Sopenharmony_ci        result = result[:-padding]
4377db96d56Sopenharmony_ci    return result
4387db96d56Sopenharmony_ci
4397db96d56Sopenharmony_ci# The following code is originally taken (with permission) from Mercurial
4407db96d56Sopenharmony_ci
4417db96d56Sopenharmony_ci_b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4427db96d56Sopenharmony_ci                b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
4437db96d56Sopenharmony_ci_b85chars = None
4447db96d56Sopenharmony_ci_b85chars2 = None
4457db96d56Sopenharmony_ci_b85dec = None
4467db96d56Sopenharmony_ci
4477db96d56Sopenharmony_cidef b85encode(b, pad=False):
4487db96d56Sopenharmony_ci    """Encode bytes-like object b in base85 format and return a bytes object.
4497db96d56Sopenharmony_ci
4507db96d56Sopenharmony_ci    If pad is true, the input is padded with b'\\0' so its length is a multiple of
4517db96d56Sopenharmony_ci    4 bytes before encoding.
4527db96d56Sopenharmony_ci    """
4537db96d56Sopenharmony_ci    global _b85chars, _b85chars2
4547db96d56Sopenharmony_ci    # Delay the initialization of tables to not waste memory
4557db96d56Sopenharmony_ci    # if the function is never called
4567db96d56Sopenharmony_ci    if _b85chars2 is None:
4577db96d56Sopenharmony_ci        _b85chars = [bytes((i,)) for i in _b85alphabet]
4587db96d56Sopenharmony_ci        _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
4597db96d56Sopenharmony_ci    return _85encode(b, _b85chars, _b85chars2, pad)
4607db96d56Sopenharmony_ci
4617db96d56Sopenharmony_cidef b85decode(b):
4627db96d56Sopenharmony_ci    """Decode the base85-encoded bytes-like object or ASCII string b
4637db96d56Sopenharmony_ci
4647db96d56Sopenharmony_ci    The result is returned as a bytes object.
4657db96d56Sopenharmony_ci    """
4667db96d56Sopenharmony_ci    global _b85dec
4677db96d56Sopenharmony_ci    # Delay the initialization of tables to not waste memory
4687db96d56Sopenharmony_ci    # if the function is never called
4697db96d56Sopenharmony_ci    if _b85dec is None:
4707db96d56Sopenharmony_ci        _b85dec = [None] * 256
4717db96d56Sopenharmony_ci        for i, c in enumerate(_b85alphabet):
4727db96d56Sopenharmony_ci            _b85dec[c] = i
4737db96d56Sopenharmony_ci
4747db96d56Sopenharmony_ci    b = _bytes_from_decode_data(b)
4757db96d56Sopenharmony_ci    padding = (-len(b)) % 5
4767db96d56Sopenharmony_ci    b = b + b'~' * padding
4777db96d56Sopenharmony_ci    out = []
4787db96d56Sopenharmony_ci    packI = struct.Struct('!I').pack
4797db96d56Sopenharmony_ci    for i in range(0, len(b), 5):
4807db96d56Sopenharmony_ci        chunk = b[i:i + 5]
4817db96d56Sopenharmony_ci        acc = 0
4827db96d56Sopenharmony_ci        try:
4837db96d56Sopenharmony_ci            for c in chunk:
4847db96d56Sopenharmony_ci                acc = acc * 85 + _b85dec[c]
4857db96d56Sopenharmony_ci        except TypeError:
4867db96d56Sopenharmony_ci            for j, c in enumerate(chunk):
4877db96d56Sopenharmony_ci                if _b85dec[c] is None:
4887db96d56Sopenharmony_ci                    raise ValueError('bad base85 character at position %d'
4897db96d56Sopenharmony_ci                                    % (i + j)) from None
4907db96d56Sopenharmony_ci            raise
4917db96d56Sopenharmony_ci        try:
4927db96d56Sopenharmony_ci            out.append(packI(acc))
4937db96d56Sopenharmony_ci        except struct.error:
4947db96d56Sopenharmony_ci            raise ValueError('base85 overflow in hunk starting at byte %d'
4957db96d56Sopenharmony_ci                             % i) from None
4967db96d56Sopenharmony_ci
4977db96d56Sopenharmony_ci    result = b''.join(out)
4987db96d56Sopenharmony_ci    if padding:
4997db96d56Sopenharmony_ci        result = result[:-padding]
5007db96d56Sopenharmony_ci    return result
5017db96d56Sopenharmony_ci
5027db96d56Sopenharmony_ci# Legacy interface.  This code could be cleaned up since I don't believe
5037db96d56Sopenharmony_ci# binascii has any line length limitations.  It just doesn't seem worth it
5047db96d56Sopenharmony_ci# though.  The files should be opened in binary mode.
5057db96d56Sopenharmony_ci
5067db96d56Sopenharmony_ciMAXLINESIZE = 76 # Excluding the CRLF
5077db96d56Sopenharmony_ciMAXBINSIZE = (MAXLINESIZE//4)*3
5087db96d56Sopenharmony_ci
5097db96d56Sopenharmony_cidef encode(input, output):
5107db96d56Sopenharmony_ci    """Encode a file; input and output are binary files."""
5117db96d56Sopenharmony_ci    while True:
5127db96d56Sopenharmony_ci        s = input.read(MAXBINSIZE)
5137db96d56Sopenharmony_ci        if not s:
5147db96d56Sopenharmony_ci            break
5157db96d56Sopenharmony_ci        while len(s) < MAXBINSIZE:
5167db96d56Sopenharmony_ci            ns = input.read(MAXBINSIZE-len(s))
5177db96d56Sopenharmony_ci            if not ns:
5187db96d56Sopenharmony_ci                break
5197db96d56Sopenharmony_ci            s += ns
5207db96d56Sopenharmony_ci        line = binascii.b2a_base64(s)
5217db96d56Sopenharmony_ci        output.write(line)
5227db96d56Sopenharmony_ci
5237db96d56Sopenharmony_ci
5247db96d56Sopenharmony_cidef decode(input, output):
5257db96d56Sopenharmony_ci    """Decode a file; input and output are binary files."""
5267db96d56Sopenharmony_ci    while True:
5277db96d56Sopenharmony_ci        line = input.readline()
5287db96d56Sopenharmony_ci        if not line:
5297db96d56Sopenharmony_ci            break
5307db96d56Sopenharmony_ci        s = binascii.a2b_base64(line)
5317db96d56Sopenharmony_ci        output.write(s)
5327db96d56Sopenharmony_ci
5337db96d56Sopenharmony_cidef _input_type_check(s):
5347db96d56Sopenharmony_ci    try:
5357db96d56Sopenharmony_ci        m = memoryview(s)
5367db96d56Sopenharmony_ci    except TypeError as err:
5377db96d56Sopenharmony_ci        msg = "expected bytes-like object, not %s" % s.__class__.__name__
5387db96d56Sopenharmony_ci        raise TypeError(msg) from err
5397db96d56Sopenharmony_ci    if m.format not in ('c', 'b', 'B'):
5407db96d56Sopenharmony_ci        msg = ("expected single byte elements, not %r from %s" %
5417db96d56Sopenharmony_ci                                          (m.format, s.__class__.__name__))
5427db96d56Sopenharmony_ci        raise TypeError(msg)
5437db96d56Sopenharmony_ci    if m.ndim != 1:
5447db96d56Sopenharmony_ci        msg = ("expected 1-D data, not %d-D data from %s" %
5457db96d56Sopenharmony_ci                                          (m.ndim, s.__class__.__name__))
5467db96d56Sopenharmony_ci        raise TypeError(msg)
5477db96d56Sopenharmony_ci
5487db96d56Sopenharmony_ci
5497db96d56Sopenharmony_cidef encodebytes(s):
5507db96d56Sopenharmony_ci    """Encode a bytestring into a bytes object containing multiple lines
5517db96d56Sopenharmony_ci    of base-64 data."""
5527db96d56Sopenharmony_ci    _input_type_check(s)
5537db96d56Sopenharmony_ci    pieces = []
5547db96d56Sopenharmony_ci    for i in range(0, len(s), MAXBINSIZE):
5557db96d56Sopenharmony_ci        chunk = s[i : i + MAXBINSIZE]
5567db96d56Sopenharmony_ci        pieces.append(binascii.b2a_base64(chunk))
5577db96d56Sopenharmony_ci    return b"".join(pieces)
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci
5607db96d56Sopenharmony_cidef decodebytes(s):
5617db96d56Sopenharmony_ci    """Decode a bytestring of base-64 data into a bytes object."""
5627db96d56Sopenharmony_ci    _input_type_check(s)
5637db96d56Sopenharmony_ci    return binascii.a2b_base64(s)
5647db96d56Sopenharmony_ci
5657db96d56Sopenharmony_ci
5667db96d56Sopenharmony_ci# Usable as a script...
5677db96d56Sopenharmony_cidef main():
5687db96d56Sopenharmony_ci    """Small main program"""
5697db96d56Sopenharmony_ci    import sys, getopt
5707db96d56Sopenharmony_ci    usage = """usage: %s [-h|-d|-e|-u|-t] [file|-]
5717db96d56Sopenharmony_ci        -h: print this help message and exit
5727db96d56Sopenharmony_ci        -d, -u: decode
5737db96d56Sopenharmony_ci        -e: encode (default)
5747db96d56Sopenharmony_ci        -t: encode and decode string 'Aladdin:open sesame'"""%sys.argv[0]
5757db96d56Sopenharmony_ci    try:
5767db96d56Sopenharmony_ci        opts, args = getopt.getopt(sys.argv[1:], 'hdeut')
5777db96d56Sopenharmony_ci    except getopt.error as msg:
5787db96d56Sopenharmony_ci        sys.stdout = sys.stderr
5797db96d56Sopenharmony_ci        print(msg)
5807db96d56Sopenharmony_ci        print(usage)
5817db96d56Sopenharmony_ci        sys.exit(2)
5827db96d56Sopenharmony_ci    func = encode
5837db96d56Sopenharmony_ci    for o, a in opts:
5847db96d56Sopenharmony_ci        if o == '-e': func = encode
5857db96d56Sopenharmony_ci        if o == '-d': func = decode
5867db96d56Sopenharmony_ci        if o == '-u': func = decode
5877db96d56Sopenharmony_ci        if o == '-t': test(); return
5887db96d56Sopenharmony_ci        if o == '-h': print(usage); return
5897db96d56Sopenharmony_ci    if args and args[0] != '-':
5907db96d56Sopenharmony_ci        with open(args[0], 'rb') as f:
5917db96d56Sopenharmony_ci            func(f, sys.stdout.buffer)
5927db96d56Sopenharmony_ci    else:
5937db96d56Sopenharmony_ci        func(sys.stdin.buffer, sys.stdout.buffer)
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci
5967db96d56Sopenharmony_cidef test():
5977db96d56Sopenharmony_ci    s0 = b"Aladdin:open sesame"
5987db96d56Sopenharmony_ci    print(repr(s0))
5997db96d56Sopenharmony_ci    s1 = encodebytes(s0)
6007db96d56Sopenharmony_ci    print(repr(s1))
6017db96d56Sopenharmony_ci    s2 = decodebytes(s1)
6027db96d56Sopenharmony_ci    print(repr(s2))
6037db96d56Sopenharmony_ci    assert s0 == s2
6047db96d56Sopenharmony_ci
6057db96d56Sopenharmony_ci
6067db96d56Sopenharmony_ciif __name__ == '__main__':
6077db96d56Sopenharmony_ci    main()
608