1import unittest
2from ctypes import *
3import re, sys
4
5if sys.byteorder == "little":
6    THIS_ENDIAN = "<"
7    OTHER_ENDIAN = ">"
8else:
9    THIS_ENDIAN = ">"
10    OTHER_ENDIAN = "<"
11
12def normalize(format):
13    # Remove current endian specifier and white space from a format
14    # string
15    if format is None:
16        return ""
17    format = format.replace(OTHER_ENDIAN, THIS_ENDIAN)
18    return re.sub(r"\s", "", format)
19
20class Test(unittest.TestCase):
21
22    def test_native_types(self):
23        for tp, fmt, shape, itemtp in native_types:
24            ob = tp()
25            v = memoryview(ob)
26            try:
27                self.assertEqual(normalize(v.format), normalize(fmt))
28                if shape is not None:
29                    self.assertEqual(len(v), shape[0])
30                else:
31                    self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob))
32                self.assertEqual(v.itemsize, sizeof(itemtp))
33                self.assertEqual(v.shape, shape)
34                # ctypes object always have a non-strided memory block
35                self.assertEqual(v.strides, None)
36                # they are always read/write
37                self.assertFalse(v.readonly)
38
39                if v.shape:
40                    n = 1
41                    for dim in v.shape:
42                        n = n * dim
43                    self.assertEqual(n * v.itemsize, len(v.tobytes()))
44            except:
45                # so that we can see the failing type
46                print(tp)
47                raise
48
49    def test_endian_types(self):
50        for tp, fmt, shape, itemtp in endian_types:
51            ob = tp()
52            v = memoryview(ob)
53            try:
54                self.assertEqual(v.format, fmt)
55                if shape is not None:
56                    self.assertEqual(len(v), shape[0])
57                else:
58                    self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob))
59                self.assertEqual(v.itemsize, sizeof(itemtp))
60                self.assertEqual(v.shape, shape)
61                # ctypes object always have a non-strided memory block
62                self.assertEqual(v.strides, None)
63                # they are always read/write
64                self.assertFalse(v.readonly)
65
66                if v.shape:
67                    n = 1
68                    for dim in v.shape:
69                        n = n * dim
70                    self.assertEqual(n, len(v))
71            except:
72                # so that we can see the failing type
73                print(tp)
74                raise
75
76# define some structure classes
77
78class Point(Structure):
79    _fields_ = [("x", c_long), ("y", c_long)]
80
81class PackedPoint(Structure):
82    _pack_ = 2
83    _fields_ = [("x", c_long), ("y", c_long)]
84
85class Point2(Structure):
86    pass
87Point2._fields_ = [("x", c_long), ("y", c_long)]
88
89class EmptyStruct(Structure):
90    _fields_ = []
91
92class aUnion(Union):
93    _fields_ = [("a", c_int)]
94
95class StructWithArrays(Structure):
96    _fields_ = [("x", c_long * 3 * 2), ("y", Point * 4)]
97
98
99class Incomplete(Structure):
100    pass
101
102class Complete(Structure):
103    pass
104PComplete = POINTER(Complete)
105Complete._fields_ = [("a", c_long)]
106
107################################################################
108#
109# This table contains format strings as they look on little endian
110# machines.  The test replaces '<' with '>' on big endian machines.
111#
112native_types = [
113    # type                      format                  shape           calc itemsize
114
115    ## simple types
116
117    (c_char,                    "<c",                   None,           c_char),
118    (c_byte,                    "<b",                   None,           c_byte),
119    (c_ubyte,                   "<B",                   None,           c_ubyte),
120    (c_short,                   "<h",                   None,           c_short),
121    (c_ushort,                  "<H",                   None,           c_ushort),
122
123    # c_int and c_uint may be aliases to c_long
124    #(c_int,                     "<i",                   None,           c_int),
125    #(c_uint,                    "<I",                   None,           c_uint),
126
127    (c_long,                    "<l",                   None,           c_long),
128    (c_ulong,                   "<L",                   None,           c_ulong),
129
130    # c_longlong and c_ulonglong are aliases on 64-bit platforms
131    #(c_longlong,                "<q",                   None,           c_longlong),
132    #(c_ulonglong,               "<Q",                   None,           c_ulonglong),
133
134    (c_float,                   "<f",                   None,           c_float),
135    (c_double,                  "<d",                   None,           c_double),
136    # c_longdouble may be an alias to c_double
137
138    (c_bool,                    "<?",                   None,           c_bool),
139    (py_object,                 "<O",                   None,           py_object),
140
141    ## pointers
142
143    (POINTER(c_byte),           "&<b",                  None,           POINTER(c_byte)),
144    (POINTER(POINTER(c_long)),  "&&<l",                 None,           POINTER(POINTER(c_long))),
145
146    ## arrays and pointers
147
148    (c_double * 4,              "<d",                   (4,),           c_double),
149    (c_float * 4 * 3 * 2,       "<f",                   (2,3,4),        c_float),
150    (POINTER(c_short) * 2,      "&<h",                  (2,),           POINTER(c_short)),
151    (POINTER(c_short) * 2 * 3,  "&<h",                  (3,2,),         POINTER(c_short)),
152    (POINTER(c_short * 2),      "&(2)<h",               None,           POINTER(c_short)),
153
154    ## structures and unions
155
156    (Point,                     "T{<l:x:<l:y:}",        None,           Point),
157    # packed structures do not implement the pep
158    (PackedPoint,               "B",                    None,           PackedPoint),
159    (Point2,                    "T{<l:x:<l:y:}",        None,           Point2),
160    (EmptyStruct,               "T{}",                  None,           EmptyStruct),
161    # the pep does't support unions
162    (aUnion,                    "B",                    None,           aUnion),
163    # structure with sub-arrays
164    (StructWithArrays,          "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", None,  StructWithArrays),
165    (StructWithArrays * 3,      "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (3,),  StructWithArrays),
166
167    ## pointer to incomplete structure
168    (Incomplete,                "B",                    None,           Incomplete),
169    (POINTER(Incomplete),       "&B",                   None,           POINTER(Incomplete)),
170
171    # 'Complete' is a structure that starts incomplete, but is completed after the
172    # pointer type to it has been created.
173    (Complete,                  "T{<l:a:}",             None,           Complete),
174    # Unfortunately the pointer format string is not fixed...
175    (POINTER(Complete),         "&B",                   None,           POINTER(Complete)),
176
177    ## other
178
179    # function signatures are not implemented
180    (CFUNCTYPE(None),           "X{}",                  None,           CFUNCTYPE(None)),
181
182    ]
183
184class BEPoint(BigEndianStructure):
185    _fields_ = [("x", c_long), ("y", c_long)]
186
187class LEPoint(LittleEndianStructure):
188    _fields_ = [("x", c_long), ("y", c_long)]
189
190################################################################
191#
192# This table contains format strings as they really look, on both big
193# and little endian machines.
194#
195endian_types = [
196    (BEPoint,                   "T{>l:x:>l:y:}",        None,           BEPoint),
197    (LEPoint,                   "T{<l:x:<l:y:}",        None,           LEPoint),
198    (POINTER(BEPoint),          "&T{>l:x:>l:y:}",       None,           POINTER(BEPoint)),
199    (POINTER(LEPoint),          "&T{<l:x:<l:y:}",       None,           POINTER(LEPoint)),
200    ]
201
202if __name__ == "__main__":
203    unittest.main()
204