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