forked from STI/Astroport.ONE
1122 lines
42 KiB
Python
1122 lines
42 KiB
Python
import ctypes, ctypes.util, operator, sys
|
|
from . import model
|
|
|
|
if sys.version_info < (3,):
|
|
bytechr = chr
|
|
else:
|
|
unicode = str
|
|
long = int
|
|
xrange = range
|
|
bytechr = lambda num: bytes([num])
|
|
|
|
class CTypesType(type):
|
|
pass
|
|
|
|
class CTypesData(object):
|
|
__metaclass__ = CTypesType
|
|
__slots__ = ['__weakref__']
|
|
__name__ = '<cdata>'
|
|
|
|
def __init__(self, *args):
|
|
raise TypeError("cannot instantiate %r" % (self.__class__,))
|
|
|
|
@classmethod
|
|
def _newp(cls, init):
|
|
raise TypeError("expected a pointer or array ctype, got '%s'"
|
|
% (cls._get_c_name(),))
|
|
|
|
@staticmethod
|
|
def _to_ctypes(value):
|
|
raise TypeError
|
|
|
|
@classmethod
|
|
def _arg_to_ctypes(cls, *value):
|
|
try:
|
|
ctype = cls._ctype
|
|
except AttributeError:
|
|
raise TypeError("cannot create an instance of %r" % (cls,))
|
|
if value:
|
|
res = cls._to_ctypes(*value)
|
|
if not isinstance(res, ctype):
|
|
res = cls._ctype(res)
|
|
else:
|
|
res = cls._ctype()
|
|
return res
|
|
|
|
@classmethod
|
|
def _create_ctype_obj(cls, init):
|
|
if init is None:
|
|
return cls._arg_to_ctypes()
|
|
else:
|
|
return cls._arg_to_ctypes(init)
|
|
|
|
@staticmethod
|
|
def _from_ctypes(ctypes_value):
|
|
raise TypeError
|
|
|
|
@classmethod
|
|
def _get_c_name(cls, replace_with=''):
|
|
return cls._reftypename.replace(' &', replace_with)
|
|
|
|
@classmethod
|
|
def _fix_class(cls):
|
|
cls.__name__ = 'CData<%s>' % (cls._get_c_name(),)
|
|
cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),)
|
|
cls.__module__ = 'ffi'
|
|
|
|
def _get_own_repr(self):
|
|
raise NotImplementedError
|
|
|
|
def _addr_repr(self, address):
|
|
if address == 0:
|
|
return 'NULL'
|
|
else:
|
|
if address < 0:
|
|
address += 1 << (8*ctypes.sizeof(ctypes.c_void_p))
|
|
return '0x%x' % address
|
|
|
|
def __repr__(self, c_name=None):
|
|
own = self._get_own_repr()
|
|
return '<cdata %r %s>' % (c_name or self._get_c_name(), own)
|
|
|
|
def _convert_to_address(self, BClass):
|
|
if BClass is None:
|
|
raise TypeError("cannot convert %r to an address" % (
|
|
self._get_c_name(),))
|
|
else:
|
|
raise TypeError("cannot convert %r to %r" % (
|
|
self._get_c_name(), BClass._get_c_name()))
|
|
|
|
@classmethod
|
|
def _get_size(cls):
|
|
return ctypes.sizeof(cls._ctype)
|
|
|
|
def _get_size_of_instance(self):
|
|
return ctypes.sizeof(self._ctype)
|
|
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
raise TypeError("cannot cast to %r" % (cls._get_c_name(),))
|
|
|
|
def _cast_to_integer(self):
|
|
return self._convert_to_address(None)
|
|
|
|
@classmethod
|
|
def _alignment(cls):
|
|
return ctypes.alignment(cls._ctype)
|
|
|
|
def __iter__(self):
|
|
raise TypeError("cdata %r does not support iteration" % (
|
|
self._get_c_name()),)
|
|
|
|
def _make_cmp(name):
|
|
cmpfunc = getattr(operator, name)
|
|
def cmp(self, other):
|
|
v_is_ptr = not isinstance(self, CTypesGenericPrimitive)
|
|
w_is_ptr = (isinstance(other, CTypesData) and
|
|
not isinstance(other, CTypesGenericPrimitive))
|
|
if v_is_ptr and w_is_ptr:
|
|
return cmpfunc(self._convert_to_address(None),
|
|
other._convert_to_address(None))
|
|
elif v_is_ptr or w_is_ptr:
|
|
return NotImplemented
|
|
else:
|
|
if isinstance(self, CTypesGenericPrimitive):
|
|
self = self._value
|
|
if isinstance(other, CTypesGenericPrimitive):
|
|
other = other._value
|
|
return cmpfunc(self, other)
|
|
cmp.func_name = name
|
|
return cmp
|
|
|
|
__eq__ = _make_cmp('__eq__')
|
|
__ne__ = _make_cmp('__ne__')
|
|
__lt__ = _make_cmp('__lt__')
|
|
__le__ = _make_cmp('__le__')
|
|
__gt__ = _make_cmp('__gt__')
|
|
__ge__ = _make_cmp('__ge__')
|
|
|
|
def __hash__(self):
|
|
return hash(self._convert_to_address(None))
|
|
|
|
def _to_string(self, maxlen):
|
|
raise TypeError("string(): %r" % (self,))
|
|
|
|
|
|
class CTypesGenericPrimitive(CTypesData):
|
|
__slots__ = []
|
|
|
|
def __hash__(self):
|
|
return hash(self._value)
|
|
|
|
def _get_own_repr(self):
|
|
return repr(self._from_ctypes(self._value))
|
|
|
|
|
|
class CTypesGenericArray(CTypesData):
|
|
__slots__ = []
|
|
|
|
@classmethod
|
|
def _newp(cls, init):
|
|
return cls(init)
|
|
|
|
def __iter__(self):
|
|
for i in xrange(len(self)):
|
|
yield self[i]
|
|
|
|
def _get_own_repr(self):
|
|
return self._addr_repr(ctypes.addressof(self._blob))
|
|
|
|
|
|
class CTypesGenericPtr(CTypesData):
|
|
__slots__ = ['_address', '_as_ctype_ptr']
|
|
_automatic_casts = False
|
|
kind = "pointer"
|
|
|
|
@classmethod
|
|
def _newp(cls, init):
|
|
return cls(init)
|
|
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
if source is None:
|
|
address = 0
|
|
elif isinstance(source, CTypesData):
|
|
address = source._cast_to_integer()
|
|
elif isinstance(source, (int, long)):
|
|
address = source
|
|
else:
|
|
raise TypeError("bad type for cast to %r: %r" %
|
|
(cls, type(source).__name__))
|
|
return cls._new_pointer_at(address)
|
|
|
|
@classmethod
|
|
def _new_pointer_at(cls, address):
|
|
self = cls.__new__(cls)
|
|
self._address = address
|
|
self._as_ctype_ptr = ctypes.cast(address, cls._ctype)
|
|
return self
|
|
|
|
def _get_own_repr(self):
|
|
try:
|
|
return self._addr_repr(self._address)
|
|
except AttributeError:
|
|
return '???'
|
|
|
|
def _cast_to_integer(self):
|
|
return self._address
|
|
|
|
def __nonzero__(self):
|
|
return bool(self._address)
|
|
__bool__ = __nonzero__
|
|
|
|
@classmethod
|
|
def _to_ctypes(cls, value):
|
|
if not isinstance(value, CTypesData):
|
|
raise TypeError("unexpected %s object" % type(value).__name__)
|
|
address = value._convert_to_address(cls)
|
|
return ctypes.cast(address, cls._ctype)
|
|
|
|
@classmethod
|
|
def _from_ctypes(cls, ctypes_ptr):
|
|
address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0
|
|
return cls._new_pointer_at(address)
|
|
|
|
@classmethod
|
|
def _initialize(cls, ctypes_ptr, value):
|
|
if value:
|
|
ctypes_ptr.contents = cls._to_ctypes(value).contents
|
|
|
|
def _convert_to_address(self, BClass):
|
|
if (BClass in (self.__class__, None) or BClass._automatic_casts
|
|
or self._automatic_casts):
|
|
return self._address
|
|
else:
|
|
return CTypesData._convert_to_address(self, BClass)
|
|
|
|
|
|
class CTypesBaseStructOrUnion(CTypesData):
|
|
__slots__ = ['_blob']
|
|
|
|
@classmethod
|
|
def _create_ctype_obj(cls, init):
|
|
# may be overridden
|
|
raise TypeError("cannot instantiate opaque type %s" % (cls,))
|
|
|
|
def _get_own_repr(self):
|
|
return self._addr_repr(ctypes.addressof(self._blob))
|
|
|
|
@classmethod
|
|
def _offsetof(cls, fieldname):
|
|
return getattr(cls._ctype, fieldname).offset
|
|
|
|
def _convert_to_address(self, BClass):
|
|
if getattr(BClass, '_BItem', None) is self.__class__:
|
|
return ctypes.addressof(self._blob)
|
|
else:
|
|
return CTypesData._convert_to_address(self, BClass)
|
|
|
|
@classmethod
|
|
def _from_ctypes(cls, ctypes_struct_or_union):
|
|
self = cls.__new__(cls)
|
|
self._blob = ctypes_struct_or_union
|
|
return self
|
|
|
|
@classmethod
|
|
def _to_ctypes(cls, value):
|
|
return value._blob
|
|
|
|
def __repr__(self, c_name=None):
|
|
return CTypesData.__repr__(self, c_name or self._get_c_name(' &'))
|
|
|
|
|
|
class CTypesBackend(object):
|
|
|
|
PRIMITIVE_TYPES = {
|
|
'char': ctypes.c_char,
|
|
'short': ctypes.c_short,
|
|
'int': ctypes.c_int,
|
|
'long': ctypes.c_long,
|
|
'long long': ctypes.c_longlong,
|
|
'signed char': ctypes.c_byte,
|
|
'unsigned char': ctypes.c_ubyte,
|
|
'unsigned short': ctypes.c_ushort,
|
|
'unsigned int': ctypes.c_uint,
|
|
'unsigned long': ctypes.c_ulong,
|
|
'unsigned long long': ctypes.c_ulonglong,
|
|
'float': ctypes.c_float,
|
|
'double': ctypes.c_double,
|
|
'_Bool': ctypes.c_bool,
|
|
}
|
|
|
|
for _name in ['unsigned long long', 'unsigned long',
|
|
'unsigned int', 'unsigned short', 'unsigned char']:
|
|
_size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
|
|
PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
|
|
if _size == ctypes.sizeof(ctypes.c_void_p):
|
|
PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name]
|
|
if _size == ctypes.sizeof(ctypes.c_size_t):
|
|
PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name]
|
|
|
|
for _name in ['long long', 'long', 'int', 'short', 'signed char']:
|
|
_size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
|
|
PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
|
|
if _size == ctypes.sizeof(ctypes.c_void_p):
|
|
PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name]
|
|
PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name]
|
|
if _size == ctypes.sizeof(ctypes.c_size_t):
|
|
PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name]
|
|
|
|
|
|
def __init__(self):
|
|
self.RTLD_LAZY = 0 # not supported anyway by ctypes
|
|
self.RTLD_NOW = 0
|
|
self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL
|
|
self.RTLD_LOCAL = ctypes.RTLD_LOCAL
|
|
|
|
def set_ffi(self, ffi):
|
|
self.ffi = ffi
|
|
|
|
def _get_types(self):
|
|
return CTypesData, CTypesType
|
|
|
|
def load_library(self, path, flags=0):
|
|
cdll = ctypes.CDLL(path, flags)
|
|
return CTypesLibrary(self, cdll)
|
|
|
|
def new_void_type(self):
|
|
class CTypesVoid(CTypesData):
|
|
__slots__ = []
|
|
_reftypename = 'void &'
|
|
@staticmethod
|
|
def _from_ctypes(novalue):
|
|
return None
|
|
@staticmethod
|
|
def _to_ctypes(novalue):
|
|
if novalue is not None:
|
|
raise TypeError("None expected, got %s object" %
|
|
(type(novalue).__name__,))
|
|
return None
|
|
CTypesVoid._fix_class()
|
|
return CTypesVoid
|
|
|
|
def new_primitive_type(self, name):
|
|
if name == 'wchar_t':
|
|
raise NotImplementedError(name)
|
|
ctype = self.PRIMITIVE_TYPES[name]
|
|
if name == 'char':
|
|
kind = 'char'
|
|
elif name in ('float', 'double'):
|
|
kind = 'float'
|
|
else:
|
|
if name in ('signed char', 'unsigned char'):
|
|
kind = 'byte'
|
|
elif name == '_Bool':
|
|
kind = 'bool'
|
|
else:
|
|
kind = 'int'
|
|
is_signed = (ctype(-1).value == -1)
|
|
#
|
|
def _cast_source_to_int(source):
|
|
if isinstance(source, (int, long, float)):
|
|
source = int(source)
|
|
elif isinstance(source, CTypesData):
|
|
source = source._cast_to_integer()
|
|
elif isinstance(source, bytes):
|
|
source = ord(source)
|
|
elif source is None:
|
|
source = 0
|
|
else:
|
|
raise TypeError("bad type for cast to %r: %r" %
|
|
(CTypesPrimitive, type(source).__name__))
|
|
return source
|
|
#
|
|
kind1 = kind
|
|
class CTypesPrimitive(CTypesGenericPrimitive):
|
|
__slots__ = ['_value']
|
|
_ctype = ctype
|
|
_reftypename = '%s &' % name
|
|
kind = kind1
|
|
|
|
def __init__(self, value):
|
|
self._value = value
|
|
|
|
@staticmethod
|
|
def _create_ctype_obj(init):
|
|
if init is None:
|
|
return ctype()
|
|
return ctype(CTypesPrimitive._to_ctypes(init))
|
|
|
|
if kind == 'int' or kind == 'byte':
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
source = _cast_source_to_int(source)
|
|
source = ctype(source).value # cast within range
|
|
return cls(source)
|
|
def __int__(self):
|
|
return self._value
|
|
|
|
if kind == 'bool':
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
if not isinstance(source, (int, long, float)):
|
|
source = _cast_source_to_int(source)
|
|
return cls(bool(source))
|
|
def __int__(self):
|
|
return int(self._value)
|
|
|
|
if kind == 'char':
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
source = _cast_source_to_int(source)
|
|
source = bytechr(source & 0xFF)
|
|
return cls(source)
|
|
def __int__(self):
|
|
return ord(self._value)
|
|
|
|
if kind == 'float':
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
if isinstance(source, float):
|
|
pass
|
|
elif isinstance(source, CTypesGenericPrimitive):
|
|
if hasattr(source, '__float__'):
|
|
source = float(source)
|
|
else:
|
|
source = int(source)
|
|
else:
|
|
source = _cast_source_to_int(source)
|
|
source = ctype(source).value # fix precision
|
|
return cls(source)
|
|
def __int__(self):
|
|
return int(self._value)
|
|
def __float__(self):
|
|
return self._value
|
|
|
|
_cast_to_integer = __int__
|
|
|
|
if kind == 'int' or kind == 'byte' or kind == 'bool':
|
|
@staticmethod
|
|
def _to_ctypes(x):
|
|
if not isinstance(x, (int, long)):
|
|
if isinstance(x, CTypesData):
|
|
x = int(x)
|
|
else:
|
|
raise TypeError("integer expected, got %s" %
|
|
type(x).__name__)
|
|
if ctype(x).value != x:
|
|
if not is_signed and x < 0:
|
|
raise OverflowError("%s: negative integer" % name)
|
|
else:
|
|
raise OverflowError("%s: integer out of bounds"
|
|
% name)
|
|
return x
|
|
|
|
if kind == 'char':
|
|
@staticmethod
|
|
def _to_ctypes(x):
|
|
if isinstance(x, bytes) and len(x) == 1:
|
|
return x
|
|
if isinstance(x, CTypesPrimitive): # <CData <char>>
|
|
return x._value
|
|
raise TypeError("character expected, got %s" %
|
|
type(x).__name__)
|
|
def __nonzero__(self):
|
|
return ord(self._value) != 0
|
|
else:
|
|
def __nonzero__(self):
|
|
return self._value != 0
|
|
__bool__ = __nonzero__
|
|
|
|
if kind == 'float':
|
|
@staticmethod
|
|
def _to_ctypes(x):
|
|
if not isinstance(x, (int, long, float, CTypesData)):
|
|
raise TypeError("float expected, got %s" %
|
|
type(x).__name__)
|
|
return ctype(x).value
|
|
|
|
@staticmethod
|
|
def _from_ctypes(value):
|
|
return getattr(value, 'value', value)
|
|
|
|
@staticmethod
|
|
def _initialize(blob, init):
|
|
blob.value = CTypesPrimitive._to_ctypes(init)
|
|
|
|
if kind == 'char':
|
|
def _to_string(self, maxlen):
|
|
return self._value
|
|
if kind == 'byte':
|
|
def _to_string(self, maxlen):
|
|
return chr(self._value & 0xff)
|
|
#
|
|
CTypesPrimitive._fix_class()
|
|
return CTypesPrimitive
|
|
|
|
def new_pointer_type(self, BItem):
|
|
getbtype = self.ffi._get_cached_btype
|
|
if BItem is getbtype(model.PrimitiveType('char')):
|
|
kind = 'charp'
|
|
elif BItem in (getbtype(model.PrimitiveType('signed char')),
|
|
getbtype(model.PrimitiveType('unsigned char'))):
|
|
kind = 'bytep'
|
|
elif BItem is getbtype(model.void_type):
|
|
kind = 'voidp'
|
|
else:
|
|
kind = 'generic'
|
|
#
|
|
class CTypesPtr(CTypesGenericPtr):
|
|
__slots__ = ['_own']
|
|
if kind == 'charp':
|
|
__slots__ += ['__as_strbuf']
|
|
_BItem = BItem
|
|
if hasattr(BItem, '_ctype'):
|
|
_ctype = ctypes.POINTER(BItem._ctype)
|
|
_bitem_size = ctypes.sizeof(BItem._ctype)
|
|
else:
|
|
_ctype = ctypes.c_void_p
|
|
if issubclass(BItem, CTypesGenericArray):
|
|
_reftypename = BItem._get_c_name('(* &)')
|
|
else:
|
|
_reftypename = BItem._get_c_name(' * &')
|
|
|
|
def __init__(self, init):
|
|
ctypeobj = BItem._create_ctype_obj(init)
|
|
if kind == 'charp':
|
|
self.__as_strbuf = ctypes.create_string_buffer(
|
|
ctypeobj.value + b'\x00')
|
|
self._as_ctype_ptr = ctypes.cast(
|
|
self.__as_strbuf, self._ctype)
|
|
else:
|
|
self._as_ctype_ptr = ctypes.pointer(ctypeobj)
|
|
self._address = ctypes.cast(self._as_ctype_ptr,
|
|
ctypes.c_void_p).value
|
|
self._own = True
|
|
|
|
def __add__(self, other):
|
|
if isinstance(other, (int, long)):
|
|
return self._new_pointer_at(self._address +
|
|
other * self._bitem_size)
|
|
else:
|
|
return NotImplemented
|
|
|
|
def __sub__(self, other):
|
|
if isinstance(other, (int, long)):
|
|
return self._new_pointer_at(self._address -
|
|
other * self._bitem_size)
|
|
elif type(self) is type(other):
|
|
return (self._address - other._address) // self._bitem_size
|
|
else:
|
|
return NotImplemented
|
|
|
|
def __getitem__(self, index):
|
|
if getattr(self, '_own', False) and index != 0:
|
|
raise IndexError
|
|
return BItem._from_ctypes(self._as_ctype_ptr[index])
|
|
|
|
def __setitem__(self, index, value):
|
|
self._as_ctype_ptr[index] = BItem._to_ctypes(value)
|
|
|
|
if kind == 'charp' or kind == 'voidp':
|
|
@classmethod
|
|
def _arg_to_ctypes(cls, *value):
|
|
if value and isinstance(value[0], bytes):
|
|
return ctypes.c_char_p(value[0])
|
|
else:
|
|
return super(CTypesPtr, cls)._arg_to_ctypes(*value)
|
|
|
|
if kind == 'charp' or kind == 'bytep':
|
|
def _to_string(self, maxlen):
|
|
if maxlen < 0:
|
|
maxlen = sys.maxsize
|
|
p = ctypes.cast(self._as_ctype_ptr,
|
|
ctypes.POINTER(ctypes.c_char))
|
|
n = 0
|
|
while n < maxlen and p[n] != b'\x00':
|
|
n += 1
|
|
return b''.join([p[i] for i in range(n)])
|
|
|
|
def _get_own_repr(self):
|
|
if getattr(self, '_own', False):
|
|
return 'owning %d bytes' % (
|
|
ctypes.sizeof(self._as_ctype_ptr.contents),)
|
|
return super(CTypesPtr, self)._get_own_repr()
|
|
#
|
|
if (BItem is self.ffi._get_cached_btype(model.void_type) or
|
|
BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))):
|
|
CTypesPtr._automatic_casts = True
|
|
#
|
|
CTypesPtr._fix_class()
|
|
return CTypesPtr
|
|
|
|
def new_array_type(self, CTypesPtr, length):
|
|
if length is None:
|
|
brackets = ' &[]'
|
|
else:
|
|
brackets = ' &[%d]' % length
|
|
BItem = CTypesPtr._BItem
|
|
getbtype = self.ffi._get_cached_btype
|
|
if BItem is getbtype(model.PrimitiveType('char')):
|
|
kind = 'char'
|
|
elif BItem in (getbtype(model.PrimitiveType('signed char')),
|
|
getbtype(model.PrimitiveType('unsigned char'))):
|
|
kind = 'byte'
|
|
else:
|
|
kind = 'generic'
|
|
#
|
|
class CTypesArray(CTypesGenericArray):
|
|
__slots__ = ['_blob', '_own']
|
|
if length is not None:
|
|
_ctype = BItem._ctype * length
|
|
else:
|
|
__slots__.append('_ctype')
|
|
_reftypename = BItem._get_c_name(brackets)
|
|
_declared_length = length
|
|
_CTPtr = CTypesPtr
|
|
|
|
def __init__(self, init):
|
|
if length is None:
|
|
if isinstance(init, (int, long)):
|
|
len1 = init
|
|
init = None
|
|
elif kind == 'char' and isinstance(init, bytes):
|
|
len1 = len(init) + 1 # extra null
|
|
else:
|
|
init = tuple(init)
|
|
len1 = len(init)
|
|
self._ctype = BItem._ctype * len1
|
|
self._blob = self._ctype()
|
|
self._own = True
|
|
if init is not None:
|
|
self._initialize(self._blob, init)
|
|
|
|
@staticmethod
|
|
def _initialize(blob, init):
|
|
if isinstance(init, bytes):
|
|
init = [init[i:i+1] for i in range(len(init))]
|
|
else:
|
|
if isinstance(init, CTypesGenericArray):
|
|
if (len(init) != len(blob) or
|
|
not isinstance(init, CTypesArray)):
|
|
raise TypeError("length/type mismatch: %s" % (init,))
|
|
init = tuple(init)
|
|
if len(init) > len(blob):
|
|
raise IndexError("too many initializers")
|
|
addr = ctypes.cast(blob, ctypes.c_void_p).value
|
|
PTR = ctypes.POINTER(BItem._ctype)
|
|
itemsize = ctypes.sizeof(BItem._ctype)
|
|
for i, value in enumerate(init):
|
|
p = ctypes.cast(addr + i * itemsize, PTR)
|
|
BItem._initialize(p.contents, value)
|
|
|
|
def __len__(self):
|
|
return len(self._blob)
|
|
|
|
def __getitem__(self, index):
|
|
if not (0 <= index < len(self._blob)):
|
|
raise IndexError
|
|
return BItem._from_ctypes(self._blob[index])
|
|
|
|
def __setitem__(self, index, value):
|
|
if not (0 <= index < len(self._blob)):
|
|
raise IndexError
|
|
self._blob[index] = BItem._to_ctypes(value)
|
|
|
|
if kind == 'char' or kind == 'byte':
|
|
def _to_string(self, maxlen):
|
|
if maxlen < 0:
|
|
maxlen = len(self._blob)
|
|
p = ctypes.cast(self._blob,
|
|
ctypes.POINTER(ctypes.c_char))
|
|
n = 0
|
|
while n < maxlen and p[n] != b'\x00':
|
|
n += 1
|
|
return b''.join([p[i] for i in range(n)])
|
|
|
|
def _get_own_repr(self):
|
|
if getattr(self, '_own', False):
|
|
return 'owning %d bytes' % (ctypes.sizeof(self._blob),)
|
|
return super(CTypesArray, self)._get_own_repr()
|
|
|
|
def _convert_to_address(self, BClass):
|
|
if BClass in (CTypesPtr, None) or BClass._automatic_casts:
|
|
return ctypes.addressof(self._blob)
|
|
else:
|
|
return CTypesData._convert_to_address(self, BClass)
|
|
|
|
@staticmethod
|
|
def _from_ctypes(ctypes_array):
|
|
self = CTypesArray.__new__(CTypesArray)
|
|
self._blob = ctypes_array
|
|
return self
|
|
|
|
@staticmethod
|
|
def _arg_to_ctypes(value):
|
|
return CTypesPtr._arg_to_ctypes(value)
|
|
|
|
def __add__(self, other):
|
|
if isinstance(other, (int, long)):
|
|
return CTypesPtr._new_pointer_at(
|
|
ctypes.addressof(self._blob) +
|
|
other * ctypes.sizeof(BItem._ctype))
|
|
else:
|
|
return NotImplemented
|
|
|
|
@classmethod
|
|
def _cast_from(cls, source):
|
|
raise NotImplementedError("casting to %r" % (
|
|
cls._get_c_name(),))
|
|
#
|
|
CTypesArray._fix_class()
|
|
return CTypesArray
|
|
|
|
def _new_struct_or_union(self, kind, name, base_ctypes_class):
|
|
#
|
|
class struct_or_union(base_ctypes_class):
|
|
pass
|
|
struct_or_union.__name__ = '%s_%s' % (kind, name)
|
|
kind1 = kind
|
|
#
|
|
class CTypesStructOrUnion(CTypesBaseStructOrUnion):
|
|
__slots__ = ['_blob']
|
|
_ctype = struct_or_union
|
|
_reftypename = '%s &' % (name,)
|
|
_kind = kind = kind1
|
|
#
|
|
CTypesStructOrUnion._fix_class()
|
|
return CTypesStructOrUnion
|
|
|
|
def new_struct_type(self, name):
|
|
return self._new_struct_or_union('struct', name, ctypes.Structure)
|
|
|
|
def new_union_type(self, name):
|
|
return self._new_struct_or_union('union', name, ctypes.Union)
|
|
|
|
def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp,
|
|
totalsize=-1, totalalignment=-1, sflags=0,
|
|
pack=0):
|
|
if totalsize >= 0 or totalalignment >= 0:
|
|
raise NotImplementedError("the ctypes backend of CFFI does not support "
|
|
"structures completed by verify(); please "
|
|
"compile and install the _cffi_backend module.")
|
|
struct_or_union = CTypesStructOrUnion._ctype
|
|
fnames = [fname for (fname, BField, bitsize) in fields]
|
|
btypes = [BField for (fname, BField, bitsize) in fields]
|
|
bitfields = [bitsize for (fname, BField, bitsize) in fields]
|
|
#
|
|
bfield_types = {}
|
|
cfields = []
|
|
for (fname, BField, bitsize) in fields:
|
|
if bitsize < 0:
|
|
cfields.append((fname, BField._ctype))
|
|
bfield_types[fname] = BField
|
|
else:
|
|
cfields.append((fname, BField._ctype, bitsize))
|
|
bfield_types[fname] = Ellipsis
|
|
if sflags & 8:
|
|
struct_or_union._pack_ = 1
|
|
elif pack:
|
|
struct_or_union._pack_ = pack
|
|
struct_or_union._fields_ = cfields
|
|
CTypesStructOrUnion._bfield_types = bfield_types
|
|
#
|
|
@staticmethod
|
|
def _create_ctype_obj(init):
|
|
result = struct_or_union()
|
|
if init is not None:
|
|
initialize(result, init)
|
|
return result
|
|
CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
|
|
#
|
|
def initialize(blob, init):
|
|
if is_union:
|
|
if len(init) > 1:
|
|
raise ValueError("union initializer: %d items given, but "
|
|
"only one supported (use a dict if needed)"
|
|
% (len(init),))
|
|
if not isinstance(init, dict):
|
|
if isinstance(init, (bytes, unicode)):
|
|
raise TypeError("union initializer: got a str")
|
|
init = tuple(init)
|
|
if len(init) > len(fnames):
|
|
raise ValueError("too many values for %s initializer" %
|
|
CTypesStructOrUnion._get_c_name())
|
|
init = dict(zip(fnames, init))
|
|
addr = ctypes.addressof(blob)
|
|
for fname, value in init.items():
|
|
BField, bitsize = name2fieldtype[fname]
|
|
assert bitsize < 0, \
|
|
"not implemented: initializer with bit fields"
|
|
offset = CTypesStructOrUnion._offsetof(fname)
|
|
PTR = ctypes.POINTER(BField._ctype)
|
|
p = ctypes.cast(addr + offset, PTR)
|
|
BField._initialize(p.contents, value)
|
|
is_union = CTypesStructOrUnion._kind == 'union'
|
|
name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
|
|
#
|
|
for fname, BField, bitsize in fields:
|
|
if fname == '':
|
|
raise NotImplementedError("nested anonymous structs/unions")
|
|
if hasattr(CTypesStructOrUnion, fname):
|
|
raise ValueError("the field name %r conflicts in "
|
|
"the ctypes backend" % fname)
|
|
if bitsize < 0:
|
|
def getter(self, fname=fname, BField=BField,
|
|
offset=CTypesStructOrUnion._offsetof(fname),
|
|
PTR=ctypes.POINTER(BField._ctype)):
|
|
addr = ctypes.addressof(self._blob)
|
|
p = ctypes.cast(addr + offset, PTR)
|
|
return BField._from_ctypes(p.contents)
|
|
def setter(self, value, fname=fname, BField=BField):
|
|
setattr(self._blob, fname, BField._to_ctypes(value))
|
|
#
|
|
if issubclass(BField, CTypesGenericArray):
|
|
setter = None
|
|
if BField._declared_length == 0:
|
|
def getter(self, fname=fname, BFieldPtr=BField._CTPtr,
|
|
offset=CTypesStructOrUnion._offsetof(fname),
|
|
PTR=ctypes.POINTER(BField._ctype)):
|
|
addr = ctypes.addressof(self._blob)
|
|
p = ctypes.cast(addr + offset, PTR)
|
|
return BFieldPtr._from_ctypes(p)
|
|
#
|
|
else:
|
|
def getter(self, fname=fname, BField=BField):
|
|
return BField._from_ctypes(getattr(self._blob, fname))
|
|
def setter(self, value, fname=fname, BField=BField):
|
|
# xxx obscure workaround
|
|
value = BField._to_ctypes(value)
|
|
oldvalue = getattr(self._blob, fname)
|
|
setattr(self._blob, fname, value)
|
|
if value != getattr(self._blob, fname):
|
|
setattr(self._blob, fname, oldvalue)
|
|
raise OverflowError("value too large for bitfield")
|
|
setattr(CTypesStructOrUnion, fname, property(getter, setter))
|
|
#
|
|
CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp))
|
|
for fname in fnames:
|
|
if hasattr(CTypesPtr, fname):
|
|
raise ValueError("the field name %r conflicts in "
|
|
"the ctypes backend" % fname)
|
|
def getter(self, fname=fname):
|
|
return getattr(self[0], fname)
|
|
def setter(self, value, fname=fname):
|
|
setattr(self[0], fname, value)
|
|
setattr(CTypesPtr, fname, property(getter, setter))
|
|
|
|
def new_function_type(self, BArgs, BResult, has_varargs):
|
|
nameargs = [BArg._get_c_name() for BArg in BArgs]
|
|
if has_varargs:
|
|
nameargs.append('...')
|
|
nameargs = ', '.join(nameargs)
|
|
#
|
|
class CTypesFunctionPtr(CTypesGenericPtr):
|
|
__slots__ = ['_own_callback', '_name']
|
|
_ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None),
|
|
*[BArg._ctype for BArg in BArgs],
|
|
use_errno=True)
|
|
_reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,))
|
|
|
|
def __init__(self, init, error=None):
|
|
# create a callback to the Python callable init()
|
|
import traceback
|
|
assert not has_varargs, "varargs not supported for callbacks"
|
|
if getattr(BResult, '_ctype', None) is not None:
|
|
error = BResult._from_ctypes(
|
|
BResult._create_ctype_obj(error))
|
|
else:
|
|
error = None
|
|
def callback(*args):
|
|
args2 = []
|
|
for arg, BArg in zip(args, BArgs):
|
|
args2.append(BArg._from_ctypes(arg))
|
|
try:
|
|
res2 = init(*args2)
|
|
res2 = BResult._to_ctypes(res2)
|
|
except:
|
|
traceback.print_exc()
|
|
res2 = error
|
|
if issubclass(BResult, CTypesGenericPtr):
|
|
if res2:
|
|
res2 = ctypes.cast(res2, ctypes.c_void_p).value
|
|
# .value: http://bugs.python.org/issue1574593
|
|
else:
|
|
res2 = None
|
|
#print repr(res2)
|
|
return res2
|
|
if issubclass(BResult, CTypesGenericPtr):
|
|
# The only pointers callbacks can return are void*s:
|
|
# http://bugs.python.org/issue5710
|
|
callback_ctype = ctypes.CFUNCTYPE(
|
|
ctypes.c_void_p,
|
|
*[BArg._ctype for BArg in BArgs],
|
|
use_errno=True)
|
|
else:
|
|
callback_ctype = CTypesFunctionPtr._ctype
|
|
self._as_ctype_ptr = callback_ctype(callback)
|
|
self._address = ctypes.cast(self._as_ctype_ptr,
|
|
ctypes.c_void_p).value
|
|
self._own_callback = init
|
|
|
|
@staticmethod
|
|
def _initialize(ctypes_ptr, value):
|
|
if value:
|
|
raise NotImplementedError("ctypes backend: not supported: "
|
|
"initializers for function pointers")
|
|
|
|
def __repr__(self):
|
|
c_name = getattr(self, '_name', None)
|
|
if c_name:
|
|
i = self._reftypename.index('(* &)')
|
|
if self._reftypename[i-1] not in ' )*':
|
|
c_name = ' ' + c_name
|
|
c_name = self._reftypename.replace('(* &)', c_name)
|
|
return CTypesData.__repr__(self, c_name)
|
|
|
|
def _get_own_repr(self):
|
|
if getattr(self, '_own_callback', None) is not None:
|
|
return 'calling %r' % (self._own_callback,)
|
|
return super(CTypesFunctionPtr, self)._get_own_repr()
|
|
|
|
def __call__(self, *args):
|
|
if has_varargs:
|
|
assert len(args) >= len(BArgs)
|
|
extraargs = args[len(BArgs):]
|
|
args = args[:len(BArgs)]
|
|
else:
|
|
assert len(args) == len(BArgs)
|
|
ctypes_args = []
|
|
for arg, BArg in zip(args, BArgs):
|
|
ctypes_args.append(BArg._arg_to_ctypes(arg))
|
|
if has_varargs:
|
|
for i, arg in enumerate(extraargs):
|
|
if arg is None:
|
|
ctypes_args.append(ctypes.c_void_p(0)) # NULL
|
|
continue
|
|
if not isinstance(arg, CTypesData):
|
|
raise TypeError(
|
|
"argument %d passed in the variadic part "
|
|
"needs to be a cdata object (got %s)" %
|
|
(1 + len(BArgs) + i, type(arg).__name__))
|
|
ctypes_args.append(arg._arg_to_ctypes(arg))
|
|
result = self._as_ctype_ptr(*ctypes_args)
|
|
return BResult._from_ctypes(result)
|
|
#
|
|
CTypesFunctionPtr._fix_class()
|
|
return CTypesFunctionPtr
|
|
|
|
def new_enum_type(self, name, enumerators, enumvalues, CTypesInt):
|
|
assert isinstance(name, str)
|
|
reverse_mapping = dict(zip(reversed(enumvalues),
|
|
reversed(enumerators)))
|
|
#
|
|
class CTypesEnum(CTypesInt):
|
|
__slots__ = []
|
|
_reftypename = '%s &' % name
|
|
|
|
def _get_own_repr(self):
|
|
value = self._value
|
|
try:
|
|
return '%d: %s' % (value, reverse_mapping[value])
|
|
except KeyError:
|
|
return str(value)
|
|
|
|
def _to_string(self, maxlen):
|
|
value = self._value
|
|
try:
|
|
return reverse_mapping[value]
|
|
except KeyError:
|
|
return str(value)
|
|
#
|
|
CTypesEnum._fix_class()
|
|
return CTypesEnum
|
|
|
|
def get_errno(self):
|
|
return ctypes.get_errno()
|
|
|
|
def set_errno(self, value):
|
|
ctypes.set_errno(value)
|
|
|
|
def string(self, b, maxlen=-1):
|
|
return b._to_string(maxlen)
|
|
|
|
def buffer(self, bptr, size=-1):
|
|
raise NotImplementedError("buffer() with ctypes backend")
|
|
|
|
def sizeof(self, cdata_or_BType):
|
|
if isinstance(cdata_or_BType, CTypesData):
|
|
return cdata_or_BType._get_size_of_instance()
|
|
else:
|
|
assert issubclass(cdata_or_BType, CTypesData)
|
|
return cdata_or_BType._get_size()
|
|
|
|
def alignof(self, BType):
|
|
assert issubclass(BType, CTypesData)
|
|
return BType._alignment()
|
|
|
|
def newp(self, BType, source):
|
|
if not issubclass(BType, CTypesData):
|
|
raise TypeError
|
|
return BType._newp(source)
|
|
|
|
def cast(self, BType, source):
|
|
return BType._cast_from(source)
|
|
|
|
def callback(self, BType, source, error, onerror):
|
|
assert onerror is None # XXX not implemented
|
|
return BType(source, error)
|
|
|
|
_weakref_cache_ref = None
|
|
|
|
def gcp(self, cdata, destructor, size=0):
|
|
if self._weakref_cache_ref is None:
|
|
import weakref
|
|
class MyRef(weakref.ref):
|
|
def __eq__(self, other):
|
|
myref = self()
|
|
return self is other or (
|
|
myref is not None and myref is other())
|
|
def __ne__(self, other):
|
|
return not (self == other)
|
|
def __hash__(self):
|
|
try:
|
|
return self._hash
|
|
except AttributeError:
|
|
self._hash = hash(self())
|
|
return self._hash
|
|
self._weakref_cache_ref = {}, MyRef
|
|
weak_cache, MyRef = self._weakref_cache_ref
|
|
|
|
if destructor is None:
|
|
try:
|
|
del weak_cache[MyRef(cdata)]
|
|
except KeyError:
|
|
raise TypeError("Can remove destructor only on a object "
|
|
"previously returned by ffi.gc()")
|
|
return None
|
|
|
|
def remove(k):
|
|
cdata, destructor = weak_cache.pop(k, (None, None))
|
|
if destructor is not None:
|
|
destructor(cdata)
|
|
|
|
new_cdata = self.cast(self.typeof(cdata), cdata)
|
|
assert new_cdata is not cdata
|
|
weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor)
|
|
return new_cdata
|
|
|
|
typeof = type
|
|
|
|
def getcname(self, BType, replace_with):
|
|
return BType._get_c_name(replace_with)
|
|
|
|
def typeoffsetof(self, BType, fieldname, num=0):
|
|
if isinstance(fieldname, str):
|
|
if num == 0 and issubclass(BType, CTypesGenericPtr):
|
|
BType = BType._BItem
|
|
if not issubclass(BType, CTypesBaseStructOrUnion):
|
|
raise TypeError("expected a struct or union ctype")
|
|
BField = BType._bfield_types[fieldname]
|
|
if BField is Ellipsis:
|
|
raise TypeError("not supported for bitfields")
|
|
return (BField, BType._offsetof(fieldname))
|
|
elif isinstance(fieldname, (int, long)):
|
|
if issubclass(BType, CTypesGenericArray):
|
|
BType = BType._CTPtr
|
|
if not issubclass(BType, CTypesGenericPtr):
|
|
raise TypeError("expected an array or ptr ctype")
|
|
BItem = BType._BItem
|
|
offset = BItem._get_size() * fieldname
|
|
if offset > sys.maxsize:
|
|
raise OverflowError
|
|
return (BItem, offset)
|
|
else:
|
|
raise TypeError(type(fieldname))
|
|
|
|
def rawaddressof(self, BTypePtr, cdata, offset=None):
|
|
if isinstance(cdata, CTypesBaseStructOrUnion):
|
|
ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata))
|
|
elif isinstance(cdata, CTypesGenericPtr):
|
|
if offset is None or not issubclass(type(cdata)._BItem,
|
|
CTypesBaseStructOrUnion):
|
|
raise TypeError("unexpected cdata type")
|
|
ptr = type(cdata)._to_ctypes(cdata)
|
|
elif isinstance(cdata, CTypesGenericArray):
|
|
ptr = type(cdata)._to_ctypes(cdata)
|
|
else:
|
|
raise TypeError("expected a <cdata 'struct-or-union'>")
|
|
if offset:
|
|
ptr = ctypes.cast(
|
|
ctypes.c_void_p(
|
|
ctypes.cast(ptr, ctypes.c_void_p).value + offset),
|
|
type(ptr))
|
|
return BTypePtr._from_ctypes(ptr)
|
|
|
|
|
|
class CTypesLibrary(object):
|
|
|
|
def __init__(self, backend, cdll):
|
|
self.backend = backend
|
|
self.cdll = cdll
|
|
|
|
def load_function(self, BType, name):
|
|
c_func = getattr(self.cdll, name)
|
|
funcobj = BType._from_ctypes(c_func)
|
|
funcobj._name = name
|
|
return funcobj
|
|
|
|
def read_variable(self, BType, name):
|
|
try:
|
|
ctypes_obj = BType._ctype.in_dll(self.cdll, name)
|
|
except AttributeError as e:
|
|
raise NotImplementedError(e)
|
|
return BType._from_ctypes(ctypes_obj)
|
|
|
|
def write_variable(self, BType, name, value):
|
|
new_ctypes_obj = BType._to_ctypes(value)
|
|
ctypes_obj = BType._ctype.in_dll(self.cdll, name)
|
|
ctypes.memmove(ctypes.addressof(ctypes_obj),
|
|
ctypes.addressof(new_ctypes_obj),
|
|
ctypes.sizeof(BType._ctype))
|