• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from ctypes import *
2from ctypes.test import need_symbol
3from test import support
4import unittest
5import os
6
7import _ctypes_test
8
9class BITS(Structure):
10    _fields_ = [("A", c_int, 1),
11                ("B", c_int, 2),
12                ("C", c_int, 3),
13                ("D", c_int, 4),
14                ("E", c_int, 5),
15                ("F", c_int, 6),
16                ("G", c_int, 7),
17                ("H", c_int, 8),
18                ("I", c_int, 9),
19
20                ("M", c_short, 1),
21                ("N", c_short, 2),
22                ("O", c_short, 3),
23                ("P", c_short, 4),
24                ("Q", c_short, 5),
25                ("R", c_short, 6),
26                ("S", c_short, 7)]
27
28func = CDLL(_ctypes_test.__file__).unpack_bitfields
29func.argtypes = POINTER(BITS), c_char
30
31##for n in "ABCDEFGHIMNOPQRS":
32##    print n, hex(getattr(BITS, n).size), getattr(BITS, n).offset
33
34class C_Test(unittest.TestCase):
35
36    def test_ints(self):
37        for i in range(512):
38            for name in "ABCDEFGHI":
39                b = BITS()
40                setattr(b, name, i)
41                self.assertEqual(getattr(b, name), func(byref(b), name.encode('ascii')))
42
43    # bpo-46913: _ctypes/cfield.c h_get() has an undefined behavior
44    @support.skip_if_sanitizer(ub=True)
45    def test_shorts(self):
46        b = BITS()
47        name = "M"
48        if func(byref(b), name.encode('ascii')) == 999:
49            self.skipTest("Compiler does not support signed short bitfields")
50        for i in range(256):
51            for name in "MNOPQRS":
52                b = BITS()
53                setattr(b, name, i)
54                self.assertEqual(getattr(b, name), func(byref(b), name.encode('ascii')))
55
56signed_int_types = (c_byte, c_short, c_int, c_long, c_longlong)
57unsigned_int_types = (c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong)
58int_types = unsigned_int_types + signed_int_types
59
60class BitFieldTest(unittest.TestCase):
61
62    def test_longlong(self):
63        class X(Structure):
64            _fields_ = [("a", c_longlong, 1),
65                        ("b", c_longlong, 62),
66                        ("c", c_longlong, 1)]
67
68        self.assertEqual(sizeof(X), sizeof(c_longlong))
69        x = X()
70        x.a, x.b, x.c = -1, 7, -1
71        self.assertEqual((x.a, x.b, x.c), (-1, 7, -1))
72
73    def test_ulonglong(self):
74        class X(Structure):
75            _fields_ = [("a", c_ulonglong, 1),
76                        ("b", c_ulonglong, 62),
77                        ("c", c_ulonglong, 1)]
78
79        self.assertEqual(sizeof(X), sizeof(c_longlong))
80        x = X()
81        self.assertEqual((x.a, x.b, x.c), (0, 0, 0))
82        x.a, x.b, x.c = 7, 7, 7
83        self.assertEqual((x.a, x.b, x.c), (1, 7, 1))
84
85    def test_signed(self):
86        for c_typ in signed_int_types:
87            class X(Structure):
88                _fields_ = [("dummy", c_typ),
89                            ("a", c_typ, 3),
90                            ("b", c_typ, 3),
91                            ("c", c_typ, 1)]
92            self.assertEqual(sizeof(X), sizeof(c_typ)*2)
93
94            x = X()
95            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 0, 0))
96            x.a = -1
97            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, -1, 0, 0))
98            x.a, x.b = 0, -1
99            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, -1, 0))
100
101
102    def test_unsigned(self):
103        for c_typ in unsigned_int_types:
104            class X(Structure):
105                _fields_ = [("a", c_typ, 3),
106                            ("b", c_typ, 3),
107                            ("c", c_typ, 1)]
108            self.assertEqual(sizeof(X), sizeof(c_typ))
109
110            x = X()
111            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 0, 0))
112            x.a = -1
113            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 7, 0, 0))
114            x.a, x.b = 0, -1
115            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 7, 0))
116
117
118    def fail_fields(self, *fields):
119        return self.get_except(type(Structure), "X", (),
120                               {"_fields_": fields})
121
122    def test_nonint_types(self):
123        # bit fields are not allowed on non-integer types.
124        result = self.fail_fields(("a", c_char_p, 1))
125        self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char_p'))
126
127        result = self.fail_fields(("a", c_void_p, 1))
128        self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_void_p'))
129
130        if c_int != c_long:
131            result = self.fail_fields(("a", POINTER(c_int), 1))
132            self.assertEqual(result, (TypeError, 'bit fields not allowed for type LP_c_int'))
133
134        result = self.fail_fields(("a", c_char, 1))
135        self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char'))
136
137        class Dummy(Structure):
138            _fields_ = []
139
140        result = self.fail_fields(("a", Dummy, 1))
141        self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy'))
142
143    @need_symbol('c_wchar')
144    def test_c_wchar(self):
145        result = self.fail_fields(("a", c_wchar, 1))
146        self.assertEqual(result,
147                (TypeError, 'bit fields not allowed for type c_wchar'))
148
149    def test_single_bitfield_size(self):
150        for c_typ in int_types:
151            result = self.fail_fields(("a", c_typ, -1))
152            self.assertEqual(result, (ValueError, 'number of bits invalid for bit field'))
153
154            result = self.fail_fields(("a", c_typ, 0))
155            self.assertEqual(result, (ValueError, 'number of bits invalid for bit field'))
156
157            class X(Structure):
158                _fields_ = [("a", c_typ, 1)]
159            self.assertEqual(sizeof(X), sizeof(c_typ))
160
161            class X(Structure):
162                _fields_ = [("a", c_typ, sizeof(c_typ)*8)]
163            self.assertEqual(sizeof(X), sizeof(c_typ))
164
165            result = self.fail_fields(("a", c_typ, sizeof(c_typ)*8 + 1))
166            self.assertEqual(result, (ValueError, 'number of bits invalid for bit field'))
167
168    def test_multi_bitfields_size(self):
169        class X(Structure):
170            _fields_ = [("a", c_short, 1),
171                        ("b", c_short, 14),
172                        ("c", c_short, 1)]
173        self.assertEqual(sizeof(X), sizeof(c_short))
174
175        class X(Structure):
176            _fields_ = [("a", c_short, 1),
177                        ("a1", c_short),
178                        ("b", c_short, 14),
179                        ("c", c_short, 1)]
180        self.assertEqual(sizeof(X), sizeof(c_short)*3)
181        self.assertEqual(X.a.offset, 0)
182        self.assertEqual(X.a1.offset, sizeof(c_short))
183        self.assertEqual(X.b.offset, sizeof(c_short)*2)
184        self.assertEqual(X.c.offset, sizeof(c_short)*2)
185
186        class X(Structure):
187            _fields_ = [("a", c_short, 3),
188                        ("b", c_short, 14),
189                        ("c", c_short, 14)]
190        self.assertEqual(sizeof(X), sizeof(c_short)*3)
191        self.assertEqual(X.a.offset, sizeof(c_short)*0)
192        self.assertEqual(X.b.offset, sizeof(c_short)*1)
193        self.assertEqual(X.c.offset, sizeof(c_short)*2)
194
195
196    def get_except(self, func, *args, **kw):
197        try:
198            func(*args, **kw)
199        except Exception as detail:
200            return detail.__class__, str(detail)
201
202    def test_mixed_1(self):
203        class X(Structure):
204            _fields_ = [("a", c_byte, 4),
205                        ("b", c_int, 4)]
206        if os.name == "nt":
207            self.assertEqual(sizeof(X), sizeof(c_int)*2)
208        else:
209            self.assertEqual(sizeof(X), sizeof(c_int))
210
211    def test_mixed_2(self):
212        class X(Structure):
213            _fields_ = [("a", c_byte, 4),
214                        ("b", c_int, 32)]
215        self.assertEqual(sizeof(X), alignment(c_int)+sizeof(c_int))
216
217    def test_mixed_3(self):
218        class X(Structure):
219            _fields_ = [("a", c_byte, 4),
220                        ("b", c_ubyte, 4)]
221        self.assertEqual(sizeof(X), sizeof(c_byte))
222
223    def test_mixed_4(self):
224        class X(Structure):
225            _fields_ = [("a", c_short, 4),
226                        ("b", c_short, 4),
227                        ("c", c_int, 24),
228                        ("d", c_short, 4),
229                        ("e", c_short, 4),
230                        ("f", c_int, 24)]
231        # MSVC does NOT combine c_short and c_int into one field, GCC
232        # does (unless GCC is run with '-mms-bitfields' which
233        # produces code compatible with MSVC).
234        if os.name == "nt":
235            self.assertEqual(sizeof(X), sizeof(c_int) * 4)
236        else:
237            self.assertEqual(sizeof(X), sizeof(c_int) * 2)
238
239    def test_anon_bitfields(self):
240        # anonymous bit-fields gave a strange error message
241        class X(Structure):
242            _fields_ = [("a", c_byte, 4),
243                        ("b", c_ubyte, 4)]
244        class Y(Structure):
245            _anonymous_ = ["_"]
246            _fields_ = [("_", X)]
247
248    @need_symbol('c_uint32')
249    def test_uint32(self):
250        class X(Structure):
251            _fields_ = [("a", c_uint32, 32)]
252        x = X()
253        x.a = 10
254        self.assertEqual(x.a, 10)
255        x.a = 0xFDCBA987
256        self.assertEqual(x.a, 0xFDCBA987)
257
258    @need_symbol('c_uint64')
259    def test_uint64(self):
260        class X(Structure):
261            _fields_ = [("a", c_uint64, 64)]
262        x = X()
263        x.a = 10
264        self.assertEqual(x.a, 10)
265        x.a = 0xFEDCBA9876543211
266        self.assertEqual(x.a, 0xFEDCBA9876543211)
267
268    @need_symbol('c_uint32')
269    def test_uint32_swap_little_endian(self):
270        # Issue #23319
271        class Little(LittleEndianStructure):
272            _fields_ = [("a", c_uint32, 24),
273                        ("b", c_uint32, 4),
274                        ("c", c_uint32, 4)]
275        b = bytearray(4)
276        x = Little.from_buffer(b)
277        x.a = 0xabcdef
278        x.b = 1
279        x.c = 2
280        self.assertEqual(b, b'\xef\xcd\xab\x21')
281
282    @need_symbol('c_uint32')
283    def test_uint32_swap_big_endian(self):
284        # Issue #23319
285        class Big(BigEndianStructure):
286            _fields_ = [("a", c_uint32, 24),
287                        ("b", c_uint32, 4),
288                        ("c", c_uint32, 4)]
289        b = bytearray(4)
290        x = Big.from_buffer(b)
291        x.a = 0xabcdef
292        x.b = 1
293        x.c = 2
294        self.assertEqual(b, b'\xab\xcd\xef\x12')
295
296if __name__ == "__main__":
297    unittest.main()
298