bases.encoding.zero_padded API documentation

# Module `bases.encoding.zero_padded`

Expand source code

    import math
    from typing import Any, Dict, Mapping, Optional, Union

    from bases.alphabet import Alphabet
    from .base import BaseEncoding
    from .simple import SimpleBaseEncoding

    class ZeroPaddedBaseEncoding(BaseEncoding):

        _simple_encoding: SimpleBaseEncoding
        _block_nbytes: int
        _block_nchars: int

        def __init__(self, alphabet: Union[str, range, Alphabet], *,
                     case_sensitive: Optional[bool] = None,
                     block_nbytes: int = 1,
                     block_nchars: int = 1):
            super().__init__(alphabet, case_sensitive=case_sensitive)
            self._simple_encoding = SimpleBaseEncoding(self.alphabet)
            self._block_nbytes = block_nbytes
            self._block_nchars = block_nchars
            self.__validate_init()

        def __validate_init(self) -> None:
            base = self.base
            block_nbytes = self.block_nbytes
            block_nchars = self.block_nchars
            max_block_nbytes = ZeroPaddedBaseEncoding.max_block_nbytes(base, block_nchars)
            max_block_nchars = ZeroPaddedBaseEncoding.max_block_nchars(base, block_nbytes)
            if block_nchars > max_block_nchars:
                raise ValueError(f"Number of characters allowed per zero-padding block is too large: "
                                 f"the maximum for base = {base} and block_nbytes = {block_nbytes} is "
                                 f"block_nchars = {max_block_nchars}")
            if block_nbytes > max_block_nbytes:
                raise ValueError(f"Number of bytes allowed per zero-padding block is too large: "
                                 f"the maximum for base = {base} and block_nchars {block_nchars} is "
                                 f"block_nbytes = {max_block_nbytes}")

        @staticmethod
        def max_block_nchars(base: int, block_nbytes: int) -> int:
            """
                Returns the maximum integer value for `block_chars` such that:

                ```py
                    256**block_nbytes > base**(block_nchars-1)
                ```
            """
            if base <= 1:
                raise ValueError("Base must be >= 2.")
            if block_nbytes <= 0:
                raise ValueError("Number of bytes per zero-padding block must be positive.")
            _max_nc = block_nbytes/math.log(base, 256)+1
            _max_nc_floor = int(math.floor(_max_nc))
            return _max_nc_floor if _max_nc > _max_nc_floor else _max_nc_floor-1

        @staticmethod
        def max_block_nbytes(base: int, block_nchars: int) -> int:
            """
                Returns the maximum integer value for `block_nbytes` such that:

                ```py
                    base**block_nchars > 256**(block_nbytes-1)
                ```
            """
            if base <= 1:
                raise ValueError("Base must be >= 2.")
            if block_nchars <= 0:
                raise ValueError("Number of chars per zero-padding block must be positive.")
            _max_nb = block_nchars/math.log(256, base)+1
            _max_nb_floor = int(math.floor(_max_nb))
            return _max_nb_floor if _max_nb > _max_nb_floor else _max_nb_floor-1

        @property
        def block_nbytes(self) -> int:
            return self._block_nbytes

        @property
        def block_nchars(self) -> int:
            return self._block_nchars

        def _canonical_bytes(self, b: bytes) -> bytes:
            self._validate_bytes(b)
            block_nbytes = self.block_nbytes
            extra_bytes = len(b)%block_nbytes
            if extra_bytes == 0:
                return b
            return b"\x00"*(block_nbytes-extra_bytes)+b

        def _canonical_string(self, s: str) -> str:
            self._validate_string(s)
            block_nchars = self.block_nchars
            extra_chars = len(s)%block_nchars
            if extra_chars == 0:
                return s
            return self.zero_char*(block_nchars-extra_chars)+s

        def _encode(self, b: bytes) -> str:
            b = self._canonical_bytes(b)
            block_nbytes = self.block_nbytes
            block_nchars = self.block_nchars
            zero_char = self.zero_char
            # strip leading zero bytes
            b_stripped = b.lstrip(b"\x00")
            # compute simple base encoding
            s = self._simple_encoding.encode(b_stripped)
            # pad simple base encoding to integral multiple of block char size
            extra_chars = len(s)%block_nchars
            if extra_chars != 0:
                s = zero_char*(block_nchars-extra_chars)+s
            # count leading zero blocks
            num_zero_blocks = (len(b)-len(b_stripped))//block_nbytes
            # return zero-padded base encoding
            s = zero_char*num_zero_blocks*block_nchars+s
            return s

        def _decode(self, s: str) -> bytes:
            s = self._canonical_string(s)
            block_nbytes = self.block_nbytes
            block_nchars = self.block_nchars
            # strip leading zero chars
            s_stripped = s.lstrip(self.zero_char)
            # compute simple base decoding
            b = self._simple_encoding.decode(s_stripped)
            # pad simple base decoding to integral multiple of block byte size
            extra_bytes = len(b)%block_nbytes
            if extra_bytes != 0:
                b = b"\x00"*(block_nbytes-extra_bytes)+b
            # compute leading zero blocks
            num_zero_blocks = (len(s)-len(s_stripped))//block_nchars
            # return zero-padded base decoding
            b = b"\x00"*num_zero_blocks*block_nbytes+b
            return b

        def options(self, skip_defaults: bool = False) -> Mapping[str, Any]:
            options: Dict[str, Any] = {}
            if not skip_defaults or self.block_nbytes != 1:
                options["block_nbytes"] = self.block_nbytes
            if not skip_defaults or self.block_nchars != 1:
                options["block_nchars"] = self.block_nchars
            return options

## Classes

`class ZeroPaddedBaseEncoding (alphabet: Union[str, range, Alphabet], *, case_sensitive: Optional[bool] = None, block_nbytes: int = 1, block_nchars: int = 1)`

  Helper class that provides a standard way to create an ABC using inheritance.

  Expand source code

      class ZeroPaddedBaseEncoding(BaseEncoding):

          _simple_encoding: SimpleBaseEncoding
          _block_nbytes: int
          _block_nchars: int

          def __init__(self, alphabet: Union[str, range, Alphabet], *,
                       case_sensitive: Optional[bool] = None,
                       block_nbytes: int = 1,
                       block_nchars: int = 1):
              super().__init__(alphabet, case_sensitive=case_sensitive)
              self._simple_encoding = SimpleBaseEncoding(self.alphabet)
              self._block_nbytes = block_nbytes
              self._block_nchars = block_nchars
              self.__validate_init()

          def __validate_init(self) -> None:
              base = self.base
              block_nbytes = self.block_nbytes
              block_nchars = self.block_nchars
              max_block_nbytes = ZeroPaddedBaseEncoding.max_block_nbytes(base, block_nchars)
              max_block_nchars = ZeroPaddedBaseEncoding.max_block_nchars(base, block_nbytes)
              if block_nchars > max_block_nchars:
                  raise ValueError(f"Number of characters allowed per zero-padding block is too large: "
                                   f"the maximum for base = {base} and block_nbytes = {block_nbytes} is "
                                   f"block_nchars = {max_block_nchars}")
              if block_nbytes > max_block_nbytes:
                  raise ValueError(f"Number of bytes allowed per zero-padding block is too large: "
                                   f"the maximum for base = {base} and block_nchars {block_nchars} is "
                                   f"block_nbytes = {max_block_nbytes}")

          @staticmethod
          def max_block_nchars(base: int, block_nbytes: int) -> int:
              """
                  Returns the maximum integer value for `block_chars` such that:

                  ```py
                      256**block_nbytes > base**(block_nchars-1)
                  ```
              """
              if base <= 1:
                  raise ValueError("Base must be >= 2.")
              if block_nbytes <= 0:
                  raise ValueError("Number of bytes per zero-padding block must be positive.")
              _max_nc = block_nbytes/math.log(base, 256)+1
              _max_nc_floor = int(math.floor(_max_nc))
              return _max_nc_floor if _max_nc > _max_nc_floor else _max_nc_floor-1

          @staticmethod
          def max_block_nbytes(base: int, block_nchars: int) -> int:
              """
                  Returns the maximum integer value for `block_nbytes` such that:

                  ```py
                      base**block_nchars > 256**(block_nbytes-1)
                  ```
              """
              if base <= 1:
                  raise ValueError("Base must be >= 2.")
              if block_nchars <= 0:
                  raise ValueError("Number of chars per zero-padding block must be positive.")
              _max_nb = block_nchars/math.log(256, base)+1
              _max_nb_floor = int(math.floor(_max_nb))
              return _max_nb_floor if _max_nb > _max_nb_floor else _max_nb_floor-1

          @property
          def block_nbytes(self) -> int:
              return self._block_nbytes

          @property
          def block_nchars(self) -> int:
              return self._block_nchars

          def _canonical_bytes(self, b: bytes) -> bytes:
              self._validate_bytes(b)
              block_nbytes = self.block_nbytes
              extra_bytes = len(b)%block_nbytes
              if extra_bytes == 0:
                  return b
              return b"\x00"*(block_nbytes-extra_bytes)+b

          def _canonical_string(self, s: str) -> str:
              self._validate_string(s)
              block_nchars = self.block_nchars
              extra_chars = len(s)%block_nchars
              if extra_chars == 0:
                  return s
              return self.zero_char*(block_nchars-extra_chars)+s

          def _encode(self, b: bytes) -> str:
              b = self._canonical_bytes(b)
              block_nbytes = self.block_nbytes
              block_nchars = self.block_nchars
              zero_char = self.zero_char
              # strip leading zero bytes
              b_stripped = b.lstrip(b"\x00")
              # compute simple base encoding
              s = self._simple_encoding.encode(b_stripped)
              # pad simple base encoding to integral multiple of block char size
              extra_chars = len(s)%block_nchars
              if extra_chars != 0:
                  s = zero_char*(block_nchars-extra_chars)+s
              # count leading zero blocks
              num_zero_blocks = (len(b)-len(b_stripped))//block_nbytes
              # return zero-padded base encoding
              s = zero_char*num_zero_blocks*block_nchars+s
              return s

          def _decode(self, s: str) -> bytes:
              s = self._canonical_string(s)
              block_nbytes = self.block_nbytes
              block_nchars = self.block_nchars
              # strip leading zero chars
              s_stripped = s.lstrip(self.zero_char)
              # compute simple base decoding
              b = self._simple_encoding.decode(s_stripped)
              # pad simple base decoding to integral multiple of block byte size
              extra_bytes = len(b)%block_nbytes
              if extra_bytes != 0:
                  b = b"\x00"*(block_nbytes-extra_bytes)+b
              # compute leading zero blocks
              num_zero_blocks = (len(s)-len(s_stripped))//block_nchars
              # return zero-padded base decoding
              b = b"\x00"*num_zero_blocks*block_nbytes+b
              return b

          def options(self, skip_defaults: bool = False) -> Mapping[str, Any]:
              options: Dict[str, Any] = {}
              if not skip_defaults or self.block_nbytes != 1:
                  options["block_nbytes"] = self.block_nbytes
              if not skip_defaults or self.block_nchars != 1:
                  options["block_nchars"] = self.block_nchars
              return options

  ### Ancestors

  - BaseEncoding
  - abc.ABC

  ### Static methods

  `def max_block_nbytes(base: int, block_nchars: int) ‑> int`

    Returns the maximum integer value for `block_nbytes` such that:

            base**block_nchars > 256**(block_nbytes-1)

    Expand source code

        @staticmethod
        def max_block_nbytes(base: int, block_nchars: int) -> int:
            """
                Returns the maximum integer value for `block_nbytes` such that:

                ```py
                    base**block_nchars > 256**(block_nbytes-1)
                ```
            """
            if base <= 1:
                raise ValueError("Base must be >= 2.")
            if block_nchars <= 0:
                raise ValueError("Number of chars per zero-padding block must be positive.")
            _max_nb = block_nchars/math.log(256, base)+1
            _max_nb_floor = int(math.floor(_max_nb))
            return _max_nb_floor if _max_nb > _max_nb_floor else _max_nb_floor-1

  `def max_block_nchars(base: int, block_nbytes: int) ‑> int`

    Returns the maximum integer value for `block_chars` such that:

            256**block_nbytes > base**(block_nchars-1)

    Expand source code

        @staticmethod
        def max_block_nchars(base: int, block_nbytes: int) -> int:
            """
                Returns the maximum integer value for `block_chars` such that:

                ```py
                    256**block_nbytes > base**(block_nchars-1)
                ```
            """
            if base <= 1:
                raise ValueError("Base must be >= 2.")
            if block_nbytes <= 0:
                raise ValueError("Number of bytes per zero-padding block must be positive.")
            _max_nc = block_nbytes/math.log(base, 256)+1
            _max_nc_floor = int(math.floor(_max_nc))
            return _max_nc_floor if _max_nc > _max_nc_floor else _max_nc_floor-1

  ### Instance variables

  `var block_nbytes : int`
    Expand source code

        @property
        def block_nbytes(self) -> int:
            return self._block_nbytes

  `var block_nchars : int`
    Expand source code

        @property
        def block_nchars(self) -> int:
            return self._block_nchars

  ### Methods

  `def options(self, skip_defaults: bool = False) ‑> Mapping[str, Any]`
    Expand source code

        def options(self, skip_defaults: bool = False) -> Mapping[str, Any]:
            options: Dict[str, Any] = {}
            if not skip_defaults or self.block_nbytes != 1:
                options["block_nbytes"] = self.block_nbytes
            if not skip_defaults or self.block_nchars != 1:
                options["block_nchars"] = self.block_nchars
            return options

  ### Inherited members

  - `BaseEncoding`:
    - `alphabet`
    - `base`
    - `case_sensitive`
    - `lower`
    - `upper`
    - `with_case_sensitivity`
    - `zero_char`