Source code for bytemaker.conversions.ctypes_
# CType Handling
import ctypes
from ctypes import Array, Structure, Union, _SimpleCData
import bytemaker.typing_redirect as typing_redirect
from bytemaker.bitvector import BitVector
from bytemaker.utils import is_instance_of_union, is_subclass_of_union
CType = typing_redirect.Union[_SimpleCData, Structure, Union, Array]
[docs]
def reverse_bytes_unit(unit: _SimpleCData):
"""
Reverses the byte order of a ctypes object.
Args:
unit (_SimpleCData): The ctypes object to reverse the byte order of.
Returns:
_SimpleCData: The ctypes object with the byte order reversed.
"""
unit_type = type(unit)
bytes_value = bytearray(unit)
bytes_value.reverse()
return unit_type.from_buffer_copy(bytes_value)
[docs]
def reverse_ctype_endianness(ctype_instance: CType) -> CType:
"""
Reverses the endianness of a ctypes object.
Args:
ctype_instance (ctypes._SimpleCData | ctypes.Structure |
ctypes.Union | ctypes.Array):
The ctypes object to reverse the endianness of.
Returns:
ctypes._SimpleCData | ctypes.Structure | ctypes.Union | ctypes.Array:
The ctypes object with the endianness reversed.
"""
if isinstance(ctype_instance, _SimpleCData):
# Reverse the byte order for single, multi-byte objects
byte_size = ctypes.sizeof(ctype_instance)
if byte_size > 1:
ctype_instance = reverse_bytes_unit(ctype_instance)
if isinstance(ctype_instance, Array):
for i in range(len(ctype_instance)):
ctype_instance[i] = reverse_ctype_endianness(ctype_instance[i])
if isinstance(ctype_instance, Structure):
ctype_instance_fields = list(ctype_instance._fields_)
if len(ctype_instance_fields) > 0 and len(ctype_instance_fields[0]) > 2:
raise NotImplementedError(
"ctype structures with _fields_ with more than 2 elements are"
"not supported."
f"Ctype instance: {ctype_instance}"
f"Ctype instance fields: {ctype_instance_fields}"
)
for field_name, field_type in ctype_instance_fields: # type: ignore
field_value = getattr(ctype_instance, field_name)
# print(field_name, field_value, type(field_value))
simple_c_data = field_type(field_value) # type: ignore[reportCallIssue]
assert isinstance(simple_c_data, _SimpleCData)
reversed_unit = reverse_ctype_endianness(simple_c_data)
setattr(ctype_instance, field_name, reversed_unit)
return ctype_instance
[docs]
def ctype_to_bytes(ctype_obj: CType, reverse_endianness=True) -> bytes:
"""
Function to convert ctypes into bytes objects
Args:
ctype_obj (ctypes._SimpleCData | ctypes.Structure |
ctypes.Union | ctypes.Array):
The ctypes object to convert to bytes
reverse_endianness (bool, optional):
Whether to reverse the endianness of the bytes after
converting. Defaults to False.
Returns:
bytes: The bytes representation of the ctypes object
"""
if not is_instance_of_union(ctype_obj, CType): # type: ignore
raise TypeError(
f"ctype_to_bytes only accepts _SimpleCData, Structure,"
f"Union, and Array objects, not {type(ctype_obj)}."
)
if reverse_endianness:
ctype_obj = reverse_ctype_endianness(ctype_obj)
retbytes = bytes(ctype_obj)
return retbytes
[docs]
def ctype_to_bits(ctype_obj: CType, reverse_endianness=True) -> BitVector:
"""
Function to convert ctypes into BitVector objects
Args:
ctype_obj (ctypes._SimpleCData | ctypes.Structure\
| ctypes.Union | ctypes.Array):
The ctypes object to convert to BitVector
reverse_endianness (bool, optional):
Whether to reverse the endianness of the BitVector\
after converting. Defaults to False.
Returns:
BitVector: The BitVector representation of the ctypes object
"""
return BitVector(ctype_to_bytes(ctype_obj, reverse_endianness=reverse_endianness))
[docs]
def bytes_to_ctype(
bytes_obj: bytes, ctype_type: type, reverse_endianness=True
) -> CType:
"""
Function to convert bytes into ctypes objects
Args:
bytes_obj (bytes): The bytes object to convert to a ctypes object
ctype_type (type): The type of the ctypes object to convert to.
Must be a member of CType
reverse_endianness (bool, optional): Whether to reverse the
endianness of the bytes before converting. Defaults to False.
Returns:
ctypes._SimpleCData | ctypes.Structure | ctypes.Union
| ctypes.Array:
The ctypes object representation of the bytes
"""
if not is_subclass_of_union(ctype_type, CType):
raise TypeError(
f"bytes_to_ctype only accepts _SimpleCData, Structure,"
f"Union, and Array types, not {ctype_type}."
)
ctype_obj = ctype_type.from_buffer_copy(bytes_obj)
if reverse_endianness:
ctype_obj = reverse_ctype_endianness(ctype_obj)
return ctype_obj
[docs]
def bits_to_ctype(
bits_obj: BitVector, ctype_type: type, reverse_endianness=True
) -> CType:
"""
Function to convert bits into ctypes objects
Args:
bits_obj (BitVector): The bits object to convert to a ctypes object
ctype_type (type): The type of the ctypes object to convert to.
Must be a member of CType
reverse_endianness (bool, optional): Whether to reverse the
endianness of the bits before converting. Defaults to False.
Returns:
ctypes._SimpleCData | ctypes.Structure | ctypes.Union
| ctypes.Array:
The ctypes object representation of the bits
"""
return bytes_to_ctype(
bits_obj.to_bytes(), ctype_type, reverse_endianness=reverse_endianness
)