diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst
index 8a241e51ebfee6..01ca99a0c3bb14 100644
--- a/Doc/library/binascii.rst
+++ b/Doc/library/binascii.rst
@@ -48,12 +48,15 @@ The :mod:`!binascii` module defines the following functions:
Added the *backtick* parameter.
-.. function:: a2b_base64(string, /, *, strict_mode=False)
- a2b_base64(string, /, *, strict_mode=True, ignorechars)
+.. function:: a2b_base64(string, /, *, alphabet=BASE64_ALPHABET, strict_mode=False)
+ a2b_base64(string, /, *, ignorechars, alphabet=BASE64_ALPHABET, strict_mode=True)
Convert a block of base64 data back to binary and return the binary data. More
than one line may be passed at a time.
+ Optional *alphabet* must be a :class:`bytes` object of length 64 which
+ specifies an alternative alphabet.
+
If *ignorechars* is specified, it should be a :term:`bytes-like object`
containing characters to ignore from the input when *strict_mode* is true.
If *ignorechars* contains the pad character ``'='``, the pad characters
@@ -76,10 +79,10 @@ The :mod:`!binascii` module defines the following functions:
Added the *strict_mode* parameter.
.. versionchanged:: 3.15
- Added the *ignorechars* parameter.
+ Added the *alphabet* and *ignorechars* parameters.
-.. function:: b2a_base64(data, *, wrapcol=0, newline=True)
+.. function:: b2a_base64(data, *, alphabet=BASE64_ALPHABET, wrapcol=0, newline=True)
Convert binary data to a line(s) of ASCII characters in base64 coding,
as specified in :rfc:`4648`.
@@ -95,7 +98,7 @@ The :mod:`!binascii` module defines the following functions:
Added the *newline* parameter.
.. versionchanged:: 3.15
- Added the *wrapcol* parameter.
+ Added the *alphabet* and *wrapcol* parameters.
.. function:: a2b_ascii85(string, /, *, foldspaces=False, adobe=False, ignorechars=b"")
@@ -148,7 +151,7 @@ The :mod:`!binascii` module defines the following functions:
.. versionadded:: 3.15
-.. function:: a2b_base85(string, /)
+.. function:: a2b_base85(string, /, *, alphabet=BASE85_ALPHABET)
Convert Base85 data back to binary and return the binary data.
More than one line may be passed at a time.
@@ -158,49 +161,25 @@ The :mod:`!binascii` module defines the following functions:
characters). Each group encodes 32 bits of binary data in the range from
``0`` to ``2 ** 32 - 1``, inclusive.
+ Optional *alphabet* must be a :class:`bytes` object of length 85 which
+ specifies an alternative alphabet.
+
Invalid Base85 data will raise :exc:`binascii.Error`.
.. versionadded:: 3.15
-.. function:: b2a_base85(data, /, *, pad=False)
+.. function:: b2a_base85(data, /, *, alphabet=BASE85_ALPHABET, pad=False)
Convert binary data to a line of ASCII characters in Base85 coding.
The return value is the converted line.
- If *pad* is true, the input is padded with ``b'\0'`` so its length is a
- multiple of 4 bytes before encoding.
-
- .. versionadded:: 3.15
-
-
-.. function:: a2b_z85(string, /)
-
- Convert Z85 data back to binary and return the binary data.
- More than one line may be passed at a time.
-
- Valid Z85 data contains characters from the Z85 alphabet in groups
- of five (except for the final group, which may have from two to five
- characters). Each group encodes 32 bits of binary data in the range from
- ``0`` to ``2 ** 32 - 1``, inclusive.
-
- See `Z85 specification `_ for more information.
-
- Invalid Z85 data will raise :exc:`binascii.Error`.
-
- .. versionadded:: 3.15
-
-
-.. function:: b2a_z85(data, /, *, pad=False)
-
- Convert binary data to a line of ASCII characters in Z85 coding.
- The return value is the converted line.
+ Optional *alphabet* must be a :term:`bytes-like object` of length 85 which
+ specifies an alternative alphabet.
If *pad* is true, the input is padded with ``b'\0'`` so its length is a
multiple of 4 bytes before encoding.
- See `Z85 specification `_ for more information.
-
.. versionadded:: 3.15
@@ -300,6 +279,67 @@ The :mod:`!binascii` module defines the following functions:
but may be handled by reading a little more data and trying again.
+.. data:: BASE64_ALPHABET
+
+ The Base 64 alphabet according to :rfc:`4648`.
+
+ .. versionadded:: next
+
+.. data:: URLSAFE_BASE64_ALPHABET
+
+ The "URL and filename safe" Base 64 alphabet according to :rfc:`4648`.
+
+ .. versionadded:: next
+
+.. data:: CRYPT_ALPHABET
+
+ The Base 64 alphabet used in the :manpage:`crypt(3)` routine and in the GEDCOM format.
+
+ .. versionadded:: next
+
+.. data:: BCRYPT_ALPHABET
+
+ The Base 64 alphabet used in the ``bcrypt`` hashing function.
+
+ .. versionadded:: next
+
+.. data:: UU_ALPHABET
+
+ The uuencoding alphabet.
+
+ .. versionadded:: next
+
+.. data:: XX_ALPHABET
+
+ The xxencoding alphabet.
+
+ .. versionadded:: next
+
+.. data:: BINHEX_ALPHABET
+
+ The Base 64 alphabet used in BinHex 4 (HQX) within the classic Mac OS.
+
+ .. versionadded:: next
+
+.. data:: BASE85_ALPHABET
+
+ The Base85 alphabet.
+
+ .. versionadded:: next
+
+.. data:: ASCII85_ALPHABET
+
+ The Ascii85 alphabet.
+
+ .. versionadded:: next
+
+.. data:: Z85_ALPHABET
+
+ The `Z85 `_ alphabet.
+
+ .. versionadded:: next
+
+
.. seealso::
Module :mod:`base64`
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 459846e55ccf70..7c71c3e48efb38 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -644,13 +644,16 @@ binascii
- :func:`~binascii.b2a_ascii85` and :func:`~binascii.a2b_ascii85`
- :func:`~binascii.b2a_base85` and :func:`~binascii.a2b_base85`
- - :func:`~binascii.b2a_z85` and :func:`~binascii.a2b_z85`
(Contributed by James Seo and Serhiy Storchaka in :gh:`101178`.)
* Added the *wrapcol* parameter in :func:`~binascii.b2a_base64`.
(Contributed by Serhiy Storchaka in :gh:`143214`.)
+* Added the *alphabet* parameter in :func:`~binascii.b2a_base64` and
+ :func:`~binascii.a2b_base64`.
+ (Contributed by Serhiy Storchaka in :gh:`145980`.)
+
* Added the *ignorechars* parameter in :func:`~binascii.a2b_base64`.
(Contributed by Serhiy Storchaka in :gh:`144001`.)
diff --git a/Lib/base64.py b/Lib/base64.py
index 36688ce43917ce..876525b7992596 100644
--- a/Lib/base64.py
+++ b/Lib/base64.py
@@ -56,11 +56,13 @@ def b64encode(s, altchars=None, *, wrapcol=0):
If wrapcol is non-zero, insert a newline (b'\\n') character after at most
every wrapcol characters.
"""
- encoded = binascii.b2a_base64(s, wrapcol=wrapcol, newline=False)
if altchars is not None:
- assert len(altchars) == 2, repr(altchars)
- return encoded.translate(bytes.maketrans(b'+/', altchars))
- return encoded
+ if len(altchars) != 2:
+ raise ValueError(f'invalid altchars: {altchars!r}')
+ alphabet = binascii.BASE64_ALPHABET[:-2] + altchars
+ return binascii.b2a_base64(s, wrapcol=wrapcol, newline=False,
+ alphabet=alphabet)
+ return binascii.b2a_base64(s, wrapcol=wrapcol, newline=False)
def b64decode(s, altchars=None, validate=_NOT_SPECIFIED, *, ignorechars=_NOT_SPECIFIED):
@@ -100,9 +102,11 @@ def b64decode(s, altchars=None, validate=_NOT_SPECIFIED, *, ignorechars=_NOT_SPE
break
s = s.translate(bytes.maketrans(altchars, b'+/'))
else:
- trans = bytes.maketrans(b'+/' + altchars, altchars + b'+/')
- s = s.translate(trans)
- ignorechars = ignorechars.translate(trans)
+ alphabet = binascii.BASE64_ALPHABET[:-2] + altchars
+ return binascii.a2b_base64(s, strict_mode=validate,
+ alphabet=alphabet,
+ ignorechars=ignorechars)
+
if ignorechars is _NOT_SPECIFIED:
ignorechars = b''
result = binascii.a2b_base64(s, strict_mode=validate,
@@ -140,7 +144,6 @@ def standard_b64decode(s):
return b64decode(s)
-_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_')
_urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/')
def urlsafe_b64encode(s):
@@ -150,7 +153,8 @@ def urlsafe_b64encode(s):
bytes object. The alphabet uses '-' instead of '+' and '_' instead of
'/'.
"""
- return b64encode(s).translate(_urlsafe_encode_translation)
+ return binascii.b2a_base64(s, newline=False,
+ alphabet=binascii.URLSAFE_BASE64_ALPHABET)
def urlsafe_b64decode(s):
"""Decode bytes using the URL- and filesystem-safe Base64 alphabet.
@@ -393,14 +397,14 @@ def b85decode(b):
def z85encode(s, pad=False):
"""Encode bytes-like object b in z85 format and return a bytes object."""
- return binascii.b2a_z85(s, pad=pad)
+ return binascii.b2a_base85(s, pad=pad, alphabet=binascii.Z85_ALPHABET)
def z85decode(s):
"""Decode the z85-encoded bytes-like object or ASCII string b
The result is returned as a bytes object.
"""
- return binascii.a2b_z85(s)
+ return binascii.a2b_base85(s, alphabet=binascii.Z85_ALPHABET)
# Legacy interface. This code could be cleaned up since I don't believe
# binascii has any line length limitations. It just doesn't seem worth it
diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py
index 9f0d15fe538810..779ebcc165a88c 100644
--- a/Lib/test/test_binascii.py
+++ b/Lib/test/test_binascii.py
@@ -10,10 +10,10 @@
# Note: "*_hex" functions are aliases for "(un)hexlify"
-b2a_functions = ['b2a_ascii85', 'b2a_base64', 'b2a_base85', 'b2a_z85',
+b2a_functions = ['b2a_ascii85', 'b2a_base64', 'b2a_base85',
'b2a_hex', 'b2a_qp', 'b2a_uu',
'hexlify']
-a2b_functions = ['a2b_ascii85', 'a2b_base64', 'a2b_base85', 'a2b_z85',
+a2b_functions = ['a2b_ascii85', 'a2b_base64', 'a2b_base85',
'a2b_hex', 'a2b_qp', 'a2b_uu',
'unhexlify']
all_functions = a2b_functions + b2a_functions + ['crc32', 'crc_hqx']
@@ -46,6 +46,51 @@ def test_exceptions(self):
self.assertIsSubclass(binascii.Error, Exception)
self.assertIsSubclass(binascii.Incomplete, Exception)
+ def test_constants(self):
+ self.assertEqual(binascii.BASE64_ALPHABET,
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ b'abcdefghijklmnopqrstuvwxyz'
+ b'0123456789+/')
+ self.assertEqual(binascii.URLSAFE_BASE64_ALPHABET,
+ binascii.BASE64_ALPHABET[:-2] + b'-_')
+ self.assertEqual(binascii.CRYPT_ALPHABET,
+ b'./0123456789'
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ b'abcdefghijklmnopqrstuvwxyz')
+ self.assertEqual(binascii.BCRYPT_ALPHABET,
+ b'./'
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ b'abcdefghijklmnopqrstuvwxyz'
+ b'0123456789')
+ self.assertEqual(binascii.UU_ALPHABET, bytes(range(32, 32+64)))
+ self.assertEqual(binascii.XX_ALPHABET,
+ b'+-0123456789'
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ b'abcdefghijklmnopqrstuvwxyz')
+ self.assertEqual(binascii.BINHEX_ALPHABET,
+ b'!"#$%&\'()*+,-012345689'
+ b'@ABCDEFGHIJKLMNPQRSTUVXYZ['
+ b'`abcdefhijklmpqr')
+
+ self.assertEqual(binascii.BASE85_ALPHABET,
+ b'0123456789'
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ b'abcdefghijklmnopqrstuvwxyz'
+ b'!#$%&()*+-;<=>?@^_`{|}~')
+ self.assertEqual(binascii.ASCII85_ALPHABET, bytes(range(33, 33+85)))
+ self.assertEqual(binascii.Z85_ALPHABET,
+ b'0123456789'
+ b'abcdefghijklmnopqrstuvwxyz'
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ b'.-:+=^!/*?&<>()[]{}@%$#')
+
+ for name in ('BASE85_ALPHABET', 'ASCII85_ALPHABET',
+ 'Z85_ALPHABET'):
+ value = getattr(binascii, name)
+ self.assertIsInstance(value, bytes)
+ self.assertEqual(len(value), 85)
+ self.assertEqual(len(set(value)), 85)
+
def test_functions(self):
# Check presence of all functions
for name in all_functions:
@@ -302,6 +347,33 @@ def assertInvalidLength(data, strict_mode=True):
assertInvalidLength(b'A\tB\nC ??DE', # only 5 valid characters
strict_mode=False)
+ def test_base64_alphabet(self):
+ alphabet = (b'!"#$%&\'()*+,-012345689@'
+ b'ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr')
+ data = self.type2test(self.rawdata)
+ encoded = binascii.b2a_base64(data, alphabet=alphabet)
+ trans = bytes.maketrans(binascii.BASE64_ALPHABET, alphabet)
+ expected = binascii.b2a_base64(data).translate(trans)
+ self.assertEqual(encoded, expected)
+ self.assertEqual(binascii.a2b_base64(encoded, alphabet=alphabet), self.rawdata)
+ self.assertEqual(binascii.b2a_base64(data, alphabet=self.type2test(alphabet)), expected)
+
+ data = self.type2test(b'')
+ self.assertEqual(binascii.b2a_base64(data, alphabet=alphabet), b'\n')
+ self.assertEqual(binascii.a2b_base64(data, alphabet=alphabet), b'')
+
+ for func in binascii.b2a_base64, binascii.a2b_base64:
+ with self.assertRaises(TypeError):
+ func(data, alphabet=None)
+ with self.assertRaises(TypeError):
+ func(data, alphabet=alphabet.decode())
+ with self.assertRaises(ValueError):
+ func(data, alphabet=alphabet[:-1])
+ with self.assertRaises(ValueError):
+ func(data, alphabet=alphabet+b'?')
+ with self.assertRaises(TypeError):
+ binascii.a2b_base64(data, alphabet=bytearray(alphabet))
+
def test_ascii85_valid(self):
# Test Ascii85 with valid data
ASCII85_PREFIX = b"<~"
@@ -587,73 +659,32 @@ def test_base85_pad(self):
b_pad_expected = b + b"\0" * padding
self.assertEqual(b_pad, b_pad_expected)
- def test_z85_valid(self):
- # Test Z85 with valid data
- lines, i = [], 0
- for k in range(1, len(self.rawdata) + 1):
- b = self.type2test(self.rawdata[i:i + k])
- a = binascii.b2a_z85(b)
- lines.append(a)
- i += k
- if i >= len(self.rawdata):
- break
- res = bytes()
- for line in lines:
- a = self.type2test(line)
- b = binascii.a2b_z85(a)
- res += b
- self.assertEqual(res, self.rawdata)
-
- # Test decoding inputs with different length
- self.assertEqual(binascii.a2b_z85(self.type2test(b'')), b'')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'a')), b'')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'ab')), b'\x1f')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'abc')),
- b'\x1f\x85')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'abcd')),
- b'\x1f\x85\x9a')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'abcde')),
- b'\x1f\x85\x9a$')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'abcdef')),
- b'\x1f\x85\x9a$')
- self.assertEqual(binascii.a2b_z85(self.type2test(b'abcdefg')),
- b'\x1f\x85\x9a$/')
-
- def test_z85_errors(self):
- def _assertRegexTemplate(assert_regex, data, **kwargs):
- with self.assertRaisesRegex(binascii.Error, assert_regex):
- binascii.a2b_z85(self.type2test(data), **kwargs)
-
- def assertNonZ85Data(data):
- _assertRegexTemplate(r"(?i)bad z85 character", data)
-
- def assertOverflow(data):
- _assertRegexTemplate(r"(?i)z85 overflow", data)
-
- assertNonZ85Data(b"\xda")
- assertNonZ85Data(b"00\0\0")
- assertNonZ85Data(b"z !/")
- assertNonZ85Data(b"By/JnB0hYQ\n")
-
- # Test Z85 with out-of-range encoded value
- assertOverflow(b"%")
- assertOverflow(b"%n")
- assertOverflow(b"%nS")
- assertOverflow(b"%nSc")
- assertOverflow(b"%nSc1")
- assertOverflow(b"%nSc0$")
- assertOverflow(b"%nSc0%nSc0%nSD0")
-
- def test_z85_pad(self):
- # Test Z85 with encode padding
- rawdata = b"n1n3Tee\n ch@rAc\te\r$"
- for i in range(1, len(rawdata) + 1):
- padding = -i % 4
- b = rawdata[:i]
- a_pad = binascii.b2a_z85(self.type2test(b), pad=True)
- b_pad = binascii.a2b_z85(self.type2test(a_pad))
- b_pad_expected = b + b"\0" * padding
- self.assertEqual(b_pad, b_pad_expected)
+ def test_base85_alphabet(self):
+ alphabet = (b'0123456789abcdefghijklmnopqrstuvwxyz'
+ b'ABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#')
+ data = self.type2test(self.rawdata)
+ encoded = binascii.b2a_base85(data, alphabet=alphabet)
+ trans = bytes.maketrans(binascii.BASE85_ALPHABET, alphabet)
+ expected = binascii.b2a_base85(data).translate(trans)
+ self.assertEqual(encoded, expected)
+ self.assertEqual(binascii.a2b_base85(encoded, alphabet=alphabet), self.rawdata)
+ self.assertEqual(binascii.b2a_base85(data, alphabet=self.type2test(alphabet)), expected)
+
+ data = self.type2test(b'')
+ self.assertEqual(binascii.b2a_base85(data, alphabet=alphabet), b'')
+ self.assertEqual(binascii.a2b_base85(data, alphabet=alphabet), b'')
+
+ for func in binascii.b2a_base85, binascii.a2b_base85:
+ with self.assertRaises(TypeError):
+ func(data, alphabet=None)
+ with self.assertRaises(TypeError):
+ func(data, alphabet=alphabet.decode())
+ with self.assertRaises(ValueError):
+ func(data, alphabet=alphabet[:-1])
+ with self.assertRaises(ValueError):
+ func(data, alphabet=alphabet+b'?')
+ with self.assertRaises(TypeError):
+ binascii.a2b_base64(data, alphabet=bytearray(alphabet))
def test_uu(self):
MAX_UU = 45
diff --git a/Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst b/Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst
new file mode 100644
index 00000000000000..217d767138ddb8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst
@@ -0,0 +1,5 @@
+Added the *alphabet* parameter in :func:`~binascii.b2a_base64`,
+:func:`~binascii.a2b_base64`, :func:`~binascii.b2a_base85` and
+:func:`~binascii.a2b_base85` and a number of ``*_ALPHABET`` constants in the
+:mod:`binascii` module. Removed :func:`~binascii.b2a_z85` and
+:func:`~binascii.a2b_z85`.
diff --git a/Modules/binascii.c b/Modules/binascii.c
index c076b12fb149b2..f35d22e557372d 100644
--- a/Modules/binascii.c
+++ b/Modules/binascii.c
@@ -67,6 +67,7 @@
typedef struct binascii_state {
PyObject *Error;
PyObject *Incomplete;
+ PyObject *reverse_table_cache;
} binascii_state;
static inline binascii_state *
@@ -228,26 +229,6 @@ static const unsigned char table_a2b_base85_a85[] Py_ALIGNED(64) = {
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
};
-static const unsigned char table_a2b_base85_z85[] Py_ALIGNED(64) = {
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,68,-1,84, 83,82,72,-1, 75,76,70,65, -1,63,62,69,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,64,-1, 73,66,74,71,
- 81,36,37,38, 39,40,41,42, 43,44,45,46, 47,48,49,50,
- 51,52,53,54, 55,56,57,58, 59,60,61,77, -1,78,67,-1,
- -1,10,11,12, 13,14,15,16, 17,18,19,20, 21,22,23,24,
- 25,26,27,28, 29,30,31,32, 33,34,35,79, -1,80,-1,-1,
-
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-};
-
static const unsigned char table_b2a_base85[] Py_ALIGNED(64) =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
@@ -256,9 +237,6 @@ static const unsigned char table_b2a_base85_a85[] Py_ALIGNED(64) =
"!\"#$%&\'()*+,-./0123456789:;<=>?@" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu";
-static const unsigned char table_b2a_base85_z85[] Py_ALIGNED(64) =
- "0123456789abcdefghijklmnopqrstuvwxyz" \
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/\x2a?&<>()[]{}@%$#"; /* clinic doesn't like '/' followed by '*' */
#define BASE85_A85_PREFIX '<'
#define BASE85_A85_AFFIX '~'
@@ -547,6 +525,52 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick)
return PyBytesWriter_FinishWithPointer(writer, ascii_data);
}
+static PyObject *
+get_reverse_table(binascii_state *state, PyObject *alphabet, int size, int padchar)
+{
+ PyObject *reverse_table;
+ if (state == NULL) {
+ return NULL;
+ }
+ if (PyBytes_GET_SIZE(alphabet) != size) {
+ PyErr_Format(PyExc_ValueError, "alphabet must have length %d", size);
+ return NULL;
+ }
+ if (PyDict_GetItemRef(state->reverse_table_cache, alphabet, &reverse_table) < 0) {
+ return NULL;
+ }
+ if (reverse_table == NULL) {
+ unsigned char out[256];
+ memset(out, (unsigned char)-1, 256);
+ const unsigned char *in = (const unsigned char *)PyBytes_AS_STRING(alphabet);
+ for (int i = 0; i < size; i++) {
+ out[in[i]] = i;
+ }
+ if (padchar >= 0) {
+ assert(padchar < 256);
+ out[padchar] = size;
+ }
+ reverse_table = PyBytes_FromStringAndSize((char *)out, 256);
+ if (reverse_table == NULL) {
+ return NULL;
+ }
+ if (PyDict_SetItem(state->reverse_table_cache, alphabet, reverse_table) < 0) {
+ Py_DECREF(reverse_table);
+ return NULL;
+ }
+ }
+ else {
+ if (!PyBytes_Check(reverse_table)
+ || PyBytes_GET_SIZE(reverse_table) != 256)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Broken binascii cache");
+ Py_DECREF(reverse_table);
+ return NULL;
+ }
+ }
+ return reverse_table;
+}
+
typedef unsigned char ignorecache_t[32];
static int
@@ -576,6 +600,7 @@ binascii.a2b_base64
When set to true, bytes that are not part of the base64 standard are
not allowed. The same applies to excess data after padding (= / ==).
Set to True by default if ignorechars is specified, False otherwise.
+ alphabet: PyBytesObject(py_default="BASE64_ALPHABET") = NULL
ignorechars: Py_buffer(py_default="") = None
A byte string containing characters to ignore from the input when
strict_mode is true.
@@ -585,14 +610,16 @@ Decode a line of base64 data.
static PyObject *
binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
- Py_buffer *ignorechars)
-/*[clinic end generated code: output=eab37aea4cfa6daa input=3be4937d72943835]*/
+ PyBytesObject *alphabet, Py_buffer *ignorechars)
+/*[clinic end generated code: output=72f15fcc0681d666 input=051bbf7cf17b6ba4]*/
{
assert(data->len >= 0);
const unsigned char *ascii_data = data->buf;
size_t ascii_len = data->len;
binascii_state *state = NULL;
+ PyObject *table_obj = NULL;
+ const unsigned char *table_a2b = table_a2b_base64;
if (strict_mode == -1) {
strict_mode = (ignorechars->buf != NULL);
@@ -605,10 +632,20 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
memset(ignorecache, 0, sizeof(ignorecache));
}
+ if (alphabet != NULL) {
+ state = get_binascii_state(module);
+ table_obj = get_reverse_table(state, (PyObject *)alphabet, 64, BASE64_PAD);
+ if (table_obj == NULL) {
+ return NULL;
+ }
+ table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj);
+ }
+
/* Allocate the buffer */
Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */
PyBytesWriter *writer = PyBytesWriter_Create(bin_len);
if (writer == NULL) {
+ Py_XDECREF(table_obj);
return NULL;
}
unsigned char *bin_data = PyBytesWriter_GetData(writer);
@@ -620,7 +657,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
*/
if (ascii_len >= 4) {
Py_ssize_t fast_chars = base64_decode_fast(ascii_data, (Py_ssize_t)ascii_len,
- bin_data, table_a2b_base64);
+ bin_data, table_a2b);
if (fast_chars > 0) {
ascii_data += fast_chars;
ascii_len -= fast_chars;
@@ -672,7 +709,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
}
}
- unsigned char v = table_a2b_base64[this_ch];
+ unsigned char v = table_a2b[this_ch];
if (v >= 64) {
if (strict_mode && !ignorechar(this_ch, ignorechars, ignorecache)) {
state = get_binascii_state(module);
@@ -749,9 +786,11 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
}
done:
+ Py_XDECREF(table_obj);
return PyBytesWriter_FinishWithPointer(writer, bin_data);
error_end:
+ Py_XDECREF(table_obj);
PyBytesWriter_Discard(writer);
return NULL;
}
@@ -765,19 +804,28 @@ binascii.b2a_base64
*
wrapcol: size_t = 0
newline: bool = True
+ alphabet: Py_buffer(py_default="BASE64_ALPHABET") = None
Base64-code line of data.
[clinic start generated code]*/
static PyObject *
binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, size_t wrapcol,
- int newline)
-/*[clinic end generated code: output=2edc7311a9515eac input=2ee4214e6d489e2e]*/
+ int newline, Py_buffer *alphabet)
+/*[clinic end generated code: output=9d9657e5fbe28c64 input=681cc9d3af74fc39]*/
{
+ const unsigned char *table_b2a = table_b2a_base64;
const unsigned char *bin_data = data->buf;
Py_ssize_t bin_len = data->len;
assert(bin_len >= 0);
+ if (alphabet->buf != NULL) {
+ if (alphabet->len != 64) {
+ PyErr_SetString(PyExc_ValueError, "alphabet must have length 64");
+ return NULL;
+ }
+ table_b2a = alphabet->buf;
+ }
/* Each group of 3 bytes (rounded up) gets encoded as 4 characters,
* not counting newlines.
* Note that 'b' gets encoded as 'Yg==' (1 in, 4 out).
@@ -809,7 +857,7 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, size_t wrapcol,
/* Use the optimized fast path for complete 3-byte groups */
Py_ssize_t fast_bytes = base64_encode_fast(bin_data, bin_len, ascii_data,
- table_b2a_base64);
+ table_b2a);
bin_data += fast_bytes;
ascii_data += (fast_bytes / 3) * 4;
bin_len -= fast_bytes;
@@ -818,17 +866,17 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, size_t wrapcol,
if (bin_len == 1) {
/* 1 byte remaining: produces 2 base64 chars + 2 padding */
unsigned int val = bin_data[0];
- *ascii_data++ = table_b2a_base64[(val >> 2) & 0x3f];
- *ascii_data++ = table_b2a_base64[(val << 4) & 0x3f];
+ *ascii_data++ = table_b2a[(val >> 2) & 0x3f];
+ *ascii_data++ = table_b2a[(val << 4) & 0x3f];
*ascii_data++ = BASE64_PAD;
*ascii_data++ = BASE64_PAD;
}
else if (bin_len == 2) {
/* 2 bytes remaining: produces 3 base64 chars + 1 padding */
unsigned int val = ((unsigned int)bin_data[0] << 8) | bin_data[1];
- *ascii_data++ = table_b2a_base64[(val >> 10) & 0x3f];
- *ascii_data++ = table_b2a_base64[(val >> 4) & 0x3f];
- *ascii_data++ = table_b2a_base64[(val << 2) & 0x3f];
+ *ascii_data++ = table_b2a[(val >> 10) & 0x3f];
+ *ascii_data++ = table_b2a[(val >> 4) & 0x3f];
+ *ascii_data++ = table_b2a[(val << 2) & 0x3f];
*ascii_data++ = BASE64_PAD;
}
@@ -1125,13 +1173,36 @@ binascii_b2a_ascii85_impl(PyObject *module, Py_buffer *data, int foldspaces,
return PyBytesWriter_FinishWithPointer(writer, ascii_data);
}
+/*[clinic input]
+binascii.a2b_base85
+
+ data: ascii_buffer
+ /
+ *
+ alphabet: PyBytesObject(py_default="BASE85_ALPHABET") = NULL
+
+Decode a line of Base85 data.
+[clinic start generated code]*/
+
static PyObject *
-base85_decode_impl(PyObject *module, Py_buffer *data,
- const unsigned char table_a2b[], const char *name)
+binascii_a2b_base85_impl(PyObject *module, Py_buffer *data,
+ PyBytesObject *alphabet)
+/*[clinic end generated code: output=3e114af53812e8ff input=81779cd049d44a55]*/
{
const unsigned char *ascii_data = data->buf;
Py_ssize_t ascii_len = data->len;
binascii_state *state = NULL;
+ PyObject *table_obj = NULL;
+ const unsigned char *table_a2b = table_a2b_base85;
+
+ if (alphabet != NULL) {
+ state = get_binascii_state(module);
+ table_obj = get_reverse_table(state, (PyObject *)alphabet, 85, -1);
+ if (table_obj == NULL) {
+ return NULL;
+ }
+ table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj);
+ }
assert(ascii_len >= 0);
@@ -1139,6 +1210,7 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
size_t bin_len = ((size_t)ascii_len + 4) / 5 * 4;
PyBytesWriter *writer = PyBytesWriter_Create(bin_len);
if (writer == NULL) {
+ Py_XDECREF(table_obj);
return NULL;
}
unsigned char *bin_data = PyBytesWriter_GetData(writer);
@@ -1164,8 +1236,8 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
state = get_binascii_state(module);
if (state != NULL) {
PyErr_Format(state->Error,
- "%s overflow in hunk starting at byte %d",
- name, (data->len - ascii_len) / 5 * 5);
+ "Base85 overflow in hunk starting at byte %d",
+ (data->len - ascii_len) / 5 * 5);
}
goto error;
}
@@ -1175,8 +1247,8 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
else {
state = get_binascii_state(module);
if (state != NULL) {
- PyErr_Format(state->Error, "bad %s character at position %d",
- name, data->len - ascii_len);
+ PyErr_Format(state->Error, "bad Base85 character at position %d",
+ data->len - ascii_len);
}
goto error;
}
@@ -1196,19 +1268,44 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
leftchar = 0;
}
+ Py_XDECREF(table_obj);
return PyBytesWriter_FinishWithPointer(writer, bin_data);
error:
PyBytesWriter_Discard(writer);
+ Py_XDECREF(table_obj);
return NULL;
}
+/*[clinic input]
+binascii.b2a_base85
+
+ data: Py_buffer
+ /
+ *
+ pad: bool = False
+ Pad input to a multiple of 4 before encoding.
+ alphabet: Py_buffer(py_default="BASE85_ALPHABET") = None
+
+Base85-code line of data.
+[clinic start generated code]*/
+
static PyObject *
-base85_encode_impl(PyObject *module, Py_buffer *data, int pad,
- const unsigned char table_b2a[], const char *name)
+binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad,
+ Py_buffer *alphabet)
+/*[clinic end generated code: output=a59f4f2ff6f0e69f input=cde4ebe8abfaa982]*/
{
const unsigned char *bin_data = data->buf;
Py_ssize_t bin_len = data->len;
+ const unsigned char *table_b2a = table_b2a_base85;
+
+ if (alphabet->buf != NULL) {
+ if (alphabet->len != 85) {
+ PyErr_SetString(PyExc_ValueError, "alphabet must have length 85");
+ return NULL;
+ }
+ table_b2a = alphabet->buf;
+ }
assert(bin_len >= 0);
@@ -1222,7 +1319,7 @@ base85_encode_impl(PyObject *module, Py_buffer *data, int pad,
if (state == NULL) {
return NULL;
}
- PyErr_Format(state->Error, "Too much data for %s", name);
+ PyErr_SetString(state->Error, "Too much data for Base85");
return NULL;
}
@@ -1272,76 +1369,6 @@ base85_encode_impl(PyObject *module, Py_buffer *data, int pad,
return PyBytesWriter_FinishWithPointer(writer, ascii_data);
}
-/*[clinic input]
-binascii.a2b_base85
-
- data: ascii_buffer
- /
-
-Decode a line of Base85 data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_a2b_base85_impl(PyObject *module, Py_buffer *data)
-/*[clinic end generated code: output=c2db6ab9181b0089 input=06c9d595352b5a2b]*/
-{
- return base85_decode_impl(module, data, table_a2b_base85, "Base85");
-}
-
-/*[clinic input]
-binascii.b2a_base85
-
- data: Py_buffer
- /
- *
- pad: bool = False
- Pad input to a multiple of 4 before encoding.
-
-Base85-code line of data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad)
-/*[clinic end generated code: output=b317adb36a57740d input=89fde81b96dcec06]*/
-{
- return base85_encode_impl(module, data, pad, table_b2a_base85, "Base85");
-}
-
-/*[clinic input]
-binascii.a2b_z85
-
- data: ascii_buffer
- /
-
-Decode a line of Z85 data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_a2b_z85_impl(PyObject *module, Py_buffer *data)
-/*[clinic end generated code: output=57d8260bb5267a98 input=c54baff4d81510a4]*/
-{
- return base85_decode_impl(module, data, table_a2b_base85_z85, "Z85");
-}
-
-/*[clinic input]
-binascii.b2a_z85
-
- data: Py_buffer
- /
- *
- pad: bool = False
- Pad input to a multiple of 4 before encoding.
-
-Z85-code line of data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_b2a_z85_impl(PyObject *module, Py_buffer *data, int pad)
-/*[clinic end generated code: output=88284835e332c9cf input=51d070a5a6cf82d8]*/
-{
- return base85_encode_impl(module, data, pad, table_b2a_base85_z85, "Z85");
-}
-
/*[clinic input]
binascii.crc_hqx
@@ -2003,8 +2030,6 @@ static struct PyMethodDef binascii_module_methods[] = {
BINASCII_A2B_ASCII85_METHODDEF
BINASCII_A2B_BASE85_METHODDEF
BINASCII_B2A_BASE85_METHODDEF
- BINASCII_A2B_Z85_METHODDEF
- BINASCII_B2A_Z85_METHODDEF
BINASCII_A2B_HEX_METHODDEF
BINASCII_B2A_HEX_METHODDEF
BINASCII_HEXLIFY_METHODDEF
@@ -2038,6 +2063,85 @@ binascii_exec(PyObject *module)
return -1;
}
+ if (PyModule_Add(module, "BASE64_ALPHABET",
+ PyBytes_FromStringAndSize((const char *)table_b2a_base64, 64)) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "URLSAFE_BASE64_ALPHABET",
+ PyBytes_FromString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-_")) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "CRYPT_ALPHABET",
+ PyBytes_FromString("./0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz")) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "BCRYPT_ALPHABET",
+ PyBytes_FromString("./"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789")) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "UU_ALPHABET",
+ PyBytes_FromString(" !\"#$%&'()*+,-./"
+ "0123456789:;<=>?@"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "[\\]^_")) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "XX_ALPHABET",
+ PyBytes_FromString("+-0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz")) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "BINHEX_ALPHABET",
+ PyBytes_FromString("!\"#$%&'()*+,-012345689@"
+ "ABCDEFGHIJKLMNPQRSTUVXYZ[`"
+ "abcdefhijklmpqr")) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "BASE85_ALPHABET",
+ PyBytes_FromStringAndSize((const char *)table_b2a_base85, 85)) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "ASCII85_ALPHABET",
+ PyBytes_FromStringAndSize((const char *)table_b2a_base85_a85, 85)) < 0)
+ {
+ return -1;
+ }
+ if (PyModule_Add(module, "Z85_ALPHABET",
+ PyBytes_FromString("0123456789"
+ "abcdefghijklmnopqrstuvwxyz"
+ /* clinic doesn't like '/' followed by '*' */
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ ".-:+=^!/\x2a?&<>()[]{}@%$#")) < 0)
+ {
+ return -1;
+ }
+
+ state->reverse_table_cache = PyDict_New();
+ if (state->reverse_table_cache == NULL) {
+ return -1;
+ }
+
+ state->reverse_table_cache = PyDict_New();
+ if (state->reverse_table_cache == NULL) {
+ return -1;
+ }
+
return 0;
}
@@ -2054,6 +2158,7 @@ binascii_traverse(PyObject *module, visitproc visit, void *arg)
binascii_state *state = get_binascii_state(module);
Py_VISIT(state->Error);
Py_VISIT(state->Incomplete);
+ Py_VISIT(state->reverse_table_cache);
return 0;
}
@@ -2063,6 +2168,7 @@ binascii_clear(PyObject *module)
binascii_state *state = get_binascii_state(module);
Py_CLEAR(state->Error);
Py_CLEAR(state->Incomplete);
+ Py_CLEAR(state->reverse_table_cache);
return 0;
}
diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h
index 68ab9c999b2894..f3ac720b1a35a3 100644
--- a/Modules/clinic/binascii.c.h
+++ b/Modules/clinic/binascii.c.h
@@ -117,7 +117,7 @@ binascii_b2a_uu(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj
PyDoc_STRVAR(binascii_a2b_base64__doc__,
"a2b_base64($module, data, /, *, strict_mode=,\n"
-" ignorechars=)\n"
+" alphabet=BASE64_ALPHABET, ignorechars=)\n"
"--\n"
"\n"
"Decode a line of base64 data.\n"
@@ -135,7 +135,7 @@ PyDoc_STRVAR(binascii_a2b_base64__doc__,
static PyObject *
binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
- Py_buffer *ignorechars);
+ PyBytesObject *alphabet, Py_buffer *ignorechars);
static PyObject *
binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@@ -143,7 +143,7 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 2
+ #define NUM_KEYWORDS 3
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
@@ -152,7 +152,7 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
- .ob_item = { &_Py_ID(strict_mode), &_Py_ID(ignorechars), },
+ .ob_item = { &_Py_ID(strict_mode), &_Py_ID(alphabet), &_Py_ID(ignorechars), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -161,17 +161,18 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"", "strict_mode", "ignorechars", NULL};
+ static const char * const _keywords[] = {"", "strict_mode", "alphabet", "ignorechars", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "a2b_base64",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[3];
+ PyObject *argsbuf[4];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
Py_buffer data = {NULL, NULL};
int strict_mode = -1;
+ PyBytesObject *alphabet = NULL;
Py_buffer ignorechars = {NULL, NULL};
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
@@ -194,11 +195,21 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
goto skip_optional_kwonly;
}
}
- if (PyObject_GetBuffer(args[2], &ignorechars, PyBUF_SIMPLE) != 0) {
+ if (args[2]) {
+ if (!PyBytes_Check(args[2])) {
+ _PyArg_BadArgument("a2b_base64", "argument 'alphabet'", "bytes", args[2]);
+ goto exit;
+ }
+ alphabet = (PyBytesObject *)args[2];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (PyObject_GetBuffer(args[3], &ignorechars, PyBUF_SIMPLE) != 0) {
goto exit;
}
skip_optional_kwonly:
- return_value = binascii_a2b_base64_impl(module, &data, strict_mode, &ignorechars);
+ return_value = binascii_a2b_base64_impl(module, &data, strict_mode, alphabet, &ignorechars);
exit:
/* Cleanup for data */
@@ -213,7 +224,8 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
}
PyDoc_STRVAR(binascii_b2a_base64__doc__,
-"b2a_base64($module, data, /, *, wrapcol=0, newline=True)\n"
+"b2a_base64($module, data, /, *, wrapcol=0, newline=True,\n"
+" alphabet=BASE64_ALPHABET)\n"
"--\n"
"\n"
"Base64-code line of data.");
@@ -223,7 +235,7 @@ PyDoc_STRVAR(binascii_b2a_base64__doc__,
static PyObject *
binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, size_t wrapcol,
- int newline);
+ int newline, Py_buffer *alphabet);
static PyObject *
binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@@ -231,7 +243,7 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 2
+ #define NUM_KEYWORDS 3
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
@@ -240,7 +252,7 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
- .ob_item = { &_Py_ID(wrapcol), &_Py_ID(newline), },
+ .ob_item = { &_Py_ID(wrapcol), &_Py_ID(newline), &_Py_ID(alphabet), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -249,18 +261,19 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"", "wrapcol", "newline", NULL};
+ static const char * const _keywords[] = {"", "wrapcol", "newline", "alphabet", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "b2a_base64",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[3];
+ PyObject *argsbuf[4];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
Py_buffer data = {NULL, NULL};
size_t wrapcol = 0;
int newline = 1;
+ Py_buffer alphabet = {NULL, NULL};
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
@@ -281,18 +294,30 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
goto skip_optional_kwonly;
}
}
- newline = PyObject_IsTrue(args[2]);
- if (newline < 0) {
+ if (args[2]) {
+ newline = PyObject_IsTrue(args[2]);
+ if (newline < 0) {
+ goto exit;
+ }
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (PyObject_GetBuffer(args[3], &alphabet, PyBUF_SIMPLE) != 0) {
goto exit;
}
skip_optional_kwonly:
- return_value = binascii_b2a_base64_impl(module, &data, wrapcol, newline);
+ return_value = binascii_b2a_base64_impl(module, &data, wrapcol, newline, &alphabet);
exit:
/* Cleanup for data */
if (data.obj) {
PyBuffer_Release(&data);
}
+ /* Cleanup for alphabet */
+ if (alphabet.obj) {
+ PyBuffer_Release(&alphabet);
+ }
return return_value;
}
@@ -519,53 +544,20 @@ binascii_b2a_ascii85(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
}
PyDoc_STRVAR(binascii_a2b_base85__doc__,
-"a2b_base85($module, data, /)\n"
+"a2b_base85($module, data, /, *, alphabet=BASE85_ALPHABET)\n"
"--\n"
"\n"
"Decode a line of Base85 data.");
#define BINASCII_A2B_BASE85_METHODDEF \
- {"a2b_base85", (PyCFunction)binascii_a2b_base85, METH_O, binascii_a2b_base85__doc__},
-
-static PyObject *
-binascii_a2b_base85_impl(PyObject *module, Py_buffer *data);
-
-static PyObject *
-binascii_a2b_base85(PyObject *module, PyObject *arg)
-{
- PyObject *return_value = NULL;
- Py_buffer data = {NULL, NULL};
-
- if (!ascii_buffer_converter(arg, &data)) {
- goto exit;
- }
- return_value = binascii_a2b_base85_impl(module, &data);
-
-exit:
- /* Cleanup for data */
- if (data.obj)
- PyBuffer_Release(&data);
-
- return return_value;
-}
-
-PyDoc_STRVAR(binascii_b2a_base85__doc__,
-"b2a_base85($module, data, /, *, pad=False)\n"
-"--\n"
-"\n"
-"Base85-code line of data.\n"
-"\n"
-" pad\n"
-" Pad input to a multiple of 4 before encoding.");
-
-#define BINASCII_B2A_BASE85_METHODDEF \
- {"b2a_base85", _PyCFunction_CAST(binascii_b2a_base85), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base85__doc__},
+ {"a2b_base85", _PyCFunction_CAST(binascii_a2b_base85), METH_FASTCALL|METH_KEYWORDS, binascii_a2b_base85__doc__},
static PyObject *
-binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad);
+binascii_a2b_base85_impl(PyObject *module, Py_buffer *data,
+ PyBytesObject *alphabet);
static PyObject *
-binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+binascii_a2b_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
@@ -579,7 +571,7 @@ binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
- .ob_item = { &_Py_ID(pad), },
+ .ob_item = { &_Py_ID(alphabet), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -588,67 +580,36 @@ binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"", "pad", NULL};
+ static const char * const _keywords[] = {"", "alphabet", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
- .fname = "b2a_base85",
+ .fname = "a2b_base85",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[2];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
Py_buffer data = {NULL, NULL};
- int pad = 0;
+ PyBytesObject *alphabet = NULL;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
- if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) {
+ if (!ascii_buffer_converter(args[0], &data)) {
goto exit;
}
if (!noptargs) {
goto skip_optional_kwonly;
}
- pad = PyObject_IsTrue(args[1]);
- if (pad < 0) {
+ if (!PyBytes_Check(args[1])) {
+ _PyArg_BadArgument("a2b_base85", "argument 'alphabet'", "bytes", args[1]);
goto exit;
}
+ alphabet = (PyBytesObject *)args[1];
skip_optional_kwonly:
- return_value = binascii_b2a_base85_impl(module, &data, pad);
-
-exit:
- /* Cleanup for data */
- if (data.obj) {
- PyBuffer_Release(&data);
- }
-
- return return_value;
-}
-
-PyDoc_STRVAR(binascii_a2b_z85__doc__,
-"a2b_z85($module, data, /)\n"
-"--\n"
-"\n"
-"Decode a line of Z85 data.");
-
-#define BINASCII_A2B_Z85_METHODDEF \
- {"a2b_z85", (PyCFunction)binascii_a2b_z85, METH_O, binascii_a2b_z85__doc__},
-
-static PyObject *
-binascii_a2b_z85_impl(PyObject *module, Py_buffer *data);
-
-static PyObject *
-binascii_a2b_z85(PyObject *module, PyObject *arg)
-{
- PyObject *return_value = NULL;
- Py_buffer data = {NULL, NULL};
-
- if (!ascii_buffer_converter(arg, &data)) {
- goto exit;
- }
- return_value = binascii_a2b_z85_impl(module, &data);
+ return_value = binascii_a2b_base85_impl(module, &data, alphabet);
exit:
/* Cleanup for data */
@@ -658,28 +619,29 @@ binascii_a2b_z85(PyObject *module, PyObject *arg)
return return_value;
}
-PyDoc_STRVAR(binascii_b2a_z85__doc__,
-"b2a_z85($module, data, /, *, pad=False)\n"
+PyDoc_STRVAR(binascii_b2a_base85__doc__,
+"b2a_base85($module, data, /, *, pad=False, alphabet=BASE85_ALPHABET)\n"
"--\n"
"\n"
-"Z85-code line of data.\n"
+"Base85-code line of data.\n"
"\n"
" pad\n"
" Pad input to a multiple of 4 before encoding.");
-#define BINASCII_B2A_Z85_METHODDEF \
- {"b2a_z85", _PyCFunction_CAST(binascii_b2a_z85), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_z85__doc__},
+#define BINASCII_B2A_BASE85_METHODDEF \
+ {"b2a_base85", _PyCFunction_CAST(binascii_b2a_base85), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base85__doc__},
static PyObject *
-binascii_b2a_z85_impl(PyObject *module, Py_buffer *data, int pad);
+binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad,
+ Py_buffer *alphabet);
static PyObject *
-binascii_b2a_z85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 1
+ #define NUM_KEYWORDS 2
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
@@ -688,7 +650,7 @@ binascii_b2a_z85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
- .ob_item = { &_Py_ID(pad), },
+ .ob_item = { &_Py_ID(pad), &_Py_ID(alphabet), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -697,17 +659,18 @@ binascii_b2a_z85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"", "pad", NULL};
+ static const char * const _keywords[] = {"", "pad", "alphabet", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
- .fname = "b2a_z85",
+ .fname = "b2a_base85",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[2];
+ PyObject *argsbuf[3];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
Py_buffer data = {NULL, NULL};
int pad = 0;
+ Py_buffer alphabet = {NULL, NULL};
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
@@ -720,18 +683,30 @@ binascii_b2a_z85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb
if (!noptargs) {
goto skip_optional_kwonly;
}
- pad = PyObject_IsTrue(args[1]);
- if (pad < 0) {
+ if (args[1]) {
+ pad = PyObject_IsTrue(args[1]);
+ if (pad < 0) {
+ goto exit;
+ }
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (PyObject_GetBuffer(args[2], &alphabet, PyBUF_SIMPLE) != 0) {
goto exit;
}
skip_optional_kwonly:
- return_value = binascii_b2a_z85_impl(module, &data, pad);
+ return_value = binascii_b2a_base85_impl(module, &data, pad, &alphabet);
exit:
/* Cleanup for data */
if (data.obj) {
PyBuffer_Release(&data);
}
+ /* Cleanup for alphabet */
+ if (alphabet.obj) {
+ PyBuffer_Release(&alphabet);
+ }
return return_value;
}
@@ -1281,4 +1256,4 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj
return return_value;
}
-/*[clinic end generated code: output=28de2d0774a0a4d7 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0343bea327c9f0b5 input=a9049054013a1b77]*/