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