Spaces:
Sleeping
Sleeping
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__,)) | |
def _newp(cls, init): | |
raise TypeError("expected a pointer or array ctype, got '%s'" | |
% (cls._get_c_name(),)) | |
def _to_ctypes(value): | |
raise TypeError | |
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 | |
def _create_ctype_obj(cls, init): | |
if init is None: | |
return cls._arg_to_ctypes() | |
else: | |
return cls._arg_to_ctypes(init) | |
def _from_ctypes(ctypes_value): | |
raise TypeError | |
def _get_c_name(cls, replace_with=''): | |
return cls._reftypename.replace(' &', replace_with) | |
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())) | |
def _get_size(cls): | |
return ctypes.sizeof(cls._ctype) | |
def _get_size_of_instance(self): | |
return ctypes.sizeof(self._ctype) | |
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) | |
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__ = [] | |
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" | |
def _newp(cls, init): | |
return cls(init) | |
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) | |
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__ | |
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) | |
def _from_ctypes(cls, ctypes_ptr): | |
address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 | |
return cls._new_pointer_at(address) | |
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'] | |
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)) | |
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) | |
def _from_ctypes(cls, ctypes_struct_or_union): | |
self = cls.__new__(cls) | |
self._blob = ctypes_struct_or_union | |
return self | |
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 &' | |
def _from_ctypes(novalue): | |
return None | |
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 | |
def _create_ctype_obj(init): | |
if init is None: | |
return ctype() | |
return ctype(CTypesPrimitive._to_ctypes(init)) | |
if kind == 'int' or kind == 'byte': | |
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': | |
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': | |
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': | |
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': | |
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': | |
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': | |
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 | |
def _from_ctypes(value): | |
return getattr(value, 'value', value) | |
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': | |
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) | |
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) | |
def _from_ctypes(ctypes_array): | |
self = CTypesArray.__new__(CTypesArray) | |
self._blob = ctypes_array | |
return self | |
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 | |
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 | |
# | |
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 | |
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)) | |