ElementPad   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Test Coverage

Coverage 93.75%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 91
ccs 30
cts 32
cp 0.9375
rs 10
wmc 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A validate() 0 8 1
A unpack() 0 7 1
A update() 0 10 3
B __init__() 0 30 1
A pack() 0 9 2
A valid() 0 12 1
A make() 0 3 1
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
9
10 1
@register
11 1
class ElementPad(Element):
12
    """
13
    The basic StarStruct element class.
14
    """
15
16 1
    def __init__(self, field, mode=Mode.Native, alignment=1):
17
        """Initialize a StarStruct element object."""
18
19
        # All of the type checks have already been performed by the class
20
        # factory
21
22
        # Pad elements effectively have no name
23 1
        self.name = None
24
25
        # The ref attribute is required for all elements, but the base element
26
        # type does not have one
27 1
        self.ref = None
28
29 1
        self._mode = mode
30
31
        # Strictly speaking, a non-byte aligned message probably wouldn't have
32
        # explicit padding fields, and if it does they probably wouldn't be
33
        # unaligned, but however rare that case might be we need to ensure that
34
        # this field is properly aligned also.
35 1
        self._alignment = alignment
36
37
        # Validate that the format specifiers are valid struct formats, this
38
        # doesn't have to be done now because the format will be checked when
39
        # any struct functions are called, but it's better to inform the user of
40
        # any errors earlier.
41
        # The easiest way to perform this check is to create a "Struct" class
42
        # instance, this will also increase the efficiency of all struct related
43
        # functions called.
44 1
        self.format = mode.value + field[1]
45 1
        self._struct = struct.Struct(self.format)
46
47 1
    @staticmethod
48
    def valid(field):
49
        """
50
        Validation function to determine if a field tuple represents a valid
51
        base element type.
52
53
        The basics have already been validated by the Element factory class,
54
        validate the specific struct format now.
55
        """
56 1
        return len(field) == 2 \
57
            and isinstance(field[1], str) \
58
            and re.match(r'\d*x', field[1])
59
60 1
    def validate(self, msg):
61
        """
62
        Ensure that the supplied message contains the required information for
63
        this element object to operate.
64
65
        The "padding" element requires no further validation.
66
        """
67 1
        pass
68
69 1
    def update(self, mode=None, alignment=None):
70
        """change the mode of the struct format"""
71 1
        if alignment:
72 1
            self._alignment = alignment
73
74 1
        if mode:
75 1
            self._mode = mode
76 1
            self.format = mode.value + self.format[1:]
77
            # recreate the struct with the new format
78 1
            self._struct = struct.Struct(self.format)
79
80 1
    def pack(self, msg):
81
        """Pack the provided values into the supplied buffer."""
82 1
        data = self._struct.pack()
83
84
        # If the data does not meet the alignment, add some padding
85 1
        missing_bytes = len(data) % self._alignment
86 1
        if missing_bytes:
87
            data += b'\x00' * missing_bytes
88 1
        return data
89
90 1
    def unpack(self, msg, buf):
91
        """Unpack data from the supplied buffer using the initialized format."""
92
        # Remember to remove any alignment-based padding
93 1
        extra_bytes = self._alignment - 1 - (struct.calcsize(self.format) %
94
                                             self._alignment)
95 1
        unused = buf[struct.calcsize(self.format) + extra_bytes:]
96 1
        return (None, unused)
97
98 1
    def make(self, msg):
99
        """This shouldn't be called, but if called it returns nothing."""
100
        return None
101