• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from typing import Optional, List, Dict, Union, Tuple, Set
16from .ast import *
17
18
19def desugar_field_(field: Field, previous: Field, constraints: Dict[str, Constraint]) -> List[Field]:
20    """Inline group and constrained fields.
21    Constrained fields are transformed into fixed fields.
22    Group fields are inlined and recursively desugared."""
23
24    if isinstance(field, ScalarField) and field.id in constraints:
25        value = constraints[field.id].value
26        fixed = FixedField(kind='fixed_field', loc=field.loc, width=field.width, value=value)
27        fixed.parent = field.parent
28        return [fixed]
29
30    elif isinstance(field, PaddingField):
31        previous.padded_size = field.size
32        field.padded_field = previous
33        return [field]
34
35    elif isinstance(field, TypedefField) and field.id in constraints:
36        tag_id = constraints[field.id].tag_id
37        fixed = FixedField(kind='fixed_field', loc=field.loc, enum_id=field.type_id, tag_id=tag_id)
38        fixed.parent = field.parent
39        return [fixed]
40
41    elif isinstance(field, GroupField):
42        group = field.parent.file.group_scope[field.group_id]
43        constraints = dict([(c.id, c) for c in field.constraints])
44        fields = []
45        for f in group.fields:
46            fields.extend(desugar_field_(f, previous, constraints))
47            previous = f
48        return fields
49
50    else:
51        return [field]
52
53
54def desugar(file: File):
55    """Inline group fields.
56    Constrained fields are transformed into fixed fields.
57    Group declarations are removed from the file object.
58    **The original file object is modified inline.**"""
59
60    declarations = []
61    for d in file.declarations:
62        if isinstance(d, GroupDeclaration):
63            continue
64
65        if isinstance(d, (PacketDeclaration, StructDeclaration)):
66            fields = []
67            for f in d.fields:
68                fields.extend(desugar_field_(f, fields[-1] if len(fields) > 0 else None, {}))
69            d.fields = fields
70
71        declarations.append(d)
72
73    file.declarations = declarations
74    file.group_scope = {}
75
76
77def make_reserved_field(width: int) -> ReservedField:
78    """Create a reserved field of specified width."""
79    return ReservedField(kind='reserved_field', loc=None, width=width)
80
81
82def get_packet_field(packet: Union[PacketDeclaration, StructDeclaration], id: str) -> Optional[Field]:
83    """Return the field with selected identifier declared in the provided
84    packet or its ancestors."""
85    id = '_payload_' if id == 'payload' else id
86    for f in packet.fields:
87        if getattr(f, 'id', None) == id:
88            return f
89    if isinstance(packet, PacketDeclaration) and packet.parent_id:
90        parent = packet.file.packet_scope[packet.parent_id]
91        return get_packet_field(parent, id)
92    elif isinstance(packet, StructDeclaration) and packet.parent_id:
93        parent = packet.file.typedef_scope[packet.parent_id]
94        return get_packet_field(parent, id)
95    else:
96        return None
97
98
99def get_packet_fields(decl: Union[PacketDeclaration, StructDeclaration]) -> List[Field]:
100    """Return the list of fields declared in the selected packet and its parents.
101    Payload fields are removed from the parent declarations."""
102
103    fields = []
104    if decl.parent:
105        fields = [f for f in get_packet_fields(decl.parent) if not isinstance(f, (PayloadField, BodyField))]
106    return fields + decl.fields
107
108
109def get_packet_shift(packet: Union[PacketDeclaration, StructDeclaration]) -> int:
110    """Return the bit shift of the payload or body field in the parent packet.
111
112    When using packet derivation on bit fields, the body may be shifted.
113    The shift is handled statically in the implementation of child packets,
114    and the incomplete field is included in the body.
115    ```
116    packet Basic {
117        type: 1,
118        _body_
119    }
120    ```
121    """
122
123    # Traverse empty parents.
124    parent = packet.parent
125    while parent and len(parent.fields) == 1:
126        parent = parent.parent
127
128    if not parent:
129        return 0
130
131    shift = 0
132    for f in packet.parent.fields:
133        if isinstance(f, (BodyField, PayloadField)):
134            return 0 if (shift % 8) == 0 else shift
135        else:
136            # Fields that do not have a constant size are assumed to start
137            # on a byte boundary, and measure an integral number of bytes.
138            # Start the count over.
139            size = get_field_size(f)
140            shift = 0 if size is None else shift + size
141
142    # No payload or body in parent packet.
143    # Not raising an error, the generation will fail somewhere else.
144    return 0
145
146
147def get_packet_ancestor(
148        decl: Union[PacketDeclaration, StructDeclaration]) -> Union[PacketDeclaration, StructDeclaration]:
149    """Return the root ancestor of the selected packet or struct."""
150    if decl.parent_id is None:
151        return decl
152    else:
153        return get_packet_ancestor(decl.file.packet_scope[decl.parent_id])
154
155
156def get_derived_packets(
157    decl: Union[PacketDeclaration, StructDeclaration],
158    traverse: bool = True,
159) -> List[Tuple[List[Constraint], Union[PacketDeclaration, StructDeclaration]]]:
160    """Return the list of packets or structs that immediately derive from the
161    selected packet or struct, coupled with the field constraints.
162    Packet aliases (containing no field declarations other than a payload)
163    are traversed."""
164
165    children = []
166    for d in decl.file.declarations:
167        if type(d) is type(decl) and d.parent_id == decl.id:
168            if (len(d.fields) == 1 and isinstance(d.fields[0], (PayloadField, BodyField))) and traverse:
169                children.extend([(d.constraints + sub_constraints, sub_child)
170                                 for (sub_constraints, sub_child) in get_derived_packets(d)])
171            else:
172                children.append((d.constraints, d))
173    return children
174
175
176def get_field_size(field: Field, skip_payload: bool = False) -> Optional[int]:
177    """Determine the size of a field in bits, if possible.
178    If the field is dynamically sized (e.g. unsized array or payload field),
179    None is returned instead. If skip_payload is set, payload and body fields
180    are counted as having size 0 rather than a variable size."""
181
182    if isinstance(field, (ScalarField, SizeField, CountField, ReservedField)):
183        return field.width
184
185    elif isinstance(field, FixedField):
186        return field.width or field.type.width
187
188    elif isinstance(field, PaddingField):
189        # Padding field width is added to the padded field size.
190        return 0
191
192    elif isinstance(field, ArrayField) and field.padded_size is not None:
193        return field.padded_size * 8
194
195    elif isinstance(field, ArrayField) and field.size is not None:
196        element_width = field.width or get_declaration_size(field.type)
197        return element_width * field.size if element_width is not None else None
198
199    elif isinstance(field, TypedefField):
200        return get_declaration_size(field.type)
201
202    elif isinstance(field, ChecksumField):
203        return 0
204
205    elif isinstance(field, (PayloadField, BodyField)) and skip_payload:
206        return 0
207
208    else:
209        return None
210
211
212def get_declaration_size(decl: Declaration, skip_payload: bool = False) -> Optional[int]:
213    """Determine the size of a declaration type in bits, if possible.
214    If the type is dynamically sized (e.g. contains an array or payload),
215    None is returned instead. If skip_payload is set, payload and body fields
216    are counted as having size 0 rather than a variable size."""
217
218    if isinstance(decl, (EnumDeclaration, CustomFieldDeclaration, ChecksumDeclaration)):
219        return decl.width
220
221    elif isinstance(decl, (PacketDeclaration, StructDeclaration)):
222        parent = decl.parent
223        packet_size = get_declaration_size(parent, skip_payload=True) if parent else 0
224        if packet_size is None:
225            return None
226        for f in decl.fields:
227            field_size = get_field_size(f, skip_payload=skip_payload)
228            if field_size is None:
229                return None
230            packet_size += field_size
231        return packet_size
232
233    else:
234        return None
235
236
237def get_array_field_size(field: ArrayField) -> Union[None, int, Field]:
238    """Return the array static size, size field, or count field.
239    If the array is unsized None is returned instead."""
240
241    if field.size is not None:
242        return field.size
243    for f in field.parent.fields:
244        if isinstance(f, (SizeField, CountField)) and f.field_id == field.id:
245            return f
246    return None
247
248
249def get_payload_field_size(field: Union[PayloadField, BodyField]) -> Optional[Field]:
250    """Return the payload or body size field.
251    If the payload is unsized None is returned instead."""
252
253    for f in field.parent.fields:
254        if isinstance(f, SizeField) and f.field_id == field.id:
255            return f
256    return None
257
258
259def get_array_element_size(field: ArrayField) -> Optional[int]:
260    """Return the array element size, if possible.
261    If the element size is not known at compile time,
262    None is returned instead."""
263
264    return field.width or get_declaration_size(field.type)
265
266
267def get_field_offset_from_start(field: Field) -> Optional[int]:
268    """Return the field bit offset from the start of the parent packet, if it
269    can be statically computed. If the offset is variable None is returned
270    instead."""
271    offset = 0
272    field_index = field.parent.fields.index(field)
273    for f in field.parent.fields[:field_index]:
274        size = get_field_size(f)
275        if size is None:
276            return None
277
278        offset += size
279    return offset
280
281
282def get_field_offset_from_end(field: Field) -> Optional[int]:
283    """Return the field bit offset from the end of the parent packet, if it
284    can be statically computed. If the offset is variable None is returned
285    instead. The selected field size is not counted towards the offset."""
286    offset = 0
287    field_index = field.parent.fields.index(field)
288    for f in field.parent.fields[field_index + 1:]:
289        size = get_field_size(f)
290        if size is None:
291            return None
292        offset += size
293    return offset
294
295
296def get_unconstrained_parent_fields(decl: Union[PacketDeclaration, StructDeclaration]) -> List[Field]:
297    """Return the list of fields from the parent declarations that have an identifier
298    but that do not have a value fixed by any of the parent constraints.
299    The fields are returned in order of declaration."""
300
301    def constraint_ids(constraints: List[Constraint]) -> Set[str]:
302        return set([c.id for c in constraints])
303
304    def aux(decl: Optional[Declaration], constraints: Set[str]) -> List[Field]:
305        if decl is None:
306            return []
307        fields = aux(decl.parent, constraints.union(constraint_ids(decl.constraints)))
308        for f in decl.fields:
309            if (isinstance(f, (ScalarField, ArrayField, TypedefField)) and not f.id in constraints):
310                fields.append(f)
311        return fields
312
313    return aux(decl.parent, constraint_ids(decl.constraints))
314
315
316def get_parent_constraints(decl: Union[PacketDeclaration, StructDeclaration]) -> List[Constraint]:
317    """Return the list of constraints from the current and parent declarations."""
318    parent_constraints = get_parent_constraints(decl.parent) if decl.parent else []
319    return parent_constraints + decl.constraints
320
321
322def is_bit_field(field: Field) -> bool:
323    """Identify fields that can have bit granularity.
324    These include: ScalarField, FixedField, TypedefField with enum type,
325    SizeField, and CountField."""
326
327    if isinstance(field, (ScalarField, SizeField, CountField, FixedField, ReservedField)):
328        return True
329
330    elif isinstance(field, TypedefField) and isinstance(field.type, EnumDeclaration):
331        return True
332
333    else:
334        return False
335