• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import sys, unittest, struct, math, ctypes
2from binascii import hexlify
3
4from ctypes import *
5
6def bin(s):
7    return hexlify(memoryview(s)).decode().upper()
8
9# Each *simple* type that supports different byte orders has an
10# __ctype_be__ attribute that specifies the same type in BIG ENDIAN
11# byte order, and a __ctype_le__ attribute that is the same type in
12# LITTLE ENDIAN byte order.
13#
14# For Structures and Unions, these types are created on demand.
15
16class Test(unittest.TestCase):
17    @unittest.skip('test disabled')
18    def test_X(self):
19        print(sys.byteorder, file=sys.stderr)
20        for i in range(32):
21            bits = BITS()
22            setattr(bits, "i%s" % i, 1)
23            dump(bits)
24
25    def test_slots(self):
26        class BigPoint(BigEndianStructure):
27            __slots__ = ()
28            _fields_ = [("x", c_int), ("y", c_int)]
29
30        class LowPoint(LittleEndianStructure):
31            __slots__ = ()
32            _fields_ = [("x", c_int), ("y", c_int)]
33
34        big = BigPoint()
35        little = LowPoint()
36        big.x = 4
37        big.y = 2
38        little.x = 2
39        little.y = 4
40        with self.assertRaises(AttributeError):
41            big.z = 42
42        with self.assertRaises(AttributeError):
43            little.z = 24
44
45    def test_endian_short(self):
46        if sys.byteorder == "little":
47            self.assertIs(c_short.__ctype_le__, c_short)
48            self.assertIs(c_short.__ctype_be__.__ctype_le__, c_short)
49        else:
50            self.assertIs(c_short.__ctype_be__, c_short)
51            self.assertIs(c_short.__ctype_le__.__ctype_be__, c_short)
52        s = c_short.__ctype_be__(0x1234)
53        self.assertEqual(bin(struct.pack(">h", 0x1234)), "1234")
54        self.assertEqual(bin(s), "1234")
55        self.assertEqual(s.value, 0x1234)
56
57        s = c_short.__ctype_le__(0x1234)
58        self.assertEqual(bin(struct.pack("<h", 0x1234)), "3412")
59        self.assertEqual(bin(s), "3412")
60        self.assertEqual(s.value, 0x1234)
61
62        s = c_ushort.__ctype_be__(0x1234)
63        self.assertEqual(bin(struct.pack(">h", 0x1234)), "1234")
64        self.assertEqual(bin(s), "1234")
65        self.assertEqual(s.value, 0x1234)
66
67        s = c_ushort.__ctype_le__(0x1234)
68        self.assertEqual(bin(struct.pack("<h", 0x1234)), "3412")
69        self.assertEqual(bin(s), "3412")
70        self.assertEqual(s.value, 0x1234)
71
72    def test_endian_int(self):
73        if sys.byteorder == "little":
74            self.assertIs(c_int.__ctype_le__, c_int)
75            self.assertIs(c_int.__ctype_be__.__ctype_le__, c_int)
76        else:
77            self.assertIs(c_int.__ctype_be__, c_int)
78            self.assertIs(c_int.__ctype_le__.__ctype_be__, c_int)
79
80        s = c_int.__ctype_be__(0x12345678)
81        self.assertEqual(bin(struct.pack(">i", 0x12345678)), "12345678")
82        self.assertEqual(bin(s), "12345678")
83        self.assertEqual(s.value, 0x12345678)
84
85        s = c_int.__ctype_le__(0x12345678)
86        self.assertEqual(bin(struct.pack("<i", 0x12345678)), "78563412")
87        self.assertEqual(bin(s), "78563412")
88        self.assertEqual(s.value, 0x12345678)
89
90        s = c_uint.__ctype_be__(0x12345678)
91        self.assertEqual(bin(struct.pack(">I", 0x12345678)), "12345678")
92        self.assertEqual(bin(s), "12345678")
93        self.assertEqual(s.value, 0x12345678)
94
95        s = c_uint.__ctype_le__(0x12345678)
96        self.assertEqual(bin(struct.pack("<I", 0x12345678)), "78563412")
97        self.assertEqual(bin(s), "78563412")
98        self.assertEqual(s.value, 0x12345678)
99
100    def test_endian_longlong(self):
101        if sys.byteorder == "little":
102            self.assertIs(c_longlong.__ctype_le__, c_longlong)
103            self.assertIs(c_longlong.__ctype_be__.__ctype_le__, c_longlong)
104        else:
105            self.assertIs(c_longlong.__ctype_be__, c_longlong)
106            self.assertIs(c_longlong.__ctype_le__.__ctype_be__, c_longlong)
107
108        s = c_longlong.__ctype_be__(0x1234567890ABCDEF)
109        self.assertEqual(bin(struct.pack(">q", 0x1234567890ABCDEF)), "1234567890ABCDEF")
110        self.assertEqual(bin(s), "1234567890ABCDEF")
111        self.assertEqual(s.value, 0x1234567890ABCDEF)
112
113        s = c_longlong.__ctype_le__(0x1234567890ABCDEF)
114        self.assertEqual(bin(struct.pack("<q", 0x1234567890ABCDEF)), "EFCDAB9078563412")
115        self.assertEqual(bin(s), "EFCDAB9078563412")
116        self.assertEqual(s.value, 0x1234567890ABCDEF)
117
118        s = c_ulonglong.__ctype_be__(0x1234567890ABCDEF)
119        self.assertEqual(bin(struct.pack(">Q", 0x1234567890ABCDEF)), "1234567890ABCDEF")
120        self.assertEqual(bin(s), "1234567890ABCDEF")
121        self.assertEqual(s.value, 0x1234567890ABCDEF)
122
123        s = c_ulonglong.__ctype_le__(0x1234567890ABCDEF)
124        self.assertEqual(bin(struct.pack("<Q", 0x1234567890ABCDEF)), "EFCDAB9078563412")
125        self.assertEqual(bin(s), "EFCDAB9078563412")
126        self.assertEqual(s.value, 0x1234567890ABCDEF)
127
128    def test_endian_float(self):
129        if sys.byteorder == "little":
130            self.assertIs(c_float.__ctype_le__, c_float)
131            self.assertIs(c_float.__ctype_be__.__ctype_le__, c_float)
132        else:
133            self.assertIs(c_float.__ctype_be__, c_float)
134            self.assertIs(c_float.__ctype_le__.__ctype_be__, c_float)
135        s = c_float(math.pi)
136        self.assertEqual(bin(struct.pack("f", math.pi)), bin(s))
137        # Hm, what's the precision of a float compared to a double?
138        self.assertAlmostEqual(s.value, math.pi, places=6)
139        s = c_float.__ctype_le__(math.pi)
140        self.assertAlmostEqual(s.value, math.pi, places=6)
141        self.assertEqual(bin(struct.pack("<f", math.pi)), bin(s))
142        s = c_float.__ctype_be__(math.pi)
143        self.assertAlmostEqual(s.value, math.pi, places=6)
144        self.assertEqual(bin(struct.pack(">f", math.pi)), bin(s))
145
146    def test_endian_double(self):
147        if sys.byteorder == "little":
148            self.assertIs(c_double.__ctype_le__, c_double)
149            self.assertIs(c_double.__ctype_be__.__ctype_le__, c_double)
150        else:
151            self.assertIs(c_double.__ctype_be__, c_double)
152            self.assertIs(c_double.__ctype_le__.__ctype_be__, c_double)
153        s = c_double(math.pi)
154        self.assertEqual(s.value, math.pi)
155        self.assertEqual(bin(struct.pack("d", math.pi)), bin(s))
156        s = c_double.__ctype_le__(math.pi)
157        self.assertEqual(s.value, math.pi)
158        self.assertEqual(bin(struct.pack("<d", math.pi)), bin(s))
159        s = c_double.__ctype_be__(math.pi)
160        self.assertEqual(s.value, math.pi)
161        self.assertEqual(bin(struct.pack(">d", math.pi)), bin(s))
162
163    def test_endian_other(self):
164        self.assertIs(c_byte.__ctype_le__, c_byte)
165        self.assertIs(c_byte.__ctype_be__, c_byte)
166
167        self.assertIs(c_ubyte.__ctype_le__, c_ubyte)
168        self.assertIs(c_ubyte.__ctype_be__, c_ubyte)
169
170        self.assertIs(c_char.__ctype_le__, c_char)
171        self.assertIs(c_char.__ctype_be__, c_char)
172
173    def test_struct_fields_unsupported_byte_order(self):
174
175        fields = [
176            ("a", c_ubyte),
177            ("b", c_byte),
178            ("c", c_short),
179            ("d", c_ushort),
180            ("e", c_int),
181            ("f", c_uint),
182            ("g", c_long),
183            ("h", c_ulong),
184            ("i", c_longlong),
185            ("k", c_ulonglong),
186            ("l", c_float),
187            ("m", c_double),
188            ("n", c_char),
189            ("b1", c_byte, 3),
190            ("b2", c_byte, 3),
191            ("b3", c_byte, 2),
192            ("a", c_int * 3 * 3 * 3)
193        ]
194
195        # these fields do not support different byte order:
196        for typ in c_wchar, c_void_p, POINTER(c_int):
197            with self.assertRaises(TypeError):
198                class T(BigEndianStructure if sys.byteorder == "little" else LittleEndianStructure):
199                    _fields_ = fields + [("x", typ)]
200
201
202    def test_struct_struct(self):
203        # nested structures with different byteorders
204
205        # create nested structures with given byteorders and set memory to data
206
207        for nested, data in (
208            (BigEndianStructure, b'\0\0\0\1\0\0\0\2'),
209            (LittleEndianStructure, b'\1\0\0\0\2\0\0\0'),
210        ):
211            for parent in (
212                BigEndianStructure,
213                LittleEndianStructure,
214                Structure,
215            ):
216                class NestedStructure(nested):
217                    _fields_ = [("x", c_uint32),
218                                ("y", c_uint32)]
219
220                class TestStructure(parent):
221                    _fields_ = [("point", NestedStructure)]
222
223                self.assertEqual(len(data), sizeof(TestStructure))
224                ptr = POINTER(TestStructure)
225                s = cast(data, ptr)[0]
226                del ctypes._pointer_type_cache[TestStructure]
227                self.assertEqual(s.point.x, 1)
228                self.assertEqual(s.point.y, 2)
229
230    def test_struct_field_alignment(self):
231        # standard packing in struct uses no alignment.
232        # So, we have to align using pad bytes.
233        #
234        # Unaligned accesses will crash Python (on those platforms that
235        # don't allow it, like sparc solaris).
236        if sys.byteorder == "little":
237            base = BigEndianStructure
238            fmt = ">bxhid"
239        else:
240            base = LittleEndianStructure
241            fmt = "<bxhid"
242
243        class S(base):
244            _fields_ = [("b", c_byte),
245                        ("h", c_short),
246                        ("i", c_int),
247                        ("d", c_double)]
248
249        s1 = S(0x12, 0x1234, 0x12345678, 3.14)
250        s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
251        self.assertEqual(bin(s1), bin(s2))
252
253    def test_unaligned_nonnative_struct_fields(self):
254        if sys.byteorder == "little":
255            base = BigEndianStructure
256            fmt = ">b h xi xd"
257        else:
258            base = LittleEndianStructure
259            fmt = "<b h xi xd"
260
261        class S(base):
262            _pack_ = 1
263            _fields_ = [("b", c_byte),
264                        ("h", c_short),
265
266                        ("_1", c_byte),
267                        ("i", c_int),
268
269                        ("_2", c_byte),
270                        ("d", c_double)]
271
272        s1 = S()
273        s1.b = 0x12
274        s1.h = 0x1234
275        s1.i = 0x12345678
276        s1.d = 3.14
277        s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
278        self.assertEqual(bin(s1), bin(s2))
279
280    def test_unaligned_native_struct_fields(self):
281        if sys.byteorder == "little":
282            fmt = "<b h xi xd"
283        else:
284            base = LittleEndianStructure
285            fmt = ">b h xi xd"
286
287        class S(Structure):
288            _pack_ = 1
289            _fields_ = [("b", c_byte),
290
291                        ("h", c_short),
292
293                        ("_1", c_byte),
294                        ("i", c_int),
295
296                        ("_2", c_byte),
297                        ("d", c_double)]
298
299        s1 = S()
300        s1.b = 0x12
301        s1.h = 0x1234
302        s1.i = 0x12345678
303        s1.d = 3.14
304        s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
305        self.assertEqual(bin(s1), bin(s2))
306
307    def test_union_fields_unsupported_byte_order(self):
308
309        fields = [
310            ("a", c_ubyte),
311            ("b", c_byte),
312            ("c", c_short),
313            ("d", c_ushort),
314            ("e", c_int),
315            ("f", c_uint),
316            ("g", c_long),
317            ("h", c_ulong),
318            ("i", c_longlong),
319            ("k", c_ulonglong),
320            ("l", c_float),
321            ("m", c_double),
322            ("n", c_char),
323            ("b1", c_byte, 3),
324            ("b2", c_byte, 3),
325            ("b3", c_byte, 2),
326            ("a", c_int * 3 * 3 * 3)
327        ]
328
329        # these fields do not support different byte order:
330        for typ in c_wchar, c_void_p, POINTER(c_int):
331            with self.assertRaises(TypeError):
332                class T(BigEndianUnion if sys.byteorder == "little" else LittleEndianUnion):
333                    _fields_ = fields + [("x", typ)]
334
335    def test_union_struct(self):
336        # nested structures in unions with different byteorders
337
338        # create nested structures in unions with given byteorders and set memory to data
339
340        for nested, data in (
341            (BigEndianStructure, b'\0\0\0\1\0\0\0\2'),
342            (LittleEndianStructure, b'\1\0\0\0\2\0\0\0'),
343        ):
344            for parent in (
345                BigEndianUnion,
346                LittleEndianUnion,
347                Union,
348            ):
349                class NestedStructure(nested):
350                    _fields_ = [("x", c_uint32),
351                                ("y", c_uint32)]
352
353                class TestUnion(parent):
354                    _fields_ = [("point", NestedStructure)]
355
356                self.assertEqual(len(data), sizeof(TestUnion))
357                ptr = POINTER(TestUnion)
358                s = cast(data, ptr)[0]
359                del ctypes._pointer_type_cache[TestUnion]
360                self.assertEqual(s.point.x, 1)
361                self.assertEqual(s.point.y, 2)
362
363if __name__ == "__main__":
364    unittest.main()
365