2026-07-10 10:38:57 +02:00

247 lines
6.7 KiB
Python

import binascii
import struct
def str_to_time(t):
"""
Converts a given time to seconds.
Supported textual representations:
- u -> microseconds
- m -> milliseconds
- s -> seconds
- M -> minutes
- H -> hours
Example: 10M is converted to 600 seconds.
:param t: time in seconds or in textual representation
"""
conversions = {'u': 0.000001, 'm': 0.001, 's': 1, 'M': 60, 'H': 3600, 'D': 86400}
if isinstance(t, str):
try:
if t[-1] not in conversions.keys():
return float(t)
# extract multiplier
mult = conversions[t[-1]]
if mult == 's':
return str_to_time(t[0:-1])
return float(t[0:-1]) * mult
except BaseException:
raise Exception(f"[PhysicalMemoryArray] Invalid value for '{t[-1]}' for t {t}")
return float(t)
def check_data(data, bits):
"""
Checks if data converted to binary corresponds to the given bits
:returns: is_match, data_value, bits_value
Note: data_value and bits_value are None if is_match is True
"""
n_bits = len(bits)
val = to_binary(data, n_bits)
# Match
if val == bits:
return True, None, None
else:
data_val = data
bits_val = None
if data is None or type(data) is int:
bits_val = from_binary(bits, int)
elif type(data) is float:
bits_val = from_binary(bits, float)
elif type(data) is str:
if len(data) == 1:
bits_val = from_binary(bits, float)
# Memory pointer
elif '-0x' in data:
addr_prefix = f"{data.split('-0x')[0]}-"
addr_bits = from_binary(bits, int)
bits_val = f"{addr_prefix}{addr_bits}"
# CRC - pointer (function pointer, saved PC)
else:
data_val = calc_crc(data, n_bits)
bits_val = from_binary(bits, int)
elif type(data) is dict:
# saved PC (address)
if 'function_name' in data and 'instruction_number' in data:
pc = f"{data['function_name']}-{data['instruction_number']}"
data_val = calc_crc(pc, n_bits)
bits_val = from_binary(bits, int)
if bits_val is None:
raise Exception(f"Unsupported data check vs {type(data)}")
return False, data_val, bits_val
def from_binary(data, conv_type):
"""
Converts data from binary to conv_type
:param data: data to convert
:param conv_type: target type of the conversion
:return: converted data
"""
# signed integer
if conv_type is int:
# positive
if data[0] == '0' or len(data) == 1:
return int(data, 2)
# negative
return int(data[1:], 2) - (2 ** (len(data) - 1))
# char
elif conv_type is str:
return chr(int(data, 2))
# float / double
elif conv_type is float:
n_bits = len(data)
if n_bits != 32 and n_bits != 64:
raise Exception(f"Binary to floating point conversion of {n_bits} not supported!")
# convert to bytes
data = int(data, 2).to_bytes(n_bits//8, byteorder='big')
pack_type = '>f' if n_bits == 32 else '>d'
return struct.unpack(pack_type, data)[0]
# bool
elif conv_type is bool:
return data[-1] == '1'
def to_binary(data, n_bits):
"""
Converts data to its binary notation, using n_bits
:param data: data to convert
:param n_bits: number of bits to use in the representation
:return: binary data
"""
if data is None:
return ''.zfill(n_bits)
elif type(data) is int:
format_str = '{:0' + str(n_bits) + 'b}'
# bit mask (used for negative integers)
mask = int('1' * n_bits, 2)
val = format_str.format(data & mask)
# fix bit size
return val[-n_bits:]
elif type(data) is float:
if n_bits != 32 and n_bits != 64:
raise Exception(f"Floating point conversion of {n_bits} not supported!")
pack_type = '>f' if n_bits == 32 else '>d'
return ''.join(f"{b:08b}" for b in struct.pack(pack_type, data))
elif type(data) is str:
# char
if len(data) == 1:
val = bin(ord(data))[2:].zfill(n_bits)
# fix bit size for unicode vals
return val[-n_bits:]
# memory pointer (get address)
elif '-0x' in data:
addr = f"0x{data.split('-0x')[1]}"
data = int(addr, 16)
return to_binary(data, n_bits)
# other pointer (function pointer, saved PC)
else:
val = calc_crc(data, n_bits)
# use int implementation
return to_binary(val, n_bits)
elif type(data) is dict:
# saved PC (address)
if 'function_name' in data and 'instruction_number' in data:
pc = f"{data['function_name']}-{data['instruction_number']}"
# use address implementation
return to_binary(pc, n_bits)
elif type(data) is bool:
val = '1' if data else '0'
return val.zfill(n_bits)
raise Exception(f"Data type {type(data)} not supported!")
def calc_crc(data, n_bits):
"""
Computes the CRC of the data
:param data: data to convert
:param n_bits: number of bits for CRC (supported CRC16, CRC32, and CRC64)
:return: CRC in integer representation
"""
crc_version = {8: crc8, 16: crc16, 32: binascii.crc32, 64: crc64}
if n_bits in crc_version.keys():
return crc_version[n_bits](bytes(data, 'utf-8'))
raise Exception(f"CRC size of {n_bits} not supported! (input={data})")
def crc8(data, poly=0x07, init=0x00):
"""
Compute the CRC-8-ATM checksum.
"""
crc = init
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc = (crc << 1) ^ poly
else:
crc <<= 1
crc &= 0xFF # Ensure 8-bit value
return crc
def crc16(data, poly=0x1021, init=0x0000):
"""
Computes CRC-16-CCITT (XModem) checksum
"""
crc = init
for byte in data:
crc ^= (byte << 8)
for _ in range(8):
crc = (crc << 1) ^ poly if (crc & 0x8000) else (crc << 1)
crc &= 0xFFFF # Keep it 16-bit
return crc
def crc64(data, poly=0x000000000000001B, init=0xFFFFFFFFFFFFFFFF):
"""
Computes CRC-64-ISO without a lookup table (bitwise).
"""
crc = init
for byte in data:
crc ^= byte << 56 # Align byte with the leftmost 8 bits of CRC
for _ in range(8): # Process each bit
crc = (crc << 1) ^ poly if (crc & (1 << 63)) else (crc << 1)
crc &= 0xFFFFFFFFFFFFFFFF # Keep it 64-bit
return crc