Source code for hbutils.binary.int

"""
This module provides signed integer types for binary I/O operations.

It implements various signed integer types (8-bit, 16-bit, 32-bit, 64-bit) that can be used
to read from and write to binary files or streams. The module also provides platform-dependent
type aliases that match C language integer types.

The main class is :class:`CSignedIntType`, which handles the conversion between binary data
and signed integer values. It supports reading and writing signed integers of different sizes
while handling two's complement representation.

Example::
    >>> import io
    >>> from hbutils.binary import c_int32
    >>> 
    >>> # Reading signed integers
    >>> with io.BytesIO(b'\\xde\\xad\\xbe\\xef') as file:
    ...     value = c_int32.read(file)
    ...     print(value)
    -272716322
    
    >>> # Writing signed integers
    >>> with io.BytesIO() as file:
    ...     c_int32.write(file, -272716322)
    ...     print(file.getvalue())
    b'\\xde\\xad\\xbe\\xef'
"""
import ctypes
from typing import Dict, BinaryIO, List

from .base import CRangedIntType
from .uint import CUnsignedIntType

__all__ = [
    'CSignedIntType',
    'c_int8', 'c_int16', 'c_int32', 'c_int64',
    'c_byte', 'c_short', 'c_int', 'c_long', 'c_longlong',
]


[docs] class CSignedIntType(CRangedIntType): """ Signed integer type for binary I/O operations. This class provides functionality to read and write signed integers of a specific size to and from binary streams. It handles two's complement representation internally by using an unsigned integer type for the actual I/O operations. :param size: Size of the integer type in bytes. :type size: int Example:: >>> import io >>> from hbutils.binary import CSignedIntType >>> >>> # Create a 2-byte signed integer type >>> int16_type = CSignedIntType(2) >>> >>> # Read a signed integer >>> with io.BytesIO(b'\\xde\\xad') as file: ... value = int16_type.read(file) ... print(value) -21026 >>> # Write a signed integer >>> with io.BytesIO() as file: ... int16_type.write(file, -21026) ... print(file.getvalue()) b'\\xde\\xad' """
[docs] def __init__(self, size: int): """ Initialize a signed integer type with the specified size. :param size: Size of the integer type in bytes. :type size: int The constructor sets up the internal unsigned integer type for I/O operations and calculates the valid range for signed integers of this size. """ self.__size = size self._unit = CUnsignedIntType(size) self.__half = 1 << (8 * self.__size - 1) CRangedIntType.__init__( self, self.__size, self._unit.minimum - self.__half, self._unit.maximum - self.__half )
[docs] def read(self, file: BinaryIO) -> int: """ Read a signed integer value from a binary stream. :param file: Binary file or stream to read from. ``io.BytesIO`` is supported as well. :type file: BinaryIO :return: The signed integer value read from the stream. :rtype: int This method reads the unsigned representation and converts it to a signed integer using two's complement representation. Example:: >>> import io >>> from hbutils.binary import c_int16 >>> >>> with io.BytesIO(b'\\xde\\xad') as file: ... value = c_int16.read(file) ... print(value) -21026 """ uval = self._unit.read(file) if uval < self.__half: return uval else: return uval - (self.__half << 1)
[docs] def write(self, file: BinaryIO, val: int): """ Write a signed integer value to a binary stream. :param file: Binary file or stream to write to. ``io.BytesIO`` is supported as well. :type file: BinaryIO :param val: Signed integer value to write. :type val: int :raises TypeError: If the value is not an integer. :raises ValueError: If the value is outside the valid range for this integer type. This method converts the signed integer to its unsigned two's complement representation before writing to the stream. Example:: >>> import io >>> from hbutils.binary import c_int16 >>> >>> with io.BytesIO() as file: ... c_int16.write(file, -21026) ... print(file.getvalue()) b'\\xde\\xad' """ if not isinstance(val, int): raise TypeError(f'Int value expected, but {repr(val)} found.') elif not (self.minimum <= val <= self.maximum): raise ValueError(f'Signed int value within ' f'[{self.minimum}, {self.maximum}] expected, but {repr(val)} found.') fval = val if val >= 0 else val + (self.__half << 1) self._unit.write(file, fval)
c_int8 = CSignedIntType(ctypes.sizeof(ctypes.c_int8)) """ Reading and writing signed integer with 8-bits. This type represents a signed 8-bit integer with a range of -128 to 127. Examples:: >>> import io >>> from hbutils.binary import c_int8 >>> >>> with io.BytesIO(b'\\xde\\xad\\xbe\\xef') as file: ... print(c_int8.read(file)) ... print(c_int8.read(file)) ... print(c_int8.read(file)) ... print(c_int8.read(file)) -34 -83 -66 -17 >>> with io.BytesIO() as file: ... c_int8.write(file, -34) ... c_int8.write(file, -83) ... c_int8.write(file, -66) ... c_int8.write(file, -17) ... print(file.getvalue()) b'\\xde\\xad\\xbe\\xef' """ c_int16 = CSignedIntType(ctypes.sizeof(ctypes.c_int16)) """ Reading and writing signed integer with 16-bits. This type represents a signed 16-bit integer with a range of -32768 to 32767. Examples:: >>> import io >>> from hbutils.binary import c_int16 >>> >>> with io.BytesIO(b'\\xde\\xad\\xbe\\xef\\x12\\x34\\x56\\xf7') as file: ... print(c_int16.read(file)) ... print(c_int16.read(file)) ... print(c_int16.read(file)) ... print(c_int16.read(file)) -21026 -4162 13330 -2218 >>> with io.BytesIO() as file: ... c_int16.write(file, -21026) ... c_int16.write(file, -4162) ... c_int16.write(file, 13330) ... c_int16.write(file, -2218) ... print(file.getvalue()) b'\\xde\\xad\\xbe\\xef\\x124V\\xf7' """ c_int32 = CSignedIntType(ctypes.sizeof(ctypes.c_int32)) """ Reading and writing signed integer with 32-bits. This type represents a signed 32-bit integer with a range of -2147483648 to 2147483647. Examples:: >>> import io >>> from hbutils.binary import c_int32 >>> >>> with io.BytesIO(b'\\xde\\xad\\xbe\\xef\\x12\\x34\\x56\\xf7') as file: ... print(c_int32.read(file)) ... print(c_int32.read(file)) -272716322 -145345518 >>> with io.BytesIO() as file: ... c_int32.write(file, -272716322) ... c_int32.write(file, -145345518) ... print(file.getvalue()) b'\\xde\\xad\\xbe\\xef\\x124V\\xf7' """ c_int64 = CSignedIntType(ctypes.sizeof(ctypes.c_int64)) """ Reading and writing signed integer with 64-bits. This type represents a signed 64-bit integer with a range of -9223372036854775808 to 9223372036854775807. Examples:: >>> import io >>> from hbutils.binary import c_int64 >>> >>> with io.BytesIO(b'\\xde\\xad\\xbe\\xef\\x12\\x34\\x56\\xf7') as file: ... print(c_int64.read(file)) -624254242407928354 >>> with io.BytesIO() as file: ... c_int64.write(file, -624254242407928354) ... print(file.getvalue()) b'\\xde\\xad\\xbe\\xef\\x124V\\xf7' """ _EXIST_TYPES: List[CSignedIntType] = [ c_int8, c_int16, c_int32, c_int64, ] _SIZE_TO_INT_TYPE: Dict[int, CSignedIntType] = { item.size: item for item in _EXIST_TYPES } def _get_from_raw(tp) -> CSignedIntType: """ Get the corresponding CSignedIntType instance for a ctypes integer type. :param tp: A ctypes integer type (e.g., ctypes.c_int, ctypes.c_long). :return: The corresponding CSignedIntType instance. :rtype: CSignedIntType This internal function maps ctypes integer types to their corresponding CSignedIntType instances based on size. """ return _SIZE_TO_INT_TYPE[ctypes.sizeof(tp)] c_byte = _get_from_raw(ctypes.c_byte) """ Alias for :data:`c_int8`. This type represents a signed byte, equivalent to a signed 8-bit integer. """ c_short = _get_from_raw(ctypes.c_short) """ Alias for :data:`c_int16`. This type represents a short integer, equivalent to a signed 16-bit integer. """ c_int = _get_from_raw(ctypes.c_int) """ Alias for :data:`c_int32` (in 64-bit OS). .. note:: Size of :data:`c_int` is the same as that in C language, which mainly depends on CPU and OS. On most modern 64-bit systems, this is a 32-bit signed integer. """ c_long = _get_from_raw(ctypes.c_long) """ Alias for :data:`c_int64` (in 64-bit OS). .. note:: Size of :data:`c_long` is the same as that in C language, which mainly depends on CPU and OS. On 64-bit Unix-like systems, this is typically a 64-bit signed integer. On Windows, it may be 32-bit even on 64-bit systems. """ c_longlong = _get_from_raw(ctypes.c_longlong) """ Alias for :data:`c_int64` (in 64-bit OS). .. note:: Size of :data:`c_longlong` is the same as that in C language, which mainly depends on CPU and OS. This is typically a 64-bit signed integer on all modern platforms. """