Code Duplication    Length = 96-98 lines in 2 locations

starstruct/elementenum.py 1 location

@@ 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

starstruct/elementbitfield.py 1 location

@@ 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