Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: cppyy-packaging Changeset: r94611:eb16b0aff1e3 Date: 2018-05-17 09:39 -0700 http://bitbucket.org/pypy/pypy/changeset/eb16b0aff1e3/
Log: add support for unsigned char arrays and general cleanup of array binding code + tests diff --git a/pypy/module/_cppyy/capi/__init__.py b/pypy/module/_cppyy/capi/__init__.py --- a/pypy/module/_cppyy/capi/__init__.py +++ b/pypy/module/_cppyy/capi/__init__.py @@ -11,6 +11,3 @@ assert lltype.typeOf(ptr) == C_OBJECT address = rffi.cast(rffi.CCHARP, ptr) return rffi.cast(C_OBJECT, lltype.direct_ptradd(address, offset)) - -def exchange_address(ptr, cif_descr, index): - return rffi.ptradd(ptr, cif_descr.exchange_args[index]) diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py --- a/pypy/module/_cppyy/converter.py +++ b/pypy/module/_cppyy/converter.py @@ -7,7 +7,7 @@ from rpython.rlib import rfloat, rawrefcount from pypy.module._rawffi.interp_rawffi import letter2tp -from pypy.module._rawffi.array import W_Array, W_ArrayInstance +from pypy.module._rawffi.array import W_ArrayInstance from pypy.module._cppyy import helper, capi, ffitypes @@ -130,20 +130,6 @@ pass -class ArrayCache(object): - def __init__(self, space): - self.space = space - def __getattr__(self, name): - if name.startswith('array_'): - typecode = name[len('array_'):] - arr = self.space.interp_w(W_Array, letter2tp(self.space, typecode)) - setattr(self, name, arr) - return arr - raise AttributeError(name) - - def _freeze_(self): - return True - class ArrayTypeConverterMixin(object): _mixin_ = True _immutable_fields_ = ['size'] @@ -162,9 +148,7 @@ # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONG, address_value) - cache = space.fromcache(ArrayCache) - arr = getattr(cache, 'array_' + self.typecode) - return arr.fromaddress(space, address, self.size) + return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address) def to_memory(self, space, w_obj, w_value, offset): # copy the full array (uses byte copy for now) @@ -205,17 +189,15 @@ # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONGP, address_value) - cache = space.fromcache(ArrayCache) - arr = getattr(cache, 'array_' + self.typecode) - return arr.fromaddress(space, address[0], self.size) + return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address[0]) def to_memory(self, space, w_obj, w_value, offset): # copy only the pointer value rawobject = get_rawobject_nonnull(space, w_obj) - byteptr = rffi.cast(rffi.CCHARPP, capi.direct_ptradd(rawobject, offset)) + byteptr = rffi.cast(rffi.VOIDPP, capi.direct_ptradd(rawobject, offset)) buf = space.getarg_w('s*', w_value) try: - byteptr[0] = buf.get_raw_address() + byteptr[0] = rffi.cast(rffi.VOIDP, buf.get_raw_address()) except ValueError: raise oefmt(space.w_TypeError, "raw buffer interface not supported") @@ -337,6 +319,10 @@ address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset)) address[0] = self._unwrap_object(space, w_value) + +class UCharConverter(ffitypes.typeid(rffi.UCHAR), CharConverter): + pass + class FloatConverter(ffitypes.typeid(rffi.FLOAT), FloatTypeConverterMixin, TypeConverter): _immutable_fields_ = ['default'] @@ -449,12 +435,12 @@ # returned as a long value for the address (INTPTR_T is not proper # per se, but rffi does not come with a PTRDIFF_T) address = self._get_raw_address(space, w_obj, offset) - ptrval = rffi.cast(rffi.ULONG, rffi.cast(rffi.VOIDPP, address)[0]) - if ptrval == 0: + ptrval = rffi.cast(rffi.ULONGP, address)[0] + if ptrval == rffi.cast(rffi.ULONG, 0): from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) - arr = space.interp_w(W_Array, letter2tp(space, 'P')) - return arr.fromaddress(space, ptrval, sys.maxint) + shape = letter2tp(space, 'P') + return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) def to_memory(self, space, w_obj, w_value, offset): address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) @@ -797,6 +783,7 @@ _converters["bool"] = BoolConverter _converters["char"] = CharConverter +_converters["unsigned char"] = UCharConverter _converters["float"] = FloatConverter _converters["const float&"] = ConstFloatRefConverter _converters["double"] = DoubleConverter @@ -886,6 +873,7 @@ "NOT_RPYTHON" array_info = ( ('b', rffi.sizeof(rffi.UCHAR), ("bool",)), # is debatable, but works ... + ('B', rffi.sizeof(rffi.UCHAR), ("unsigned char",)), ('h', rffi.sizeof(rffi.SHORT), ("short int", "short")), ('H', rffi.sizeof(rffi.USHORT), ("unsigned short int", "unsigned short")), ('i', rffi.sizeof(rffi.INT), ("int",)), @@ -901,9 +889,11 @@ for tcode, tsize, names in array_info: class ArrayConverter(ArrayTypeConverterMixin, TypeConverter): + _immutable_fields_ = ['typecode', 'typesize'] typecode = tcode typesize = tsize class PtrConverter(PtrTypeConverterMixin, TypeConverter): + _immutable_fields_ = ['typecode', 'typesize'] typecode = tcode typesize = tsize for name in names: @@ -919,7 +909,6 @@ def _add_aliased_converters(): "NOT_RPYTHON" aliases = ( - ("char", "unsigned char"), # TODO: check ("char", "signed char"), # TODO: check ("const char*", "char*"), diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py --- a/pypy/module/_cppyy/executor.py +++ b/pypy/module/_cppyy/executor.py @@ -5,7 +5,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import jit_libffi -from pypy.module._rawffi.interp_rawffi import unpack_simple_shape +from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_Array, W_ArrayInstance from pypy.module._cppyy import helper, capi, ffitypes @@ -56,11 +56,11 @@ raise NotImplementedError lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args) ptrval = rffi.cast(rffi.ULONG, lresult) - arr = space.interp_w(W_Array, unpack_simple_shape(space, space.newtext(self.typecode))) - if ptrval == 0: + if ptrval == rffi.cast(rffi.ULONG, 0): from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) - return arr.fromaddress(space, ptrval, sys.maxint) + shape = letter2tp(space, self.typecode) + return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) class VoidExecutor(FunctionExecutor): diff --git a/pypy/module/_cppyy/ffitypes.py b/pypy/module/_cppyy/ffitypes.py --- a/pypy/module/_cppyy/ffitypes.py +++ b/pypy/module/_cppyy/ffitypes.py @@ -74,6 +74,37 @@ # allow int to pass to char and make sure that str is of length 1 if space.isinstance_w(w_value, space.w_int): ival = space.c_int_w(w_value) + if ival < -128 or 127 < ival: + raise oefmt(space.w_ValueError, "char arg not in range(-128,128)") + + value = rffi.cast(rffi.CHAR, space.c_int_w(w_value)) + else: + value = space.text_w(w_value) + if len(value) != 1: + raise oefmt(space.w_ValueError, + "char expected, got string of size %d", len(value)) + + value = rffi.cast(rffi.CHAR, value[0]) + return value # turn it into a "char" to the annotator + + def cffi_type(self, space): + state = space.fromcache(State) + return state.c_char + +class UCharTypeMixin(object): + _mixin_ = True + _immutable_fields_ = ['c_type', 'c_ptrtype'] + + c_type = rffi.UCHAR + c_ptrtype = rffi.CCHARP # there's no such thing as rffi.UCHARP + + def _wrap_object(self, space, obj): + return space.newbytes(obj) + + def _unwrap_object(self, space, w_value): + # allow int to pass to char and make sure that str is of length 1 + if space.isinstance_w(w_value, space.w_int): + ival = space.c_int_w(w_value) if ival < 0 or 256 <= ival: raise oefmt(space.w_ValueError, "char arg not in range(256)") @@ -277,6 +308,7 @@ "NOT_RPYTHON" if c_type == bool: return BoolTypeMixin if c_type == rffi.CHAR: return CharTypeMixin + if c_type == rffi.UCHAR: return UCharTypeMixin if c_type == rffi.SHORT: return ShortTypeMixin if c_type == rffi.USHORT: return UShortTypeMixin if c_type == rffi.INT: return IntTypeMixin diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -267,11 +267,12 @@ def do_fast_call(self, cppthis, args_w, call_local): if self.cif_descr == lltype.nullptr(jit_libffi.CIF_DESCRIPTION): raise FastCallNotPossible + jit.promote(self) cif_descr = self.cif_descr buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size, flavor='raw') try: # this pointer - data = capi.exchange_address(buffer, cif_descr, 0) + data = rffi.ptradd(buffer, cif_descr.exchange_args[0]) x = rffi.cast(rffi.LONGP, data) # LONGP needed for test_zjit.py x[0] = rffi.cast(rffi.LONG, cppthis) @@ -280,11 +281,11 @@ for i in range(len(args_w)): conv = self.converters[i] w_arg = args_w[i] - data = capi.exchange_address(buffer, cif_descr, i+1) + data = rffi.ptradd(buffer, cif_descr.exchange_args[i+1]) conv.convert_argument_libffi(self.space, w_arg, data, call_local) for j in range(i+1, len(self.arg_defs)): conv = self.converters[j] - data = capi.exchange_address(buffer, cif_descr, j+1) + data = rffi.ptradd(buffer, cif_descr.exchange_args[j+1]) conv.default_argument_libffi(self.space, data) assert self._funcaddr @@ -296,7 +297,7 @@ return w_res # from ctypefunc; have my own version for annotater purposes and to disable - # memory tracking (method live time is longer than the tests) + # memory tracking (method life time is longer than the tests) @jit.dont_look_inside def _rawallocate(self, builder): builder.space = self.space @@ -309,7 +310,7 @@ # allocate the buffer if we_are_translated(): rawmem = lltype.malloc(rffi.CCHARP.TO, builder.nb_bytes, - flavor='raw', track_allocation=False) + flavor='raw') rawmem = rffi.cast(jit_libffi.CIF_DESCRIPTION_P, rawmem) else: # gross overestimation of the length below, but too bad @@ -352,7 +353,7 @@ # has been offset to the matching class. Hence, the libffi pointer is # uniquely defined and needs to be setup only once. funcaddr = capi.c_function_address_from_index(self.space, self.scope, self.index) - if funcaddr and cppthis: # methods only for now + if funcaddr and cppthis: # TODO: methods only for now state = self.space.fromcache(ffitypes.State) # argument type specification (incl. cppthis) diff --git a/pypy/module/_cppyy/test/datatypes.cxx b/pypy/module/_cppyy/test/datatypes.cxx --- a/pypy/module/_cppyy/test/datatypes.cxx +++ b/pypy/module/_cppyy/test/datatypes.cxx @@ -29,6 +29,7 @@ m_voidp = (void*)0; m_bool_array2 = new bool[N]; + m_uchar_array2 = new unsigned char[N]; m_short_array2 = new short[N]; m_ushort_array2 = new unsigned short[N]; m_int_array2 = new int[N]; @@ -42,6 +43,8 @@ for (int i = 0; i < N; ++i) { m_bool_array[i] = bool(i%2); m_bool_array2[i] = bool((i+1)%2); + m_uchar_array[i] = 1u*i; + m_uchar_array2[i] = 2u*i; m_short_array[i] = -1*i; m_short_array2[i] = -2*i; m_ushort_array[i] = 3u*i; @@ -114,6 +117,8 @@ bool* CppyyTestData::get_bool_array() { return m_bool_array; } bool* CppyyTestData::get_bool_array2() { return m_bool_array2; } +unsigned char* CppyyTestData::get_uchar_array() { return m_uchar_array; } +unsigned char* CppyyTestData::get_uchar_array2() { return m_uchar_array2; } short* CppyyTestData::get_short_array() { return m_short_array; } short* CppyyTestData::get_short_array2() { return m_short_array2; } unsigned short* CppyyTestData::get_ushort_array() { return m_ushort_array; } @@ -233,6 +238,7 @@ void CppyyTestData::set_enum_cr(const EWhat& w) { m_enum = w; } //- passers ----------------------------------------------------------------- +unsigned char* CppyyTestData::pass_array(unsigned char* a) { return a; } short* CppyyTestData::pass_array(short* a) { return a; } unsigned short* CppyyTestData::pass_array(unsigned short* a) { return a; } int* CppyyTestData::pass_array(int* a) { return a; } diff --git a/pypy/module/_cppyy/test/datatypes.h b/pypy/module/_cppyy/test/datatypes.h --- a/pypy/module/_cppyy/test/datatypes.h +++ b/pypy/module/_cppyy/test/datatypes.h @@ -99,6 +99,8 @@ bool* get_bool_array(); bool* get_bool_array2(); + unsigned char* get_uchar_array(); + unsigned char* get_uchar_array2(); short* get_short_array(); short* get_short_array2(); unsigned short* get_ushort_array(); @@ -217,6 +219,7 @@ void set_enum_cr(const EWhat&); // passers + unsigned char* pass_array(unsigned char*); short* pass_array(short*); unsigned short* pass_array(unsigned short*); int* pass_array(int*); @@ -226,6 +229,7 @@ float* pass_array(float*); double* pass_array(double*); + unsigned char* pass_void_array_B(void* a) { return pass_array((unsigned char*)a); } short* pass_void_array_h(void* a) { return pass_array((short*)a); } unsigned short* pass_void_array_H(void* a) { return pass_array((unsigned short*)a); } int* pass_void_array_i(void* a) { return pass_array((int*)a); } @@ -265,6 +269,8 @@ // array types bool m_bool_array[N]; bool* m_bool_array2; + unsigned char m_uchar_array[N]; + unsigned char* m_uchar_array2; short m_short_array[N]; short* m_short_array2; unsigned short m_ushort_array[N]; diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py --- a/pypy/module/_cppyy/test/test_datatypes.py +++ b/pypy/module/_cppyy/test/test_datatypes.py @@ -73,8 +73,8 @@ assert c.get_bool_array2()[i] == bool((i+1)%2) # reading of integer array types - names = [ 'short', 'ushort', 'int', 'uint', 'long', 'ulong'] - alpha = [(-1, -2), (3, 4), (-5, -6), (7, 8), (-9, -10), (11, 12)] + names = ['uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong'] + alpha = [ (1, 2), (-1, -2), (3, 4), (-5, -6), (7, 8), (-9, -10), (11, 12)] for j in range(self.N): assert getattr(c, 'm_%s_array' % names[i])[i] == alpha[i][0]*i assert getattr(c, 'get_%s_array' % names[i])()[i] == alpha[i][0]*i @@ -89,6 +89,7 @@ assert round(c.m_double_array2[k] + 16.*k, 8) == 0 # out-of-bounds checks + raises(IndexError, c.m_uchar_array.__getitem__, self.N) raises(IndexError, c.m_short_array.__getitem__, self.N) raises(IndexError, c.m_ushort_array.__getitem__, self.N) raises(IndexError, c.m_int_array.__getitem__, self.N) @@ -177,10 +178,10 @@ c.destroy_arrays() # integer arrays - names = ['short', 'ushort', 'int', 'uint', 'long', 'ulong'] + names = ['uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong'] import array a = range(self.N) - atypes = ['h', 'H', 'i', 'I', 'l', 'L' ] + atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L' ] for j in range(len(names)): b = array.array(atypes[j], a) setattr(c, 'm_'+names[j]+'_array', b) # buffer copies @@ -188,7 +189,7 @@ assert eval('c.m_%s_array[i]' % names[j]) == b[i] setattr(c, 'm_'+names[j]+'_array2', b) # pointer copies - b[i] = 28 + b[3] = 28 for i in range(self.N): assert eval('c.m_%s_array2[i]' % names[j]) == b[i] @@ -681,6 +682,7 @@ c = CppyyTestData() for func in ['get_bool_array', 'get_bool_array2', + 'get_uchar_array', 'get_uchar_array2', 'get_ushort_array', 'get_ushort_array2', 'get_int_array', 'get_int_array2', 'get_uint_array', 'get_uint_array2', diff --git a/pypy/module/_cppyy/test/test_zjit.py b/pypy/module/_cppyy/test/test_zjit.py --- a/pypy/module/_cppyy/test/test_zjit.py +++ b/pypy/module/_cppyy/test/test_zjit.py @@ -17,19 +17,6 @@ # (note that the module is not otherwise used in the test itself) import pypy.module.cpyext -# change capi's direct_ptradd and exchange_address to being jit-opaque -@jit.dont_look_inside -def _opaque_direct_ptradd(ptr, offset): - address = rffi.cast(rffi.CCHARP, ptr) - return rffi.cast(capi.C_OBJECT, lltype.direct_ptradd(address, offset)) -capi.direct_ptradd = _opaque_direct_ptradd - -@jit.dont_look_inside -def _opaque_exchange_address(ptr, cif_descr, index): - offset = rffi.cast(rffi.LONG, cif_descr.exchange_args[index]) - return rffi.ptradd(ptr, offset) -capi.exchange_address = _opaque_exchange_address - # add missing alt_errno (??) def get_tlobj(self): try: _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit