• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import ctypes, ctypes.util, operator, sys
2from . import model
3
4if sys.version_info < (3,):
5    bytechr = chr
6else:
7    unicode = str
8    long = int
9    xrange = range
10    bytechr = lambda num: bytes([num])
11
12class CTypesType(type):
13    pass
14
15class CTypesData(object):
16    __metaclass__ = CTypesType
17    __slots__ = ['__weakref__']
18    __name__ = '<cdata>'
19
20    def __init__(self, *args):
21        raise TypeError("cannot instantiate %r" % (self.__class__,))
22
23    @classmethod
24    def _newp(cls, init):
25        raise TypeError("expected a pointer or array ctype, got '%s'"
26                        % (cls._get_c_name(),))
27
28    @staticmethod
29    def _to_ctypes(value):
30        raise TypeError
31
32    @classmethod
33    def _arg_to_ctypes(cls, *value):
34        try:
35            ctype = cls._ctype
36        except AttributeError:
37            raise TypeError("cannot create an instance of %r" % (cls,))
38        if value:
39            res = cls._to_ctypes(*value)
40            if not isinstance(res, ctype):
41                res = cls._ctype(res)
42        else:
43            res = cls._ctype()
44        return res
45
46    @classmethod
47    def _create_ctype_obj(cls, init):
48        if init is None:
49            return cls._arg_to_ctypes()
50        else:
51            return cls._arg_to_ctypes(init)
52
53    @staticmethod
54    def _from_ctypes(ctypes_value):
55        raise TypeError
56
57    @classmethod
58    def _get_c_name(cls, replace_with=''):
59        return cls._reftypename.replace(' &', replace_with)
60
61    @classmethod
62    def _fix_class(cls):
63        cls.__name__ = 'CData<%s>' % (cls._get_c_name(),)
64        cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),)
65        cls.__module__ = 'ffi'
66
67    def _get_own_repr(self):
68        raise NotImplementedError
69
70    def _addr_repr(self, address):
71        if address == 0:
72            return 'NULL'
73        else:
74            if address < 0:
75                address += 1 << (8*ctypes.sizeof(ctypes.c_void_p))
76            return '0x%x' % address
77
78    def __repr__(self, c_name=None):
79        own = self._get_own_repr()
80        return '<cdata %r %s>' % (c_name or self._get_c_name(), own)
81
82    def _convert_to_address(self, BClass):
83        if BClass is None:
84            raise TypeError("cannot convert %r to an address" % (
85                self._get_c_name(),))
86        else:
87            raise TypeError("cannot convert %r to %r" % (
88                self._get_c_name(), BClass._get_c_name()))
89
90    @classmethod
91    def _get_size(cls):
92        return ctypes.sizeof(cls._ctype)
93
94    def _get_size_of_instance(self):
95        return ctypes.sizeof(self._ctype)
96
97    @classmethod
98    def _cast_from(cls, source):
99        raise TypeError("cannot cast to %r" % (cls._get_c_name(),))
100
101    def _cast_to_integer(self):
102        return self._convert_to_address(None)
103
104    @classmethod
105    def _alignment(cls):
106        return ctypes.alignment(cls._ctype)
107
108    def __iter__(self):
109        raise TypeError("cdata %r does not support iteration" % (
110            self._get_c_name()),)
111
112    def _make_cmp(name):
113        cmpfunc = getattr(operator, name)
114        def cmp(self, other):
115            v_is_ptr = not isinstance(self, CTypesGenericPrimitive)
116            w_is_ptr = (isinstance(other, CTypesData) and
117                           not isinstance(other, CTypesGenericPrimitive))
118            if v_is_ptr and w_is_ptr:
119                return cmpfunc(self._convert_to_address(None),
120                               other._convert_to_address(None))
121            elif v_is_ptr or w_is_ptr:
122                return NotImplemented
123            else:
124                if isinstance(self, CTypesGenericPrimitive):
125                    self = self._value
126                if isinstance(other, CTypesGenericPrimitive):
127                    other = other._value
128                return cmpfunc(self, other)
129        cmp.func_name = name
130        return cmp
131
132    __eq__ = _make_cmp('__eq__')
133    __ne__ = _make_cmp('__ne__')
134    __lt__ = _make_cmp('__lt__')
135    __le__ = _make_cmp('__le__')
136    __gt__ = _make_cmp('__gt__')
137    __ge__ = _make_cmp('__ge__')
138
139    def __hash__(self):
140        return hash(self._convert_to_address(None))
141
142    def _to_string(self, maxlen):
143        raise TypeError("string(): %r" % (self,))
144
145
146class CTypesGenericPrimitive(CTypesData):
147    __slots__ = []
148
149    def __hash__(self):
150        return hash(self._value)
151
152    def _get_own_repr(self):
153        return repr(self._from_ctypes(self._value))
154
155
156class CTypesGenericArray(CTypesData):
157    __slots__ = []
158
159    @classmethod
160    def _newp(cls, init):
161        return cls(init)
162
163    def __iter__(self):
164        for i in xrange(len(self)):
165            yield self[i]
166
167    def _get_own_repr(self):
168        return self._addr_repr(ctypes.addressof(self._blob))
169
170
171class CTypesGenericPtr(CTypesData):
172    __slots__ = ['_address', '_as_ctype_ptr']
173    _automatic_casts = False
174    kind = "pointer"
175
176    @classmethod
177    def _newp(cls, init):
178        return cls(init)
179
180    @classmethod
181    def _cast_from(cls, source):
182        if source is None:
183            address = 0
184        elif isinstance(source, CTypesData):
185            address = source._cast_to_integer()
186        elif isinstance(source, (int, long)):
187            address = source
188        else:
189            raise TypeError("bad type for cast to %r: %r" %
190                            (cls, type(source).__name__))
191        return cls._new_pointer_at(address)
192
193    @classmethod
194    def _new_pointer_at(cls, address):
195        self = cls.__new__(cls)
196        self._address = address
197        self._as_ctype_ptr = ctypes.cast(address, cls._ctype)
198        return self
199
200    def _get_own_repr(self):
201        try:
202            return self._addr_repr(self._address)
203        except AttributeError:
204            return '???'
205
206    def _cast_to_integer(self):
207        return self._address
208
209    def __nonzero__(self):
210        return bool(self._address)
211    __bool__ = __nonzero__
212
213    @classmethod
214    def _to_ctypes(cls, value):
215        if not isinstance(value, CTypesData):
216            raise TypeError("unexpected %s object" % type(value).__name__)
217        address = value._convert_to_address(cls)
218        return ctypes.cast(address, cls._ctype)
219
220    @classmethod
221    def _from_ctypes(cls, ctypes_ptr):
222        address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0
223        return cls._new_pointer_at(address)
224
225    @classmethod
226    def _initialize(cls, ctypes_ptr, value):
227        if value:
228            ctypes_ptr.contents = cls._to_ctypes(value).contents
229
230    def _convert_to_address(self, BClass):
231        if (BClass in (self.__class__, None) or BClass._automatic_casts
232            or self._automatic_casts):
233            return self._address
234        else:
235            return CTypesData._convert_to_address(self, BClass)
236
237
238class CTypesBaseStructOrUnion(CTypesData):
239    __slots__ = ['_blob']
240
241    @classmethod
242    def _create_ctype_obj(cls, init):
243        # may be overridden
244        raise TypeError("cannot instantiate opaque type %s" % (cls,))
245
246    def _get_own_repr(self):
247        return self._addr_repr(ctypes.addressof(self._blob))
248
249    @classmethod
250    def _offsetof(cls, fieldname):
251        return getattr(cls._ctype, fieldname).offset
252
253    def _convert_to_address(self, BClass):
254        if getattr(BClass, '_BItem', None) is self.__class__:
255            return ctypes.addressof(self._blob)
256        else:
257            return CTypesData._convert_to_address(self, BClass)
258
259    @classmethod
260    def _from_ctypes(cls, ctypes_struct_or_union):
261        self = cls.__new__(cls)
262        self._blob = ctypes_struct_or_union
263        return self
264
265    @classmethod
266    def _to_ctypes(cls, value):
267        return value._blob
268
269    def __repr__(self, c_name=None):
270        return CTypesData.__repr__(self, c_name or self._get_c_name(' &'))
271
272
273class CTypesBackend(object):
274
275    PRIMITIVE_TYPES = {
276        'char': ctypes.c_char,
277        'short': ctypes.c_short,
278        'int': ctypes.c_int,
279        'long': ctypes.c_long,
280        'long long': ctypes.c_longlong,
281        'signed char': ctypes.c_byte,
282        'unsigned char': ctypes.c_ubyte,
283        'unsigned short': ctypes.c_ushort,
284        'unsigned int': ctypes.c_uint,
285        'unsigned long': ctypes.c_ulong,
286        'unsigned long long': ctypes.c_ulonglong,
287        'float': ctypes.c_float,
288        'double': ctypes.c_double,
289        '_Bool': ctypes.c_bool,
290        }
291
292    for _name in ['unsigned long long', 'unsigned long',
293                  'unsigned int', 'unsigned short', 'unsigned char']:
294        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
295        PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
296        if _size == ctypes.sizeof(ctypes.c_void_p):
297            PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name]
298        if _size == ctypes.sizeof(ctypes.c_size_t):
299            PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name]
300
301    for _name in ['long long', 'long', 'int', 'short', 'signed char']:
302        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
303        PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
304        if _size == ctypes.sizeof(ctypes.c_void_p):
305            PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name]
306            PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name]
307        if _size == ctypes.sizeof(ctypes.c_size_t):
308            PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name]
309
310
311    def __init__(self):
312        self.RTLD_LAZY = 0   # not supported anyway by ctypes
313        self.RTLD_NOW  = 0
314        self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL
315        self.RTLD_LOCAL = ctypes.RTLD_LOCAL
316
317    def set_ffi(self, ffi):
318        self.ffi = ffi
319
320    def _get_types(self):
321        return CTypesData, CTypesType
322
323    def load_library(self, path, flags=0):
324        cdll = ctypes.CDLL(path, flags)
325        return CTypesLibrary(self, cdll)
326
327    def new_void_type(self):
328        class CTypesVoid(CTypesData):
329            __slots__ = []
330            _reftypename = 'void &'
331            @staticmethod
332            def _from_ctypes(novalue):
333                return None
334            @staticmethod
335            def _to_ctypes(novalue):
336                if novalue is not None:
337                    raise TypeError("None expected, got %s object" %
338                                    (type(novalue).__name__,))
339                return None
340        CTypesVoid._fix_class()
341        return CTypesVoid
342
343    def new_primitive_type(self, name):
344        if name == 'wchar_t':
345            raise NotImplementedError(name)
346        ctype = self.PRIMITIVE_TYPES[name]
347        if name == 'char':
348            kind = 'char'
349        elif name in ('float', 'double'):
350            kind = 'float'
351        else:
352            if name in ('signed char', 'unsigned char'):
353                kind = 'byte'
354            elif name == '_Bool':
355                kind = 'bool'
356            else:
357                kind = 'int'
358            is_signed = (ctype(-1).value == -1)
359        #
360        def _cast_source_to_int(source):
361            if isinstance(source, (int, long, float)):
362                source = int(source)
363            elif isinstance(source, CTypesData):
364                source = source._cast_to_integer()
365            elif isinstance(source, bytes):
366                source = ord(source)
367            elif source is None:
368                source = 0
369            else:
370                raise TypeError("bad type for cast to %r: %r" %
371                                (CTypesPrimitive, type(source).__name__))
372            return source
373        #
374        kind1 = kind
375        class CTypesPrimitive(CTypesGenericPrimitive):
376            __slots__ = ['_value']
377            _ctype = ctype
378            _reftypename = '%s &' % name
379            kind = kind1
380
381            def __init__(self, value):
382                self._value = value
383
384            @staticmethod
385            def _create_ctype_obj(init):
386                if init is None:
387                    return ctype()
388                return ctype(CTypesPrimitive._to_ctypes(init))
389
390            if kind == 'int' or kind == 'byte':
391                @classmethod
392                def _cast_from(cls, source):
393                    source = _cast_source_to_int(source)
394                    source = ctype(source).value     # cast within range
395                    return cls(source)
396                def __int__(self):
397                    return self._value
398
399            if kind == 'bool':
400                @classmethod
401                def _cast_from(cls, source):
402                    if not isinstance(source, (int, long, float)):
403                        source = _cast_source_to_int(source)
404                    return cls(bool(source))
405                def __int__(self):
406                    return self._value
407
408            if kind == 'char':
409                @classmethod
410                def _cast_from(cls, source):
411                    source = _cast_source_to_int(source)
412                    source = bytechr(source & 0xFF)
413                    return cls(source)
414                def __int__(self):
415                    return ord(self._value)
416
417            if kind == 'float':
418                @classmethod
419                def _cast_from(cls, source):
420                    if isinstance(source, float):
421                        pass
422                    elif isinstance(source, CTypesGenericPrimitive):
423                        if hasattr(source, '__float__'):
424                            source = float(source)
425                        else:
426                            source = int(source)
427                    else:
428                        source = _cast_source_to_int(source)
429                    source = ctype(source).value     # fix precision
430                    return cls(source)
431                def __int__(self):
432                    return int(self._value)
433                def __float__(self):
434                    return self._value
435
436            _cast_to_integer = __int__
437
438            if kind == 'int' or kind == 'byte' or kind == 'bool':
439                @staticmethod
440                def _to_ctypes(x):
441                    if not isinstance(x, (int, long)):
442                        if isinstance(x, CTypesData):
443                            x = int(x)
444                        else:
445                            raise TypeError("integer expected, got %s" %
446                                            type(x).__name__)
447                    if ctype(x).value != x:
448                        if not is_signed and x < 0:
449                            raise OverflowError("%s: negative integer" % name)
450                        else:
451                            raise OverflowError("%s: integer out of bounds"
452                                                % name)
453                    return x
454
455            if kind == 'char':
456                @staticmethod
457                def _to_ctypes(x):
458                    if isinstance(x, bytes) and len(x) == 1:
459                        return x
460                    if isinstance(x, CTypesPrimitive):    # <CData <char>>
461                        return x._value
462                    raise TypeError("character expected, got %s" %
463                                    type(x).__name__)
464                def __nonzero__(self):
465                    return ord(self._value) != 0
466            else:
467                def __nonzero__(self):
468                    return self._value != 0
469            __bool__ = __nonzero__
470
471            if kind == 'float':
472                @staticmethod
473                def _to_ctypes(x):
474                    if not isinstance(x, (int, long, float, CTypesData)):
475                        raise TypeError("float expected, got %s" %
476                                        type(x).__name__)
477                    return ctype(x).value
478
479            @staticmethod
480            def _from_ctypes(value):
481                return getattr(value, 'value', value)
482
483            @staticmethod
484            def _initialize(blob, init):
485                blob.value = CTypesPrimitive._to_ctypes(init)
486
487            if kind == 'char':
488                def _to_string(self, maxlen):
489                    return self._value
490            if kind == 'byte':
491                def _to_string(self, maxlen):
492                    return chr(self._value & 0xff)
493        #
494        CTypesPrimitive._fix_class()
495        return CTypesPrimitive
496
497    def new_pointer_type(self, BItem):
498        getbtype = self.ffi._get_cached_btype
499        if BItem is getbtype(model.PrimitiveType('char')):
500            kind = 'charp'
501        elif BItem in (getbtype(model.PrimitiveType('signed char')),
502                       getbtype(model.PrimitiveType('unsigned char'))):
503            kind = 'bytep'
504        elif BItem is getbtype(model.void_type):
505            kind = 'voidp'
506        else:
507            kind = 'generic'
508        #
509        class CTypesPtr(CTypesGenericPtr):
510            __slots__ = ['_own']
511            if kind == 'charp':
512                __slots__ += ['__as_strbuf']
513            _BItem = BItem
514            if hasattr(BItem, '_ctype'):
515                _ctype = ctypes.POINTER(BItem._ctype)
516                _bitem_size = ctypes.sizeof(BItem._ctype)
517            else:
518                _ctype = ctypes.c_void_p
519            if issubclass(BItem, CTypesGenericArray):
520                _reftypename = BItem._get_c_name('(* &)')
521            else:
522                _reftypename = BItem._get_c_name(' * &')
523
524            def __init__(self, init):
525                ctypeobj = BItem._create_ctype_obj(init)
526                if kind == 'charp':
527                    self.__as_strbuf = ctypes.create_string_buffer(
528                        ctypeobj.value + b'\x00')
529                    self._as_ctype_ptr = ctypes.cast(
530                        self.__as_strbuf, self._ctype)
531                else:
532                    self._as_ctype_ptr = ctypes.pointer(ctypeobj)
533                self._address = ctypes.cast(self._as_ctype_ptr,
534                                            ctypes.c_void_p).value
535                self._own = True
536
537            def __add__(self, other):
538                if isinstance(other, (int, long)):
539                    return self._new_pointer_at(self._address +
540                                                other * self._bitem_size)
541                else:
542                    return NotImplemented
543
544            def __sub__(self, other):
545                if isinstance(other, (int, long)):
546                    return self._new_pointer_at(self._address -
547                                                other * self._bitem_size)
548                elif type(self) is type(other):
549                    return (self._address - other._address) // self._bitem_size
550                else:
551                    return NotImplemented
552
553            def __getitem__(self, index):
554                if getattr(self, '_own', False) and index != 0:
555                    raise IndexError
556                return BItem._from_ctypes(self._as_ctype_ptr[index])
557
558            def __setitem__(self, index, value):
559                self._as_ctype_ptr[index] = BItem._to_ctypes(value)
560
561            if kind == 'charp' or kind == 'voidp':
562                @classmethod
563                def _arg_to_ctypes(cls, *value):
564                    if value and isinstance(value[0], bytes):
565                        return ctypes.c_char_p(value[0])
566                    else:
567                        return super(CTypesPtr, cls)._arg_to_ctypes(*value)
568
569            if kind == 'charp' or kind == 'bytep':
570                def _to_string(self, maxlen):
571                    if maxlen < 0:
572                        maxlen = sys.maxsize
573                    p = ctypes.cast(self._as_ctype_ptr,
574                                    ctypes.POINTER(ctypes.c_char))
575                    n = 0
576                    while n < maxlen and p[n] != b'\x00':
577                        n += 1
578                    return b''.join([p[i] for i in range(n)])
579
580            def _get_own_repr(self):
581                if getattr(self, '_own', False):
582                    return 'owning %d bytes' % (
583                        ctypes.sizeof(self._as_ctype_ptr.contents),)
584                return super(CTypesPtr, self)._get_own_repr()
585        #
586        if (BItem is self.ffi._get_cached_btype(model.void_type) or
587            BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))):
588            CTypesPtr._automatic_casts = True
589        #
590        CTypesPtr._fix_class()
591        return CTypesPtr
592
593    def new_array_type(self, CTypesPtr, length):
594        if length is None:
595            brackets = ' &[]'
596        else:
597            brackets = ' &[%d]' % length
598        BItem = CTypesPtr._BItem
599        getbtype = self.ffi._get_cached_btype
600        if BItem is getbtype(model.PrimitiveType('char')):
601            kind = 'char'
602        elif BItem in (getbtype(model.PrimitiveType('signed char')),
603                       getbtype(model.PrimitiveType('unsigned char'))):
604            kind = 'byte'
605        else:
606            kind = 'generic'
607        #
608        class CTypesArray(CTypesGenericArray):
609            __slots__ = ['_blob', '_own']
610            if length is not None:
611                _ctype = BItem._ctype * length
612            else:
613                __slots__.append('_ctype')
614            _reftypename = BItem._get_c_name(brackets)
615            _declared_length = length
616            _CTPtr = CTypesPtr
617
618            def __init__(self, init):
619                if length is None:
620                    if isinstance(init, (int, long)):
621                        len1 = init
622                        init = None
623                    elif kind == 'char' and isinstance(init, bytes):
624                        len1 = len(init) + 1    # extra null
625                    else:
626                        init = tuple(init)
627                        len1 = len(init)
628                    self._ctype = BItem._ctype * len1
629                self._blob = self._ctype()
630                self._own = True
631                if init is not None:
632                    self._initialize(self._blob, init)
633
634            @staticmethod
635            def _initialize(blob, init):
636                if isinstance(init, bytes):
637                    init = [init[i:i+1] for i in range(len(init))]
638                else:
639                    if isinstance(init, CTypesGenericArray):
640                        if (len(init) != len(blob) or
641                            not isinstance(init, CTypesArray)):
642                            raise TypeError("length/type mismatch: %s" % (init,))
643                    init = tuple(init)
644                if len(init) > len(blob):
645                    raise IndexError("too many initializers")
646                addr = ctypes.cast(blob, ctypes.c_void_p).value
647                PTR = ctypes.POINTER(BItem._ctype)
648                itemsize = ctypes.sizeof(BItem._ctype)
649                for i, value in enumerate(init):
650                    p = ctypes.cast(addr + i * itemsize, PTR)
651                    BItem._initialize(p.contents, value)
652
653            def __len__(self):
654                return len(self._blob)
655
656            def __getitem__(self, index):
657                if not (0 <= index < len(self._blob)):
658                    raise IndexError
659                return BItem._from_ctypes(self._blob[index])
660
661            def __setitem__(self, index, value):
662                if not (0 <= index < len(self._blob)):
663                    raise IndexError
664                self._blob[index] = BItem._to_ctypes(value)
665
666            if kind == 'char' or kind == 'byte':
667                def _to_string(self, maxlen):
668                    if maxlen < 0:
669                        maxlen = len(self._blob)
670                    p = ctypes.cast(self._blob,
671                                    ctypes.POINTER(ctypes.c_char))
672                    n = 0
673                    while n < maxlen and p[n] != b'\x00':
674                        n += 1
675                    return b''.join([p[i] for i in range(n)])
676
677            def _get_own_repr(self):
678                if getattr(self, '_own', False):
679                    return 'owning %d bytes' % (ctypes.sizeof(self._blob),)
680                return super(CTypesArray, self)._get_own_repr()
681
682            def _convert_to_address(self, BClass):
683                if BClass in (CTypesPtr, None) or BClass._automatic_casts:
684                    return ctypes.addressof(self._blob)
685                else:
686                    return CTypesData._convert_to_address(self, BClass)
687
688            @staticmethod
689            def _from_ctypes(ctypes_array):
690                self = CTypesArray.__new__(CTypesArray)
691                self._blob = ctypes_array
692                return self
693
694            @staticmethod
695            def _arg_to_ctypes(value):
696                return CTypesPtr._arg_to_ctypes(value)
697
698            def __add__(self, other):
699                if isinstance(other, (int, long)):
700                    return CTypesPtr._new_pointer_at(
701                        ctypes.addressof(self._blob) +
702                        other * ctypes.sizeof(BItem._ctype))
703                else:
704                    return NotImplemented
705
706            @classmethod
707            def _cast_from(cls, source):
708                raise NotImplementedError("casting to %r" % (
709                    cls._get_c_name(),))
710        #
711        CTypesArray._fix_class()
712        return CTypesArray
713
714    def _new_struct_or_union(self, kind, name, base_ctypes_class):
715        #
716        class struct_or_union(base_ctypes_class):
717            pass
718        struct_or_union.__name__ = '%s_%s' % (kind, name)
719        kind1 = kind
720        #
721        class CTypesStructOrUnion(CTypesBaseStructOrUnion):
722            __slots__ = ['_blob']
723            _ctype = struct_or_union
724            _reftypename = '%s &' % (name,)
725            _kind = kind = kind1
726        #
727        CTypesStructOrUnion._fix_class()
728        return CTypesStructOrUnion
729
730    def new_struct_type(self, name):
731        return self._new_struct_or_union('struct', name, ctypes.Structure)
732
733    def new_union_type(self, name):
734        return self._new_struct_or_union('union', name, ctypes.Union)
735
736    def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp,
737                                 totalsize=-1, totalalignment=-1, sflags=0,
738                                 pack=0):
739        if totalsize >= 0 or totalalignment >= 0:
740            raise NotImplementedError("the ctypes backend of CFFI does not support "
741                                      "structures completed by verify(); please "
742                                      "compile and install the _cffi_backend module.")
743        struct_or_union = CTypesStructOrUnion._ctype
744        fnames = [fname for (fname, BField, bitsize) in fields]
745        btypes = [BField for (fname, BField, bitsize) in fields]
746        bitfields = [bitsize for (fname, BField, bitsize) in fields]
747        #
748        bfield_types = {}
749        cfields = []
750        for (fname, BField, bitsize) in fields:
751            if bitsize < 0:
752                cfields.append((fname, BField._ctype))
753                bfield_types[fname] = BField
754            else:
755                cfields.append((fname, BField._ctype, bitsize))
756                bfield_types[fname] = Ellipsis
757        if sflags & 8:
758            struct_or_union._pack_ = 1
759        elif pack:
760            struct_or_union._pack_ = pack
761        struct_or_union._fields_ = cfields
762        CTypesStructOrUnion._bfield_types = bfield_types
763        #
764        @staticmethod
765        def _create_ctype_obj(init):
766            result = struct_or_union()
767            if init is not None:
768                initialize(result, init)
769            return result
770        CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
771        #
772        def initialize(blob, init):
773            if is_union:
774                if len(init) > 1:
775                    raise ValueError("union initializer: %d items given, but "
776                                    "only one supported (use a dict if needed)"
777                                     % (len(init),))
778            if not isinstance(init, dict):
779                if isinstance(init, (bytes, unicode)):
780                    raise TypeError("union initializer: got a str")
781                init = tuple(init)
782                if len(init) > len(fnames):
783                    raise ValueError("too many values for %s initializer" %
784                                     CTypesStructOrUnion._get_c_name())
785                init = dict(zip(fnames, init))
786            addr = ctypes.addressof(blob)
787            for fname, value in init.items():
788                BField, bitsize = name2fieldtype[fname]
789                assert bitsize < 0, \
790                       "not implemented: initializer with bit fields"
791                offset = CTypesStructOrUnion._offsetof(fname)
792                PTR = ctypes.POINTER(BField._ctype)
793                p = ctypes.cast(addr + offset, PTR)
794                BField._initialize(p.contents, value)
795        is_union = CTypesStructOrUnion._kind == 'union'
796        name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
797        #
798        for fname, BField, bitsize in fields:
799            if fname == '':
800                raise NotImplementedError("nested anonymous structs/unions")
801            if hasattr(CTypesStructOrUnion, fname):
802                raise ValueError("the field name %r conflicts in "
803                                 "the ctypes backend" % fname)
804            if bitsize < 0:
805                def getter(self, fname=fname, BField=BField,
806                           offset=CTypesStructOrUnion._offsetof(fname),
807                           PTR=ctypes.POINTER(BField._ctype)):
808                    addr = ctypes.addressof(self._blob)
809                    p = ctypes.cast(addr + offset, PTR)
810                    return BField._from_ctypes(p.contents)
811                def setter(self, value, fname=fname, BField=BField):
812                    setattr(self._blob, fname, BField._to_ctypes(value))
813                #
814                if issubclass(BField, CTypesGenericArray):
815                    setter = None
816                    if BField._declared_length == 0:
817                        def getter(self, fname=fname, BFieldPtr=BField._CTPtr,
818                                   offset=CTypesStructOrUnion._offsetof(fname),
819                                   PTR=ctypes.POINTER(BField._ctype)):
820                            addr = ctypes.addressof(self._blob)
821                            p = ctypes.cast(addr + offset, PTR)
822                            return BFieldPtr._from_ctypes(p)
823                #
824            else:
825                def getter(self, fname=fname, BField=BField):
826                    return BField._from_ctypes(getattr(self._blob, fname))
827                def setter(self, value, fname=fname, BField=BField):
828                    # xxx obscure workaround
829                    value = BField._to_ctypes(value)
830                    oldvalue = getattr(self._blob, fname)
831                    setattr(self._blob, fname, value)
832                    if value != getattr(self._blob, fname):
833                        setattr(self._blob, fname, oldvalue)
834                        raise OverflowError("value too large for bitfield")
835            setattr(CTypesStructOrUnion, fname, property(getter, setter))
836        #
837        CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp))
838        for fname in fnames:
839            if hasattr(CTypesPtr, fname):
840                raise ValueError("the field name %r conflicts in "
841                                 "the ctypes backend" % fname)
842            def getter(self, fname=fname):
843                return getattr(self[0], fname)
844            def setter(self, value, fname=fname):
845                setattr(self[0], fname, value)
846            setattr(CTypesPtr, fname, property(getter, setter))
847
848    def new_function_type(self, BArgs, BResult, has_varargs):
849        nameargs = [BArg._get_c_name() for BArg in BArgs]
850        if has_varargs:
851            nameargs.append('...')
852        nameargs = ', '.join(nameargs)
853        #
854        class CTypesFunctionPtr(CTypesGenericPtr):
855            __slots__ = ['_own_callback', '_name']
856            _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None),
857                                      *[BArg._ctype for BArg in BArgs],
858                                      use_errno=True)
859            _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,))
860
861            def __init__(self, init, error=None):
862                # create a callback to the Python callable init()
863                import traceback
864                assert not has_varargs, "varargs not supported for callbacks"
865                if getattr(BResult, '_ctype', None) is not None:
866                    error = BResult._from_ctypes(
867                        BResult._create_ctype_obj(error))
868                else:
869                    error = None
870                def callback(*args):
871                    args2 = []
872                    for arg, BArg in zip(args, BArgs):
873                        args2.append(BArg._from_ctypes(arg))
874                    try:
875                        res2 = init(*args2)
876                        res2 = BResult._to_ctypes(res2)
877                    except:
878                        traceback.print_exc()
879                        res2 = error
880                    if issubclass(BResult, CTypesGenericPtr):
881                        if res2:
882                            res2 = ctypes.cast(res2, ctypes.c_void_p).value
883                                # .value: http://bugs.python.org/issue1574593
884                        else:
885                            res2 = None
886                    #print repr(res2)
887                    return res2
888                if issubclass(BResult, CTypesGenericPtr):
889                    # The only pointers callbacks can return are void*s:
890                    # http://bugs.python.org/issue5710
891                    callback_ctype = ctypes.CFUNCTYPE(
892                        ctypes.c_void_p,
893                        *[BArg._ctype for BArg in BArgs],
894                        use_errno=True)
895                else:
896                    callback_ctype = CTypesFunctionPtr._ctype
897                self._as_ctype_ptr = callback_ctype(callback)
898                self._address = ctypes.cast(self._as_ctype_ptr,
899                                            ctypes.c_void_p).value
900                self._own_callback = init
901
902            @staticmethod
903            def _initialize(ctypes_ptr, value):
904                if value:
905                    raise NotImplementedError("ctypes backend: not supported: "
906                                          "initializers for function pointers")
907
908            def __repr__(self):
909                c_name = getattr(self, '_name', None)
910                if c_name:
911                    i = self._reftypename.index('(* &)')
912                    if self._reftypename[i-1] not in ' )*':
913                        c_name = ' ' + c_name
914                    c_name = self._reftypename.replace('(* &)', c_name)
915                return CTypesData.__repr__(self, c_name)
916
917            def _get_own_repr(self):
918                if getattr(self, '_own_callback', None) is not None:
919                    return 'calling %r' % (self._own_callback,)
920                return super(CTypesFunctionPtr, self)._get_own_repr()
921
922            def __call__(self, *args):
923                if has_varargs:
924                    assert len(args) >= len(BArgs)
925                    extraargs = args[len(BArgs):]
926                    args = args[:len(BArgs)]
927                else:
928                    assert len(args) == len(BArgs)
929                ctypes_args = []
930                for arg, BArg in zip(args, BArgs):
931                    ctypes_args.append(BArg._arg_to_ctypes(arg))
932                if has_varargs:
933                    for i, arg in enumerate(extraargs):
934                        if arg is None:
935                            ctypes_args.append(ctypes.c_void_p(0))  # NULL
936                            continue
937                        if not isinstance(arg, CTypesData):
938                            raise TypeError(
939                                "argument %d passed in the variadic part "
940                                "needs to be a cdata object (got %s)" %
941                                (1 + len(BArgs) + i, type(arg).__name__))
942                        ctypes_args.append(arg._arg_to_ctypes(arg))
943                result = self._as_ctype_ptr(*ctypes_args)
944                return BResult._from_ctypes(result)
945        #
946        CTypesFunctionPtr._fix_class()
947        return CTypesFunctionPtr
948
949    def new_enum_type(self, name, enumerators, enumvalues, CTypesInt):
950        assert isinstance(name, str)
951        reverse_mapping = dict(zip(reversed(enumvalues),
952                                   reversed(enumerators)))
953        #
954        class CTypesEnum(CTypesInt):
955            __slots__ = []
956            _reftypename = '%s &' % name
957
958            def _get_own_repr(self):
959                value = self._value
960                try:
961                    return '%d: %s' % (value, reverse_mapping[value])
962                except KeyError:
963                    return str(value)
964
965            def _to_string(self, maxlen):
966                value = self._value
967                try:
968                    return reverse_mapping[value]
969                except KeyError:
970                    return str(value)
971        #
972        CTypesEnum._fix_class()
973        return CTypesEnum
974
975    def get_errno(self):
976        return ctypes.get_errno()
977
978    def set_errno(self, value):
979        ctypes.set_errno(value)
980
981    def string(self, b, maxlen=-1):
982        return b._to_string(maxlen)
983
984    def buffer(self, bptr, size=-1):
985        raise NotImplementedError("buffer() with ctypes backend")
986
987    def sizeof(self, cdata_or_BType):
988        if isinstance(cdata_or_BType, CTypesData):
989            return cdata_or_BType._get_size_of_instance()
990        else:
991            assert issubclass(cdata_or_BType, CTypesData)
992            return cdata_or_BType._get_size()
993
994    def alignof(self, BType):
995        assert issubclass(BType, CTypesData)
996        return BType._alignment()
997
998    def newp(self, BType, source):
999        if not issubclass(BType, CTypesData):
1000            raise TypeError
1001        return BType._newp(source)
1002
1003    def cast(self, BType, source):
1004        return BType._cast_from(source)
1005
1006    def callback(self, BType, source, error, onerror):
1007        assert onerror is None   # XXX not implemented
1008        return BType(source, error)
1009
1010    _weakref_cache_ref = None
1011
1012    def gcp(self, cdata, destructor, size=0):
1013        if self._weakref_cache_ref is None:
1014            import weakref
1015            class MyRef(weakref.ref):
1016                def __eq__(self, other):
1017                    myref = self()
1018                    return self is other or (
1019                        myref is not None and myref is other())
1020                def __ne__(self, other):
1021                    return not (self == other)
1022                def __hash__(self):
1023                    try:
1024                        return self._hash
1025                    except AttributeError:
1026                        self._hash = hash(self())
1027                        return self._hash
1028            self._weakref_cache_ref = {}, MyRef
1029        weak_cache, MyRef = self._weakref_cache_ref
1030
1031        if destructor is None:
1032            try:
1033                del weak_cache[MyRef(cdata)]
1034            except KeyError:
1035                raise TypeError("Can remove destructor only on a object "
1036                                "previously returned by ffi.gc()")
1037            return None
1038
1039        def remove(k):
1040            cdata, destructor = weak_cache.pop(k, (None, None))
1041            if destructor is not None:
1042                destructor(cdata)
1043
1044        new_cdata = self.cast(self.typeof(cdata), cdata)
1045        assert new_cdata is not cdata
1046        weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor)
1047        return new_cdata
1048
1049    typeof = type
1050
1051    def getcname(self, BType, replace_with):
1052        return BType._get_c_name(replace_with)
1053
1054    def typeoffsetof(self, BType, fieldname, num=0):
1055        if isinstance(fieldname, str):
1056            if num == 0 and issubclass(BType, CTypesGenericPtr):
1057                BType = BType._BItem
1058            if not issubclass(BType, CTypesBaseStructOrUnion):
1059                raise TypeError("expected a struct or union ctype")
1060            BField = BType._bfield_types[fieldname]
1061            if BField is Ellipsis:
1062                raise TypeError("not supported for bitfields")
1063            return (BField, BType._offsetof(fieldname))
1064        elif isinstance(fieldname, (int, long)):
1065            if issubclass(BType, CTypesGenericArray):
1066                BType = BType._CTPtr
1067            if not issubclass(BType, CTypesGenericPtr):
1068                raise TypeError("expected an array or ptr ctype")
1069            BItem = BType._BItem
1070            offset = BItem._get_size() * fieldname
1071            if offset > sys.maxsize:
1072                raise OverflowError
1073            return (BItem, offset)
1074        else:
1075            raise TypeError(type(fieldname))
1076
1077    def rawaddressof(self, BTypePtr, cdata, offset=None):
1078        if isinstance(cdata, CTypesBaseStructOrUnion):
1079            ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata))
1080        elif isinstance(cdata, CTypesGenericPtr):
1081            if offset is None or not issubclass(type(cdata)._BItem,
1082                                                CTypesBaseStructOrUnion):
1083                raise TypeError("unexpected cdata type")
1084            ptr = type(cdata)._to_ctypes(cdata)
1085        elif isinstance(cdata, CTypesGenericArray):
1086            ptr = type(cdata)._to_ctypes(cdata)
1087        else:
1088            raise TypeError("expected a <cdata 'struct-or-union'>")
1089        if offset:
1090            ptr = ctypes.cast(
1091                ctypes.c_void_p(
1092                    ctypes.cast(ptr, ctypes.c_void_p).value + offset),
1093                type(ptr))
1094        return BTypePtr._from_ctypes(ptr)
1095
1096
1097class CTypesLibrary(object):
1098
1099    def __init__(self, backend, cdll):
1100        self.backend = backend
1101        self.cdll = cdll
1102
1103    def load_function(self, BType, name):
1104        c_func = getattr(self.cdll, name)
1105        funcobj = BType._from_ctypes(c_func)
1106        funcobj._name = name
1107        return funcobj
1108
1109    def read_variable(self, BType, name):
1110        try:
1111            ctypes_obj = BType._ctype.in_dll(self.cdll, name)
1112        except AttributeError as e:
1113            raise NotImplementedError(e)
1114        return BType._from_ctypes(ctypes_obj)
1115
1116    def write_variable(self, BType, name, value):
1117        new_ctypes_obj = BType._to_ctypes(value)
1118        ctypes_obj = BType._ctype.in_dll(self.cdll, name)
1119        ctypes.memmove(ctypes.addressof(ctypes_obj),
1120                       ctypes.addressof(new_ctypes_obj),
1121                       ctypes.sizeof(BType._ctype))
1122