• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os
2import unittest
3from ctypes import (CDLL, Structure, sizeof, POINTER, byref, alignment,
4                    LittleEndianStructure, BigEndianStructure,
5                    c_byte, c_ubyte, c_char, c_char_p, c_void_p, c_wchar,
6                    c_uint32, c_uint64,
7                    c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong)
8from test import support
9from test.support import import_helper
10_ctypes_test = import_helper.import_module("_ctypes_test")
11
12
13class BITS(Structure):
14    _fields_ = [("A", c_int, 1),
15                ("B", c_int, 2),
16                ("C", c_int, 3),
17                ("D", c_int, 4),
18                ("E", c_int, 5),
19                ("F", c_int, 6),
20                ("G", c_int, 7),
21                ("H", c_int, 8),
22                ("I", c_int, 9),
23
24                ("M", c_short, 1),
25                ("N", c_short, 2),
26                ("O", c_short, 3),
27                ("P", c_short, 4),
28                ("Q", c_short, 5),
29                ("R", c_short, 6),
30                ("S", c_short, 7)]
31
32func = CDLL(_ctypes_test.__file__).unpack_bitfields
33func.argtypes = POINTER(BITS), c_char
34
35
36class C_Test(unittest.TestCase):
37
38    def test_ints(self):
39        for i in range(512):
40            for name in "ABCDEFGHI":
41                b = BITS()
42                setattr(b, name, i)
43                self.assertEqual(getattr(b, name), func(byref(b), name.encode('ascii')))
44
45    # bpo-46913: _ctypes/cfield.c h_get() has an undefined behavior
46    @support.skip_if_sanitizer(ub=True)
47    def test_shorts(self):
48        b = BITS()
49        name = "M"
50        if func(byref(b), name.encode('ascii')) == 999:
51            self.skipTest("Compiler does not support signed short bitfields")
52        for i in range(256):
53            for name in "MNOPQRS":
54                b = BITS()
55                setattr(b, name, i)
56                self.assertEqual(getattr(b, name), func(byref(b), name.encode('ascii')))
57
58
59signed_int_types = (c_byte, c_short, c_int, c_long, c_longlong)
60unsigned_int_types = (c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong)
61int_types = unsigned_int_types + signed_int_types
62
63class BitFieldTest(unittest.TestCase):
64
65    def test_longlong(self):
66        class X(Structure):
67            _fields_ = [("a", c_longlong, 1),
68                        ("b", c_longlong, 62),
69                        ("c", c_longlong, 1)]
70
71        self.assertEqual(sizeof(X), sizeof(c_longlong))
72        x = X()
73        x.a, x.b, x.c = -1, 7, -1
74        self.assertEqual((x.a, x.b, x.c), (-1, 7, -1))
75
76    def test_ulonglong(self):
77        class X(Structure):
78            _fields_ = [("a", c_ulonglong, 1),
79                        ("b", c_ulonglong, 62),
80                        ("c", c_ulonglong, 1)]
81
82        self.assertEqual(sizeof(X), sizeof(c_longlong))
83        x = X()
84        self.assertEqual((x.a, x.b, x.c), (0, 0, 0))
85        x.a, x.b, x.c = 7, 7, 7
86        self.assertEqual((x.a, x.b, x.c), (1, 7, 1))
87
88    def test_signed(self):
89        for c_typ in signed_int_types:
90            class X(Structure):
91                _fields_ = [("dummy", c_typ),
92                            ("a", c_typ, 3),
93                            ("b", c_typ, 3),
94                            ("c", c_typ, 1)]
95            self.assertEqual(sizeof(X), sizeof(c_typ)*2)
96
97            x = X()
98            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 0, 0))
99            x.a = -1
100            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, -1, 0, 0))
101            x.a, x.b = 0, -1
102            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, -1, 0))
103
104
105    def test_unsigned(self):
106        for c_typ in unsigned_int_types:
107            class X(Structure):
108                _fields_ = [("a", c_typ, 3),
109                            ("b", c_typ, 3),
110                            ("c", c_typ, 1)]
111            self.assertEqual(sizeof(X), sizeof(c_typ))
112
113            x = X()
114            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 0, 0))
115            x.a = -1
116            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 7, 0, 0))
117            x.a, x.b = 0, -1
118            self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 7, 0))
119
120    def fail_fields(self, *fields):
121        return self.get_except(type(Structure), "X", (),
122                               {"_fields_": fields})
123
124    def test_nonint_types(self):
125        # bit fields are not allowed on non-integer types.
126        result = self.fail_fields(("a", c_char_p, 1))
127        self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char_p'))
128
129        result = self.fail_fields(("a", c_void_p, 1))
130        self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_void_p'))
131
132        if c_int != c_long:
133            result = self.fail_fields(("a", POINTER(c_int), 1))
134            self.assertEqual(result, (TypeError, 'bit fields not allowed for type LP_c_int'))
135
136        result = self.fail_fields(("a", c_char, 1))
137        self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char'))
138
139        class Dummy(Structure):
140            _fields_ = []
141
142        result = self.fail_fields(("a", Dummy, 1))
143        self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy'))
144
145    def test_c_wchar(self):
146        result = self.fail_fields(("a", c_wchar, 1))
147        self.assertEqual(result,
148                (TypeError, 'bit fields not allowed for type c_wchar'))
149
150    def test_single_bitfield_size(self):
151        for c_typ in int_types:
152            result = self.fail_fields(("a", c_typ, -1))
153            self.assertEqual(result, (ValueError, 'number of bits invalid for bit field'))
154
155            result = self.fail_fields(("a", c_typ, 0))
156            self.assertEqual(result, (ValueError, 'number of bits invalid for bit field'))
157
158            class X(Structure):
159                _fields_ = [("a", c_typ, 1)]
160            self.assertEqual(sizeof(X), sizeof(c_typ))
161
162            class X(Structure):
163                _fields_ = [("a", c_typ, sizeof(c_typ)*8)]
164            self.assertEqual(sizeof(X), sizeof(c_typ))
165
166            result = self.fail_fields(("a", c_typ, sizeof(c_typ)*8 + 1))
167            self.assertEqual(result, (ValueError, 'number of bits invalid for bit field'))
168
169    def test_multi_bitfields_size(self):
170        class X(Structure):
171            _fields_ = [("a", c_short, 1),
172                        ("b", c_short, 14),
173                        ("c", c_short, 1)]
174        self.assertEqual(sizeof(X), sizeof(c_short))
175
176        class X(Structure):
177            _fields_ = [("a", c_short, 1),
178                        ("a1", c_short),
179                        ("b", c_short, 14),
180                        ("c", c_short, 1)]
181        self.assertEqual(sizeof(X), sizeof(c_short)*3)
182        self.assertEqual(X.a.offset, 0)
183        self.assertEqual(X.a1.offset, sizeof(c_short))
184        self.assertEqual(X.b.offset, sizeof(c_short)*2)
185        self.assertEqual(X.c.offset, sizeof(c_short)*2)
186
187        class X(Structure):
188            _fields_ = [("a", c_short, 3),
189                        ("b", c_short, 14),
190                        ("c", c_short, 14)]
191        self.assertEqual(sizeof(X), sizeof(c_short)*3)
192        self.assertEqual(X.a.offset, sizeof(c_short)*0)
193        self.assertEqual(X.b.offset, sizeof(c_short)*1)
194        self.assertEqual(X.c.offset, sizeof(c_short)*2)
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    def test_uint32(self):
249        class X(Structure):
250            _fields_ = [("a", c_uint32, 32)]
251        x = X()
252        x.a = 10
253        self.assertEqual(x.a, 10)
254        x.a = 0xFDCBA987
255        self.assertEqual(x.a, 0xFDCBA987)
256
257    def test_uint64(self):
258        class X(Structure):
259            _fields_ = [("a", c_uint64, 64)]
260        x = X()
261        x.a = 10
262        self.assertEqual(x.a, 10)
263        x.a = 0xFEDCBA9876543211
264        self.assertEqual(x.a, 0xFEDCBA9876543211)
265
266    def test_uint32_swap_little_endian(self):
267        # Issue #23319
268        class Little(LittleEndianStructure):
269            _fields_ = [("a", c_uint32, 24),
270                        ("b", c_uint32, 4),
271                        ("c", c_uint32, 4)]
272        b = bytearray(4)
273        x = Little.from_buffer(b)
274        x.a = 0xabcdef
275        x.b = 1
276        x.c = 2
277        self.assertEqual(b, b'\xef\xcd\xab\x21')
278
279    def test_uint32_swap_big_endian(self):
280        # Issue #23319
281        class Big(BigEndianStructure):
282            _fields_ = [("a", c_uint32, 24),
283                        ("b", c_uint32, 4),
284                        ("c", c_uint32, 4)]
285        b = bytearray(4)
286        x = Big.from_buffer(b)
287        x.a = 0xabcdef
288        x.b = 1
289        x.c = 2
290        self.assertEqual(b, b'\xab\xcd\xef\x12')
291
292
293if __name__ == "__main__":
294    unittest.main()
295