From c6fdc281e2e5669096706e795f935acad2c4279a Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Fri, 11 Sep 2026 10:33:37 +0000 Subject: [PATCH 1/2] Optimize b16decode for empty ignorechars --- Lib/base64.py | 12 ++++++++---- Lib/test/test_base64.py | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Lib/base64.py b/Lib/base64.py index fa562f74a810345..9cc85d9788603e8 100644 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -301,10 +301,14 @@ def b16decode(s, casefold=False, *, ignorechars=b''): s = _bytes_from_decode_data(s) if not isinstance(ignorechars, bytes): ignorechars = bytes(memoryview(ignorechars)) - for b in b'abcdef': - if b in s and b not in ignorechars: - raise binascii.Error('Non-base16 digit found') - s = s.translate(None, delete=b'abcdef') + if ignorechars: + for b in b'abcdef': + if b in s and b not in ignorechars: + raise binascii.Error('Non-base16 digit found') + translated = s.translate(None, delete=b'abcdef') + if not ignorechars and len(translated) != len(s): + raise binascii.Error('Non-base16 digit found') + s = translated return binascii.unhexlify(s, ignorechars=ignorechars) # diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index 868abcfee24e101..c2357e15a57a941 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -886,6 +886,27 @@ def test_b16decode(self): # Incorrect "padding" self.assertRaises(binascii.Error, base64.b16decode, '010') + def test_b16decode_empty_ignorechars(self): + for ignorechars in (b'', bytearray(), memoryview(b'')): + with self.subTest(ignorechars=ignorechars): + self.assertEqual(base64.b16decode(b'', ignorechars=ignorechars), + b'') + self.assertEqual(base64.b16decode(b'00AF', ignorechars=ignorechars), + b'\x00\xaf') + # Deleting a lowercase digit must not turn invalid input + # into valid input, including an empty string. + for digit in b'abcdef': + lower = bytes([digit]) + for data in (lower, lower * 2, lower + b'00', + b'0' + lower + b'0', b'00' + lower): + for s in (data, data.decode('ascii'), + bytearray(data), memoryview(data)): + with self.subTest(s=s): + with self.assertRaisesRegex( + binascii.Error, '^Non-base16 digit found$' + ): + base64.b16decode(s, ignorechars=ignorechars) + def test_b16decode_ignorechars(self): self._common_test_ignorechars(base64.b16decode) eq = self.assertEqual From 4298ee9383a9c3baaf9d792c0f155cf66b500c0e Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Fri, 11 Sep 2026 12:19:35 +0000 Subject: [PATCH 2/2] Skip b16decode translation when ignorechars is empty --- Lib/base64.py | 11 ++++------- Lib/test/test_base64.py | 21 --------------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/Lib/base64.py b/Lib/base64.py index 9cc85d9788603e8..347807de00d74f3 100644 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -301,14 +301,11 @@ def b16decode(s, casefold=False, *, ignorechars=b''): s = _bytes_from_decode_data(s) if not isinstance(ignorechars, bytes): ignorechars = bytes(memoryview(ignorechars)) + for b in b'abcdef': + if b in s and b not in ignorechars: + raise binascii.Error('Non-base16 digit found') if ignorechars: - for b in b'abcdef': - if b in s and b not in ignorechars: - raise binascii.Error('Non-base16 digit found') - translated = s.translate(None, delete=b'abcdef') - if not ignorechars and len(translated) != len(s): - raise binascii.Error('Non-base16 digit found') - s = translated + s = s.translate(None, delete=b'abcdef') return binascii.unhexlify(s, ignorechars=ignorechars) # diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index c2357e15a57a941..868abcfee24e101 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -886,27 +886,6 @@ def test_b16decode(self): # Incorrect "padding" self.assertRaises(binascii.Error, base64.b16decode, '010') - def test_b16decode_empty_ignorechars(self): - for ignorechars in (b'', bytearray(), memoryview(b'')): - with self.subTest(ignorechars=ignorechars): - self.assertEqual(base64.b16decode(b'', ignorechars=ignorechars), - b'') - self.assertEqual(base64.b16decode(b'00AF', ignorechars=ignorechars), - b'\x00\xaf') - # Deleting a lowercase digit must not turn invalid input - # into valid input, including an empty string. - for digit in b'abcdef': - lower = bytes([digit]) - for data in (lower, lower * 2, lower + b'00', - b'0' + lower + b'0', b'00' + lower): - for s in (data, data.decode('ascii'), - bytearray(data), memoryview(data)): - with self.subTest(s=s): - with self.assertRaisesRegex( - binascii.Error, '^Non-base16 digit found$' - ): - base64.b16decode(s, ignorechars=ignorechars) - def test_b16decode_ignorechars(self): self._common_test_ignorechars(base64.b16decode) eq = self.assertEqual