ElementBitField.update()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 8.6667

Importance

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