| @@ 11-108 (lines=98) @@ | ||
| 8 | from starstruct.modes import Mode |
|
| 9 | ||
| 10 | ||
| 11 | @register |
|
| 12 | class ElementEnum(Element): |
|
| 13 | """ |
|
| 14 | The enumeration StarStruct element class. |
|
| 15 | """ |
|
| 16 | ||
| 17 | def __init__(self, field, mode=Mode.Native, alignment=1): |
|
| 18 | """Initialize a StarStruct element object.""" |
|
| 19 | ||
| 20 | # All of the type checks have already been performed by the class |
|
| 21 | # factory |
|
| 22 | self.name = field[0] |
|
| 23 | self.ref = field[2] |
|
| 24 | ||
| 25 | self._mode = mode |
|
| 26 | self._alignment = alignment |
|
| 27 | ||
| 28 | # Validate that the format specifiers are valid struct formats, this |
|
| 29 | # doesn't have to be done now because the format will be checked when |
|
| 30 | # any struct functions are called, but it's better to inform the user of |
|
| 31 | # any errors earlier. |
|
| 32 | # The easiest way to perform this check is to create a "Struct" class |
|
| 33 | # instance, this will also increase the efficiency of all struct related |
|
| 34 | # functions called. |
|
| 35 | self.format = mode.value + field[1] |
|
| 36 | self._struct = struct.Struct(self.format) |
|
| 37 | ||
| 38 | @staticmethod |
|
| 39 | def valid(field): |
|
| 40 | """ |
|
| 41 | Validation function to determine if a field tuple represents a valid |
|
| 42 | enum element type. |
|
| 43 | ||
| 44 | The basics have already been validated by the Element factory class, |
|
| 45 | validate that the struct format is a valid numeric or string value. |
|
| 46 | """ |
|
| 47 | return (len(field) == 3 and |
|
| 48 | isinstance(field[1], str) and |
|
| 49 | re.match(r'\d*[cbB?hHiIlLqQnNfdP]|\d*[sp]', field[1]) and |
|
| 50 | issubclass(field[2], enum.Enum)) |
|
| 51 | ||
| 52 | def validate(self, msg): |
|
| 53 | """ |
|
| 54 | Ensure that the supplied message contains the required information for |
|
| 55 | this element object to operate. |
|
| 56 | ||
| 57 | The "enum" element requires no further validation. |
|
| 58 | """ |
|
| 59 | pass |
|
| 60 | ||
| 61 | def update(self, mode=None, alignment=None): |
|
| 62 | """change the mode of the struct format""" |
|
| 63 | if alignment: |
|
| 64 | self._alignment = alignment |
|
| 65 | ||
| 66 | if mode: |
|
| 67 | self._mode = mode |
|
| 68 | self.format = mode.value + self.format[1:] |
|
| 69 | # recreate the struct with the new format |
|
| 70 | self._struct = struct.Struct(self.format) |
|
| 71 | ||
| 72 | def pack(self, msg): |
|
| 73 | """Pack the provided values into the supplied buffer.""" |
|
| 74 | # The value to pack could be a raw value, or an enum value, first |
|
| 75 | # ensure that the value provided is a valid value for the referenced |
|
| 76 | # enum class. |
|
| 77 | enum_val = self.ref(msg[self.name]) |
|
| 78 | data = self._struct.pack(enum_val.value) |
|
| 79 | ||
| 80 | # If the data does not meet the alignment, add some padding |
|
| 81 | missing_bytes = len(data) % self._alignment |
|
| 82 | if missing_bytes: |
|
| 83 | data += b'\x00' * missing_bytes |
|
| 84 | return data |
|
| 85 | ||
| 86 | def unpack(self, msg, buf): |
|
| 87 | """Unpack data from the supplied buffer using the initialized format.""" |
|
| 88 | ret = self._struct.unpack_from(buf, 0) |
|
| 89 | ||
| 90 | # Remember to remove any alignment-based padding |
|
| 91 | extra_bytes = self._alignment - 1 - (struct.calcsize(self.format) % |
|
| 92 | self._alignment) |
|
| 93 | unused = buf[struct.calcsize(self.format) + extra_bytes:] |
|
| 94 | ||
| 95 | # Convert the returned value to the referenced Enum type |
|
| 96 | try: |
|
| 97 | member = self.ref(ret[0]) |
|
| 98 | except ValueError as e: |
|
| 99 | raise ValueError( |
|
| 100 | 'Value: {0} was not valid for {1}\n\twith msg: {2},\n\tbuf: {3}'.format( |
|
| 101 | ret[0], self.ref, msg, buf |
|
| 102 | )).with_traceback(e.__traceback__) |
|
| 103 | ||
| 104 | return (member, unused) |
|
| 105 | ||
| 106 | def make(self, msg): |
|
| 107 | """Return the "transformed" value for this element""" |
|
| 108 | return self.ref(msg[self.name]) |
|
| 109 | ||
| @@ 11-106 (lines=96) @@ | ||
| 8 | from starstruct.bitfield import BitField |
|
| 9 | ||
| 10 | ||
| 11 | @register |
|
| 12 | class ElementBitField(Element): |
|
| 13 | """ |
|
| 14 | The bitfield StarStruct element class. |
|
| 15 | """ |
|
| 16 | ||
| 17 | def __init__(self, field, mode=Mode.Native, alignment=1): |
|
| 18 | """Initialize a StarStruct element object.""" |
|
| 19 | ||
| 20 | # All of the type checks have already been performed by the class |
|
| 21 | # factory |
|
| 22 | self.name = field[0] |
|
| 23 | self.ref = field[2] |
|
| 24 | ||
| 25 | self._mode = mode |
|
| 26 | self._alignment = alignment |
|
| 27 | ||
| 28 | # Validate that the format specifiers are valid struct formats, this |
|
| 29 | # doesn't have to be done now because the format will be checked when |
|
| 30 | # any struct functions are called, but it's better to inform the user of |
|
| 31 | # any errors earlier. |
|
| 32 | # The easiest way to perform this check is to create a "Struct" class |
|
| 33 | # instance, this will also increase the efficiency of all struct related |
|
| 34 | # functions called. |
|
| 35 | self.format = mode.value + field[1] |
|
| 36 | self._struct = struct.Struct(self.format) |
|
| 37 | ||
| 38 | @staticmethod |
|
| 39 | def valid(field): |
|
| 40 | """ |
|
| 41 | Validation function to determine if a field tuple represents a valid |
|
| 42 | enum element type. |
|
| 43 | ||
| 44 | The basics have already been validated by the Element factory class, |
|
| 45 | validate that the struct format is a valid numeric value. |
|
| 46 | """ |
|
| 47 | return (len(field) == 3 and |
|
| 48 | isinstance(field[1], str) and |
|
| 49 | re.match(r'\d*[cbB?hHiIlLqQnNfdP]', field[1]) and |
|
| 50 | isinstance(field[2], BitField)) |
|
| 51 | ||
| 52 | def validate(self, msg): |
|
| 53 | """ |
|
| 54 | Ensure that the supplied message contains the required information for |
|
| 55 | this element object to operate. |
|
| 56 | ||
| 57 | The "enum" element requires no further validation. |
|
| 58 | """ |
|
| 59 | pass |
|
| 60 | ||
| 61 | def update(self, mode=None, alignment=None): |
|
| 62 | """change the mode of the struct format""" |
|
| 63 | if alignment: |
|
| 64 | self._alignment = alignment |
|
| 65 | ||
| 66 | if mode: |
|
| 67 | self._mode = mode |
|
| 68 | self.format = mode.value + self.format[1:] |
|
| 69 | # recreate the struct with the new format |
|
| 70 | self._struct = struct.Struct(self.format) |
|
| 71 | ||
| 72 | def pack(self, msg): |
|
| 73 | """Pack the provided values into the supplied buffer.""" |
|
| 74 | # Turn the enum value list into a single number and pack it into the |
|
| 75 | # specified format |
|
| 76 | data = self._struct.pack(self.ref.pack(msg[self.name])) |
|
| 77 | ||
| 78 | # If the data does not meet the alignment, add some padding |
|
| 79 | missing_bytes = len(data) % self._alignment |
|
| 80 | if missing_bytes: |
|
| 81 | data += b'\x00' * missing_bytes |
|
| 82 | return data |
|
| 83 | ||
| 84 | def unpack(self, msg, buf): |
|
| 85 | """Unpack data from the supplied buffer using the initialized format.""" |
|
| 86 | ret = self._struct.unpack_from(buf, 0) |
|
| 87 | ||
| 88 | # Remember to remove any alignment-based padding |
|
| 89 | extra_bytes = self._alignment - 1 - (struct.calcsize(self.format) % |
|
| 90 | self._alignment) |
|
| 91 | unused = buf[struct.calcsize(self.format) + extra_bytes:] |
|
| 92 | ||
| 93 | # Convert the returned value to the referenced BitField type |
|
| 94 | try: |
|
| 95 | member = self.ref.unpack(ret[0]) |
|
| 96 | except ValueError as e: |
|
| 97 | raise ValueError( |
|
| 98 | 'Value: {0} was not valid for {1}\n\twith msg: {2},\n\tbuf: {3}'.format( |
|
| 99 | ret[0], self.ref, msg, buf |
|
| 100 | )).with_traceback(e.__traceback__) |
|
| 101 | ||
| 102 | return (member, unused) |
|
| 103 | ||
| 104 | def make(self, msg): |
|
| 105 | """Return the "transformed" value for this element""" |
|
| 106 | return self.ref.make(msg[self.name]) |
|
| 107 | ||