Source code for scrutiny.core.basic_types

#    basic_types.py
#        Contains the basic types used project-wides
#
#   - License : MIT - See LICENSE file
#   - Project : Scrutiny Debugger (github.com/scrutinydebugger/scrutiny-main)
#
#    Copyright (c) 2022 Scrutiny Debugger

__all__ = [
    'Endianness',
    'DataTypeType',
    'DataTypeSize',
    'EmbeddedDataType',
    'RuntimePublishedValue',
    'MemoryRegion',
    'WatchableType',
    'ServerDatastoreContentType',
]

from enum import Enum
from dataclasses import dataclass
from scrutiny.tools import validation
from scrutiny.tools.typing import *


[docs] @dataclass(frozen=True, slots=True) class MemoryRegion: """(Immutable struct) Represent a memory region spanning from ``start`` to ``start+size-1`` inclusively""" start: int """Start address of the region""" size: int """Size in bytes of the region""" def __post_init__(self) -> None: validation.assert_int_range(self.start, 'start', minval=0) validation.assert_int_range(self.size, 'size', minval=0) def touches(self, other: "MemoryRegion") -> bool: if self.size <= 0 or other.size <= 0: return False if self.start >= other.start + other.size: return False if other.start >= self.start + self.size: return False return True @property def end(self) -> int: return max(self.start, self.start + self.size - 1)
[docs] class Endianness(Enum): """(Enum) Represent an data storage endianness""" Little = 0 """Little endian. 0x12345678 is stored as 78 56 34 12 """ Big = 1 """Big endian. 0x12345678 is stored as 12 34 56 78 """ def to_str(self) -> str: if self == Endianness.Little: return 'little' elif self == Endianness.Big: return 'big' else: raise ValueError(f"Unsupported endianness {self}") @classmethod def from_str(cls, s: str) -> "Endianness": s = s.lower().strip() if s == 'little': return Endianness.Little if s == 'big': return Endianness.Big raise ValueError(f"Invalid endianness {s}")
class DataTypeType(Enum): _sint = (0 << 4) _uint = (1 << 4) _float = (2 << 4) _bool = (3 << 4) _cfloat = (4 << 4) _struct = (5 << 4) _pointer = (6 << 4) _NA = (0xF << 4) class DataTypeSize(Enum): _8 = 0 _16 = 1 _32 = 2 _64 = 3 _128 = 4 _256 = 5 _NA = 0xF
[docs] class EmbeddedDataType(Enum): """ (Enum) Represent a datatype that can be read from a device. The embedded library has the same definition of datatypes as this one. They needs to match. Not all datatype are supported. (cfloat or >64 bits) """ sint8 = DataTypeType._sint.value | DataTypeSize._8.value sint16 = DataTypeType._sint.value | DataTypeSize._16.value sint32 = DataTypeType._sint.value | DataTypeSize._32.value sint64 = DataTypeType._sint.value | DataTypeSize._64.value sint128 = DataTypeType._sint.value | DataTypeSize._128.value sint256 = DataTypeType._sint.value | DataTypeSize._256.value uint8 = DataTypeType._uint.value | DataTypeSize._8.value uint16 = DataTypeType._uint.value | DataTypeSize._16.value uint32 = DataTypeType._uint.value | DataTypeSize._32.value uint64 = DataTypeType._uint.value | DataTypeSize._64.value uint128 = DataTypeType._uint.value | DataTypeSize._128.value uint256 = DataTypeType._uint.value | DataTypeSize._256.value float8 = DataTypeType._float.value | DataTypeSize._8.value float16 = DataTypeType._float.value | DataTypeSize._16.value float32 = DataTypeType._float.value | DataTypeSize._32.value float64 = DataTypeType._float.value | DataTypeSize._64.value float128 = DataTypeType._float.value | DataTypeSize._128.value float256 = DataTypeType._float.value | DataTypeSize._256.value cfloat8 = DataTypeType._cfloat.value | DataTypeSize._8.value cfloat16 = DataTypeType._cfloat.value | DataTypeSize._16.value cfloat32 = DataTypeType._cfloat.value | DataTypeSize._32.value cfloat64 = DataTypeType._cfloat.value | DataTypeSize._64.value cfloat128 = DataTypeType._cfloat.value | DataTypeSize._128.value cfloat256 = DataTypeType._cfloat.value | DataTypeSize._256.value ptr8 = DataTypeType._pointer.value | DataTypeSize._8.value ptr16 = DataTypeType._pointer.value | DataTypeSize._16.value ptr32 = DataTypeType._pointer.value | DataTypeSize._32.value ptr64 = DataTypeType._pointer.value | DataTypeSize._64.value ptr128 = DataTypeType._pointer.value | DataTypeSize._128.value ptr256 = DataTypeType._pointer.value | DataTypeSize._256.value bool8 = DataTypeType._bool.value | DataTypeSize._8.value bool16 = DataTypeType._bool.value | DataTypeSize._16.value bool32 = DataTypeType._bool.value | DataTypeSize._32.value bool64 = DataTypeType._bool.value | DataTypeSize._64.value bool128 = DataTypeType._bool.value | DataTypeSize._128.value bool256 = DataTypeType._bool.value | DataTypeSize._256.value struct = DataTypeType._struct.value | DataTypeSize._NA.value NA = DataTypeType._NA.value | DataTypeSize._NA.value
[docs] def get_size_bit(self) -> int: """Return the size of the datatype in bits. Returns 0 if NA""" v = self.get_size_8bits() return v * 8
[docs] def get_size_8bits(self) -> int: """Return the size of the datatype in 8bits bytes. Returns 0 if NA""" vbytes = (self.value & 0xF) if DataTypeSize(vbytes) == DataTypeSize._NA: return 0 return 1 << vbytes
[docs] def is_integer(self) -> bool: """Tells if the datatype is an integer type (sint or uint)""" type_type = self.value & 0xF0 if type_type in (DataTypeType._sint.value, DataTypeType._uint.value): return True return False
[docs] def is_float(self) -> bool: """Tells if a datatype is a floating point value (float)""" type_type = self.value & 0xF0 # Cfloat??? if type_type in (DataTypeType._float.value, DataTypeType._cfloat.value): return True return False
[docs] def is_signed(self) -> bool: """Tells if the datatype support a sign (sint, float, cfloat)""" type_type = self.value & 0xF0 if type_type in (DataTypeType._sint.value, DataTypeType._float.value, DataTypeType._cfloat.value): return True return False
[docs] def is_pointer(self) -> bool: """Tells if the type is a pointer type (independent of the size)""" type_type = self.value & 0xF0 return type_type == DataTypeType._pointer.value
[docs] def is_bool(self) -> bool: type_type = self.value & 0xF0 return type_type == DataTypeType._bool.value
@classmethod def make(cls, datatype_type: DataTypeType, size: Union[int, DataTypeSize]) -> "EmbeddedDataType": if isinstance(size, int): if size == 1: return cls.make(datatype_type, DataTypeSize._8) if size == 2: return cls.make(datatype_type, DataTypeSize._16) if size == 4: return cls.make(datatype_type, DataTypeSize._32) if size == 8: return cls.make(datatype_type, DataTypeSize._64) if size == 16: return cls.make(datatype_type, DataTypeSize._128) if size == 32: return cls.make(datatype_type, DataTypeSize._256) raise ValueError(f"Impossible size given {size}") else: return cls(datatype_type.value | size.value)
@dataclass(frozen=True, slots=True) class RuntimePublishedValue: """ (Immutable struct) A Runtime Published Value (RPV) is one of the basic elements that can be read from a target device. RPVs are defined in the embedded code and known by the server by polling the device. They don't have a name, they are identified by a 16bits identifier. The user can add an Alias on a RPV to assign them a name """ id: int """RPV ID (16bits)""" datatype: EmbeddedDataType """The data type of the value""" def __post_init__(self) -> None: validation.assert_int_range(self.id, 'id', 0, 0xFFFF) validation.assert_type(self.datatype, 'datatype', EmbeddedDataType) def __repr__(self) -> str: return "<%s: 0x%x (%s) at 0x%016x>" % (self.__class__.__name__, self.id, self.datatype.name, id(self))
[docs] class WatchableType(str, Enum): """(Enum) Type of watchable available on the server""" Variable = 'var' """A variable found in the device firmware debug symbols""" RuntimePublishedValue = 'rpv' """A readable/writable element identified by a 16bits ID. Explicitly defined in the device firmware source code""" Alias = 'alias' """A symbolic link watchable that can refers to a :attr:`Variable` or a :attr:`RuntimePublishedValue`"""
[docs] @classmethod def all(cls) -> List["WatchableType"]: """Return the list of valid Watchable types. Mainly for unit testing""" return [cls.Variable, cls.RuntimePublishedValue, cls.Alias]
def __str__(self) -> str: return self.value def to_str(self) -> str: return str(self) @classmethod def from_str(cls, v: str) -> "WatchableType": return WatchableType(v)
[docs] class ServerDatastoreContentType(str, Enum): """(Enum) Type of items that can be downloaded from the server. This enum contains all watchable types + some special items. """ Variable = 'var' """A variable found in the device firmware debug symbols.""" RuntimePublishedValue = 'rpv' """A readable/writable element identified by a 16bits ID. Explicitly defined in the device firmware source code""" Alias = 'alias' """A symbolic link watchable that can refers to a :attr:`Variable` or a :attr:`RuntimePublishedValue`""" VariableFactory = 'var_factory' """A data structure containing a pattern that can generate variables. Mainly used for array instantiation"""
[docs] @classmethod def all(cls) -> List["ServerDatastoreContentType"]: """Return the list of valid items. Mainly for unit testing""" return [cls.Variable, cls.RuntimePublishedValue, cls.Alias, cls.VariableFactory]
def __str__(self) -> str: return self.value def to_str(self) -> str: return str(self) @classmethod def from_str(cls, v: str) -> "ServerDatastoreContentType": return ServerDatastoreContentType(v)