168 lines
5.7 KiB
Python
168 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
from base64 import b64decode as builtin_decode
|
|
from base64 import b64encode as builtin_encode
|
|
from base64 import encodebytes as builtin_encodebytes
|
|
from binascii import Error as BinAsciiError
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from ._typing import Buffer
|
|
|
|
_bytes_types = (bytes, bytearray) # Types acceptable as binary data
|
|
|
|
|
|
def _get_simd_name(flags: int) -> str:
|
|
assert flags == 0
|
|
return "fallback"
|
|
|
|
|
|
def _get_simd_path() -> int:
|
|
return 0
|
|
|
|
|
|
def _get_bytes(s: str | Buffer) -> bytes | bytearray:
|
|
if isinstance(s, str):
|
|
try:
|
|
return s.encode("ascii")
|
|
except UnicodeEncodeError:
|
|
msg = "string argument should contain only ASCII characters"
|
|
raise ValueError(msg) from None
|
|
if isinstance(s, _bytes_types):
|
|
return s
|
|
try:
|
|
mv = memoryview(s)
|
|
if not mv.c_contiguous:
|
|
msg = f"{s.__class__.__name__!r:s}: underlying buffer is not C-contiguous"
|
|
raise BufferError(msg)
|
|
return mv.tobytes()
|
|
except TypeError:
|
|
msg = (
|
|
"argument should be a bytes-like object or ASCII "
|
|
f"string, not {s.__class__.__name__!r:s}"
|
|
)
|
|
raise TypeError(msg) from None
|
|
|
|
|
|
def b64decode(
|
|
s: str | Buffer, altchars: str | Buffer | None = None, validate: bool = False
|
|
) -> bytes:
|
|
"""Decode bytes encoded with the standard Base64 alphabet.
|
|
|
|
Argument ``s`` is a :term:`bytes-like object` or ASCII string to
|
|
decode.
|
|
|
|
Optional ``altchars`` must be a :term:`bytes-like object` or ASCII
|
|
string of length 2 which specifies the alternative alphabet used instead
|
|
of the '+' and '/' characters.
|
|
|
|
If ``validate`` is ``False`` (the default), characters that are neither in
|
|
the normal base-64 alphabet nor the alternative alphabet are discarded
|
|
prior to the padding check.
|
|
If ``validate`` is ``True``, these non-alphabet characters in the input
|
|
result in a :exc:`binascii.Error`.
|
|
|
|
The result is returned as a :class:`bytes` object.
|
|
|
|
A :exc:`binascii.Error` is raised if ``s`` is incorrectly padded.
|
|
"""
|
|
s = _get_bytes(s)
|
|
if altchars is not None:
|
|
altchars = _get_bytes(altchars)
|
|
if validate:
|
|
if len(s) % 4 != 0:
|
|
msg = "Incorrect padding"
|
|
raise BinAsciiError(msg)
|
|
result = builtin_decode(s, altchars, validate=False)
|
|
|
|
# check length of result vs length of input
|
|
expected_len = 0
|
|
if len(s) > 0:
|
|
padding = 0
|
|
# len(s) % 4 != 0 implies len(s) >= 4 here
|
|
if s[-2] == 61: # 61 == ord("=")
|
|
padding += 1
|
|
if s[-1] == 61:
|
|
padding += 1
|
|
expected_len = 3 * (len(s) // 4) - padding
|
|
if expected_len != len(result):
|
|
msg = "Non-base64 digit found"
|
|
raise BinAsciiError(msg)
|
|
return result
|
|
return builtin_decode(s, altchars, validate=False)
|
|
|
|
|
|
def b64decode_as_bytearray(
|
|
s: str | Buffer, altchars: str | Buffer | None = None, validate: bool = False
|
|
) -> bytearray:
|
|
"""Decode bytes encoded with the standard Base64 alphabet.
|
|
|
|
Argument ``s`` is a :term:`bytes-like object` or ASCII string to
|
|
decode.
|
|
|
|
Optional ``altchars`` must be a :term:`bytes-like object` or ASCII
|
|
string of length 2 which specifies the alternative alphabet used instead
|
|
of the '+' and '/' characters.
|
|
|
|
If ``validate`` is ``False`` (the default), characters that are neither in
|
|
the normal base-64 alphabet nor the alternative alphabet are discarded
|
|
prior to the padding check.
|
|
If ``validate`` is ``True``, these non-alphabet characters in the input
|
|
result in a :exc:`binascii.Error`.
|
|
|
|
The result is returned as a :class:`bytearray` object.
|
|
|
|
A :exc:`binascii.Error` is raised if ``s`` is incorrectly padded.
|
|
"""
|
|
return bytearray(b64decode(s, altchars=altchars, validate=validate))
|
|
|
|
|
|
def b64encode(s: Buffer, altchars: str | Buffer | None = None) -> bytes:
|
|
"""Encode bytes using the standard Base64 alphabet.
|
|
|
|
Argument ``s`` is a :term:`bytes-like object` to encode.
|
|
|
|
Optional ``altchars`` must be a byte string of length 2 which specifies
|
|
an alternative alphabet for the '+' and '/' characters. This allows an
|
|
application to e.g. generate url or filesystem safe Base64 strings.
|
|
|
|
The result is returned as a :class:`bytes` object.
|
|
"""
|
|
mv = memoryview(s)
|
|
if not mv.c_contiguous:
|
|
msg = f"{s.__class__.__name__!r:s}: underlying buffer is not C-contiguous"
|
|
raise BufferError(msg)
|
|
if altchars is not None:
|
|
altchars = _get_bytes(altchars)
|
|
return builtin_encode(s, altchars)
|
|
|
|
|
|
def b64encode_as_string(s: Buffer, altchars: str | Buffer | None = None) -> str:
|
|
"""Encode bytes using the standard Base64 alphabet.
|
|
|
|
Argument ``s`` is a :term:`bytes-like object` to encode.
|
|
|
|
Optional ``altchars`` must be a byte string of length 2 which specifies
|
|
an alternative alphabet for the '+' and '/' characters. This allows an
|
|
application to e.g. generate url or filesystem safe Base64 strings.
|
|
|
|
The result is returned as a :class:`str` object.
|
|
"""
|
|
return b64encode(s, altchars).decode("ascii")
|
|
|
|
|
|
def encodebytes(s: Buffer) -> bytes:
|
|
"""Encode bytes into a bytes object with newlines (b'\\\\n') inserted after
|
|
every 76 bytes of output, and ensuring that there is a trailing newline,
|
|
as per :rfc:`2045` (MIME).
|
|
|
|
Argument ``s`` is a :term:`bytes-like object` to encode.
|
|
|
|
The result is returned as a :class:`bytes` object.
|
|
"""
|
|
mv = memoryview(s)
|
|
if not mv.c_contiguous:
|
|
msg = f"{s.__class__.__name__!r:s}: underlying buffer is not C-contiguous"
|
|
raise BufferError(msg)
|
|
return builtin_encodebytes(s)
|