Skip to content

eyepy.io.utils

find_float(bytestring, value, endian=None, bits=None, rtol=1e-05, atol=1e-08)

Find all occurrences of a float in a byte string.

Parameters:

Name Type Description Default
bytestring bytes

The byte string to search.

required
value float

The float to search for.

required
endian Optional[str]

"l" for little and "b" for big, the endianness of the float. If not specified, the endianness is assumed to be the same as the endianness of the system.

None
bits Optional[Union[int, list[int], str, list[str]]]

The number of bits in the float. If not specified, 16, 32 and 64 bit floats are searched for.

None
rtol float

The relative tolerance parameter for matching a value (see numpy.isclose).

1e-05
atol float

The absolute tolerance parameter for matching a value (see numpy.isclose).

1e-08

Returns:

Type Description
dict[str, list[int]]

A dictionary where the key is the type and the value, a list of offsets for which the searched value was found

Source code in src/eyepy/io/utils.py
def find_float(bytestring: bytes,
               value: float,
               endian: Optional[str] = None,
               bits: Optional[Union[int, list[int], str, list[str]]] = None,
               rtol: float = 1e-05,
               atol: float = 1e-08) -> dict[str, list[int]]:
    """Find all occurrences of a float in a byte string.

    Args:
        bytestring: The byte string to search.
        value: The float to search for.
        endian: "l" for little and "b" for big, the endianness of the float.
            If not specified, the endianness is assumed to be the same as the endianness of the system.
        bits: The number of bits in the float. If not specified, 16, 32 and 64
            bit floats are searched for.
        rtol: The relative tolerance parameter for matching a value (see numpy.isclose).
        atol: The absolute tolerance parameter for matching a value (see numpy.isclose).

    Returns:
        A dictionary where the key is the type and the value, a list of offsets for which the searched value was found
    """

    # construct format strings
    if endian is None:
        endian = sys.byteorder[0]  # first letter of endianness
    if bits is None:
        bits = ['16', '32', '64']
    elif isinstance(bits, int):
        bits = [str(bits)]
    elif isinstance(bits, str):
        bits = [bits]
    elif isinstance(bits, list):
        bits = [str(b) for b in bits]

    # Build a list of all format strings
    formats = [(int(b), f'Float{b}{endian}') for b in bits]

    # find all occurrences
    results = defaultdict(list)
    for bts, fmt_string in formats:
        format = getattr(cs, fmt_string)

        # Parse the bytestring with multiple byte offsets depending on the format
        for offset in range(bts // 8):
            # Calculate the number of items that can be parsed
            count = (len(bytestring) - offset) // (bts // 8)
            if count <= 0:
                continue
            data = np.array(cs.Array(count, format).parse(bytestring[offset:]))

            # Find all occurrences for this format
            hits = np.nonzero(np.isclose(data, value, rtol, atol))
            res = [(pos * (bts // 8)) + offset + 1 for pos in hits[0]]
            if res:
                results[fmt_string] += res

    results = {**results}
    return results

find_int(bytestring, value, signed=None, endian=None, bits=None, rtol=1e-05, atol=1e-08)

Find all occurrences of an integer in a byte string.

Parameters:

Name Type Description Default
bytestring bytes

The byte string to search.

required
value int

The integer to search for.

required
signed Optional[Union[bool, str, list[str]]]

Whether the integer is signed or not. If not specified, the integer is assumed to be signed if it is negative, otherwise signed and unsigned are searched for.

None
endian Optional[str]

"l" for little and "b" for big, the endianness of the integer. If not specified, the endianness is assumed to be the same as the endianness of the system.

None
bits Optional[Union[int, list[int], str, list[str]]]

The number of bits in the integer. If not specified, 8, 16, 24, 32, and 64 bit integers are searched for.

None
rtol float

The relative tolerance parameter for matching a value (see numpy.isclose).

1e-05
atol float

The absolute tolerance parameter for matching a value (see numpy.isclose).

1e-08

Returns:

Type Description
dict[str, list[int]]

A dictionary where the key is the type and the value, a list of offsets for which the searched value was found

Source code in src/eyepy/io/utils.py
def find_int(bytestring: bytes,
             value: int,
             signed: Optional[Union[bool, str, list[str]]] = None,
             endian: Optional[str] = None,
             bits: Optional[Union[int, list[int], str, list[str]]] = None,
             rtol: float = 1e-05,
             atol: float = 1e-08) -> dict[str, list[int]]:
    """Find all occurrences of an integer in a byte string.

    Args:
        bytestring: The byte string to search.
        value: The integer to search for.
        signed: Whether the integer is signed or not. If not specified, the
            integer is assumed to be signed if it is negative, otherwise signed and unsigned are searched for.
        endian: "l" for little and "b" for big, the endianness of the integer.
            If not specified, the endianness is assumed to be the same as the endianness of the system.
        bits: The number of bits in the integer. If not specified, 8, 16, 24, 32, and 64
            bit integers are searched for.
        rtol: The relative tolerance parameter for matching a value (see numpy.isclose).
        atol: The absolute tolerance parameter for matching a value (see numpy.isclose).

    Returns:
        A dictionary where the key is the type and the value, a list of offsets for which the searched value was found
    """

    # construct format strings
    if signed is None:
        signed = ['s'] if value < 0 else ['s', 'u']
    elif isinstance(signed, bool):
        signed = ['s'] if signed else ['u']
    elif isinstance(signed, str):
        signed = [signed]
    if endian is None:
        endian = sys.byteorder[0]  # first letter of endianness
    if bits is None:
        bits = ['8', '16', '24', '32', '64']
    elif isinstance(bits, int):
        bits = [str(bits)]
    elif isinstance(bits, str):
        bits = [bits]
    elif isinstance(bits, list):
        bits = [str(b) for b in bits]

    # Build a list of all format strings
    formats = [(int(b), f'Int{b}{s}{endian}') for b in bits for s in signed]

    # find all occurrences
    results = defaultdict(list)
    for bts, fmt_string in formats:
        format = getattr(cs, fmt_string)

        # Parse the bytestring with multiple byte offsets depending on the format
        for offset in range(bts // 8):

            # Calculate the number of items that can be parsed
            count = (len(bytestring) - offset) // (bts // 8)
            if count <= 0:
                continue
            data = np.array(cs.Array(count, format).parse(bytestring[offset:]))

            # Find all occurrences
            hits = np.nonzero(np.isclose(data, value, rtol=rtol, atol=atol))
            res = [(pos * (bts // 8)) + offset + 1 for pos in hits[0]]
            if res:
                results[fmt_string] += res

    results = {**results}
    return results