Skip to content

Commit 2ebb795

Browse files
committed
Add anonymous nested union
1 parent f337b0d commit 2ebb795

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed

cstruct/c_parser.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def parse_struct(
182182
flexible_array: bool = False
183183
offset: int = 0
184184
max_alignment: int = 0
185+
anonymous: int = 0
185186
if isinstance(__struct__, Tokens):
186187
tokens = __struct__
187188
else:
@@ -197,6 +198,16 @@ def parse_struct(
197198
vname = tokens.pop()
198199
if vname in fields_types:
199200
raise ParserError("Duplicate member '{}'".format(vname))
201+
# anonymous nested union
202+
if vname == ';' and field_type.ref is not None and (__is_union__ or field_type.ref.__is_union__):
203+
# add the anonymous struct fields to the parent
204+
for nested_field_name, nested_field_type in field_type.ref.__fields_types__.items():
205+
if nested_field_name in fields_types:
206+
raise ParserError("Duplicate member '{}'".format(nested_field_name))
207+
fields_types[nested_field_name] = nested_field_type
208+
vname = "__anonymous{}".format(anonymous)
209+
anonymous += 1
210+
tokens.push(';')
200211
fields_types[vname] = field_type
201212
# calculate the max field size (for the alignment)
202213
max_alignment = max(max_alignment, field_type.alignment)

tests/test_nested.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#!/usr/bin/env python
2+
# *****************************************************************************
3+
#
4+
# Copyright (c) 2013-2019 Andrea Bonomi <andrea.bonomi@gmail.com>
5+
#
6+
# Published under the terms of the MIT license.
7+
#
8+
# Permission is hereby granted, free of charge, to any person obtaining a copy
9+
# of this software and associated documentation files (the "Software"), to
10+
# deal in the Software without restriction, including without limitation the
11+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12+
# sell copies of the Software, and to permit persons to whom the Software is
13+
# furnished to do so, subject to the following conditions:
14+
#
15+
# The above copyright notice and this permission notice shall be included in
16+
# all copies or substantial portions of the Software.
17+
#
18+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24+
# IN THE SOFTWARE.
25+
#
26+
# *****************************************************************************
27+
28+
import pytest
29+
import cstruct
30+
from cstruct import sizeof
31+
from cstruct.exceptions import ParserError
32+
33+
34+
INVALID_ANONYMOUS = """
35+
struct NestedStruct {
36+
struct {
37+
int a;
38+
int b;
39+
};
40+
int a;
41+
int b;
42+
};
43+
"""
44+
45+
46+
class NestedStruct(cstruct.MemCStruct):
47+
__byte_order__ = cstruct.LITTLE_ENDIAN
48+
__def__ = """
49+
struct NestedStruct {
50+
struct {
51+
int a;
52+
int b;
53+
} s;
54+
int a;
55+
int b;
56+
};
57+
"""
58+
59+
60+
class NestedUnion(cstruct.MemCStruct):
61+
__byte_order__ = cstruct.LITTLE_ENDIAN
62+
__def__ = """
63+
union NestedUnion {
64+
struct {
65+
int a;
66+
int b;
67+
} s;
68+
int a;
69+
};
70+
"""
71+
72+
73+
class NestedAnonymousUnion(cstruct.MemCStruct):
74+
__byte_order__ = cstruct.LITTLE_ENDIAN
75+
__def__ = """
76+
union NestedAnonymousUnion {
77+
struct {
78+
int a;
79+
int b;
80+
};
81+
int c;
82+
};
83+
"""
84+
85+
86+
class Packet(cstruct.MemCStruct):
87+
__byte_order__ = cstruct.LITTLE_ENDIAN
88+
__def__ = """
89+
#define MaxPacket 20
90+
91+
struct Packet {
92+
uint8_t packetLength;
93+
union {
94+
uint8_t bytes[MaxPacket];
95+
struct {
96+
uint16_t field1;
97+
uint16_t field2;
98+
uint16_t field3;
99+
} format1;
100+
struct {
101+
double value1;
102+
double value2;
103+
} format2;
104+
};
105+
};
106+
"""
107+
108+
109+
def test_invalid_anonymous():
110+
with pytest.raises(ParserError):
111+
cstruct.parse(INVALID_ANONYMOUS)
112+
assert True
113+
114+
115+
def test_sizeof_nested_struct():
116+
assert sizeof('struct NestedStruct') == 16
117+
o = NestedStruct()
118+
assert len(o) == 16
119+
120+
121+
def test_pack_unpack_nested_struct():
122+
o = NestedStruct()
123+
o.s.a = 1
124+
o.s.b = 2
125+
o.a = 10
126+
o.b = 20
127+
b = o.pack()
128+
o1 = NestedStruct()
129+
130+
o1.unpack(b)
131+
assert o1.s.a == 1
132+
assert o1.s.b == 2
133+
assert o1.a == 10
134+
assert o1.b == 20
135+
136+
137+
def test_sizeof_nested_union():
138+
assert sizeof('struct NestedUnion') == 8
139+
o = NestedUnion()
140+
assert len(o) == 8
141+
142+
143+
def test_pack_unpack_nested_union():
144+
o = NestedUnion()
145+
o.s.a = 1
146+
o.s.b = 2
147+
b = o.pack()
148+
149+
o1 = NestedUnion()
150+
o1.unpack(b)
151+
assert o1.s.a == 1
152+
assert o1.s.b == 2
153+
154+
o = NestedUnion()
155+
o.a = 1979
156+
b = o.pack()
157+
158+
o1 = NestedUnion()
159+
o1.unpack(b)
160+
assert o1.a == 1979
161+
o1.s.b = 0
162+
assert o1.a == 1979
163+
o1.s.a = 0
164+
assert o1.a == 0
165+
166+
167+
def test_sizeof_nested_anonymous_union():
168+
assert sizeof('struct NestedAnonymousUnion') == 8
169+
o = NestedAnonymousUnion()
170+
assert len(o) == 8
171+
172+
173+
def test_pack_unpack_nested_anonymous_union():
174+
o = NestedAnonymousUnion()
175+
o.a = 1
176+
o.b = 2
177+
b = o.pack()
178+
179+
o1 = NestedAnonymousUnion()
180+
o1.unpack(b)
181+
assert o1.a == 1
182+
assert o1.b == 2
183+
184+
o = NestedAnonymousUnion()
185+
o.c = 1979
186+
b = o.pack()
187+
188+
o1 = NestedAnonymousUnion()
189+
o1.unpack(b)
190+
assert o1.c == 1979
191+
o1.b = 0
192+
assert o1.c == 1979
193+
o1.a = 0
194+
assert o1.c == 0
195+
196+
197+
def test_nested_anonymous_union_struct():
198+
o = Packet()
199+
assert sizeof("struct Packet") == len(o)
200+
201+
o = Packet()
202+
o.packetLength = 10
203+
o.format1.field1 = 11
204+
o.format1.field2 = 12
205+
o.format1.field3 = 13
206+
b = o.pack()
207+
208+
o1 = Packet()
209+
o1.unpack(b)
210+
assert o1.format1.field1 == 11
211+
assert o1.format1.field2 == 12
212+
assert o1.format1.field3 == 13

0 commit comments

Comments
 (0)