Source code for hbutils.binary.base

"""
This module provides basic IO types for binary file operations.

It defines a hierarchy of classes for reading and writing binary data with different characteristics:
- CIOType: Base class for all IO types
- CFixedType: Types with fixed size (int, uint, float, etc.)
- CRangedIntType: Fixed-size types with value range constraints
- CMarkedType: Types that can be read/written using Python's struct module

These classes are designed to work with binary file objects and io.BytesIO instances.
"""

import struct
from typing import BinaryIO


[docs] class CIOType: """ Basic IO type. Used as base class of all the IO types. Provides the interface for reading from and writing to binary IO objects. """
[docs] def read(self, file: BinaryIO): """ Read from binary IO object. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :return: Reading result. .. warning:: Need to be implemented in subclasses. :raises NotImplementedError: This method must be implemented by subclasses. """ raise NotImplementedError # pragma: no cover
[docs] def write(self, file: BinaryIO, val): """ Write object to binary IO object. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :param val: Object to write. .. warning:: Need to be implemented in subclasses. :raises NotImplementedError: This method must be implemented by subclasses. """ raise NotImplementedError # pragma: no cover
[docs] class CFixedType(CIOType): """ Type with fixed size. Represents types with a fixed size in bytes, such as ``int``, ``uint`` and ``float``. This class extends CIOType to add size information. """
[docs] def __init__(self, size: int): """ Constructor of :class:`CFixedType`. :param size: Size of the type in bytes. :type size: int """ self.__size = size
@property def size(self) -> int: """ Size of the given type in bytes. :return: The size of the type. :rtype: int """ return self.__size
[docs] def read(self, file: BinaryIO): """ Read from binary IO object. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :return: Reading result. .. warning:: Need to be implemented in subclasses. :raises NotImplementedError: This method must be implemented by subclasses. """ raise NotImplementedError # pragma: no cover
[docs] def write(self, file: BinaryIO, val): """ Write object to binary IO object. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :param val: Object to write. .. warning:: Need to be implemented in subclasses. :raises NotImplementedError: This method must be implemented by subclasses. """ raise NotImplementedError # pragma: no cover
[docs] class CRangedIntType(CFixedType): """ Type with fixed size and value range. Represents integer types with fixed size and range constraints, such as ``int`` and ``uint``. This class extends CFixedType to add minimum and maximum value constraints. """
[docs] def __init__(self, size: int, minimum: int, maximum: int): """ Constructor of :class:`CRangedIntType`. :param size: Size of the type in bytes. :type size: int :param minimum: Minimum value of the type. :type minimum: int :param maximum: Maximum value of the type. :type maximum: int """ CFixedType.__init__(self, size) self.__size = size self.__minimum = minimum self.__maximum = maximum
@property def minimum(self) -> int: """ Minimum value of the type. :return: The minimum value that can be represented by this type. :rtype: int """ return self.__minimum @property def maximum(self) -> int: """ Maximum value of the type. :return: The maximum value that can be represented by this type. :rtype: int """ return self.__maximum
[docs] def read(self, file: BinaryIO): """ Read from binary IO object. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :return: Reading result. .. warning:: Need to be implemented in subclasses. :raises NotImplementedError: This method must be implemented by subclasses. """ raise NotImplementedError # pragma: no cover
[docs] def write(self, file: BinaryIO, val): """ Write object to binary IO object. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :param val: Object to write. .. warning:: Need to be implemented in subclasses. :raises NotImplementedError: This method must be implemented by subclasses. """ raise NotImplementedError # pragma: no cover
[docs] class CMarkedType(CFixedType): """ Type with struct mark. Represents types that can be directly read and written using Python's ``struct`` module. The mark parameter corresponds to format characters used by struct (e.g., 'i' for int, 'f' for float). Example:: >>> import io >>> float_type = CMarkedType('f', 4) >>> buffer = io.BytesIO() >>> float_type.write(buffer, 3.14) >>> buffer.seek(0) 0 >>> float_type.read(buffer) 3.140000104904175 """
[docs] def __init__(self, mark: str, size: int): """ Constructor of :class:`CMarkedType`. :param mark: Format character for the struct module (e.g., 'i', 'f', 'd'). :type mark: str :param size: Size of the type in bytes. :type size: int """ CFixedType.__init__(self, size) self.__mark = mark
@property def mark(self) -> str: """ Mark of the type. The format character that will be used to read from and write to binary data with the ``struct`` module. :return: The struct format character. :rtype: str """ return self.__mark
[docs] def read(self, file: BinaryIO): """ Read from binary with ``struct`` module. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :return: Result value read from the binary file. Example:: >>> import io >>> buffer = io.BytesIO(b'\\x00\\x00\\x00\\x05') >>> int_type = CMarkedType('i', 4) >>> int_type.read(buffer) 5 """ r, = struct.unpack(self.mark, file.read(self.size)) return r
[docs] def write(self, file: BinaryIO, val): """ Write value to binary IO with ``struct`` module. :param file: Binary file, ``io.BytesIO`` is supported as well. :type file: BinaryIO :param val: Writing value. Will be converted to float before packing. Example:: >>> import io >>> buffer = io.BytesIO() >>> float_type = CMarkedType('f', 4) >>> float_type.write(buffer, 3.14) >>> buffer.getvalue() b'\\xc3\\xf5H@' """ file.write(struct.pack(self.mark, float(val)))