Author: Matti Picus <matti.pi...@gmail.com> Branch: newmemoryview-app-level Changeset: r96155:8972b398d0ba Date: 2019-02-24 22:12 +0200 http://bitbucket.org/pypy/pypy/changeset/8972b398d0ba/
Log: add newmemoryview via IndirectView backported from py3.6, use in _ctypes/array diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py --- a/lib_pypy/_ctypes/array.py +++ b/lib_pypy/_ctypes/array.py @@ -4,6 +4,7 @@ from _ctypes.basics import _CData, cdata_from_address, _CDataMeta, sizeof from _ctypes.basics import keepalive_key, store_reference, ensure_objects from _ctypes.basics import CArgObject, as_ffi_pointer +import sys, __pypy__ class ArrayMeta(_CDataMeta): def __new__(self, name, cls, typedict): @@ -241,6 +242,21 @@ def _as_ffi_pointer_(self, ffitype): return as_ffi_pointer(self, ffitype) + def __buffer__(self, flags): + shape = [] + obj = self + while 1: + shape.append(obj._length_) + try: + obj[0]._length_ + except AttributeError: + break + obj = obj[0] + + fmt = get_format_str(obj._type_) + itemsize = len(buffer(obj[0])) + return __pypy__.newmemoryview(memoryview(self._buffer), itemsize, fmt, shape) + ARRAY_CACHE = {} def create_array_type(base, length): @@ -260,3 +276,25 @@ cls = ArrayMeta(name, (Array,), tpdict) ARRAY_CACHE[key] = cls return cls + +byteorder = {'little': '>', 'big': '<'} +swappedorder = {'little': '<', 'big': '>'} + +def get_format_str(typ): + if hasattr(typ, '_fields_'): + if hasattr(typ, '_swappedbytes_'): + bo = swappedorder[sys.byteorder] + else: + bo = byteorder[sys.byteorder] + flds = [] + for name, obj in typ._fields_: + flds.append(bo) + flds.append(get_format_str(obj)) + flds.append(':') + flds.append(name) + flds.append(':') + return 'T{' + ''.join(flds) + '}' + elif hasattr(typ, '_type_'): + return typ._type_ + else: + raise ValueError('cannot get format string for %r' % typ) diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -110,6 +110,7 @@ 'side_effects_ok' : 'interp_magic.side_effects_ok', 'stack_almost_full' : 'interp_magic.stack_almost_full', 'pyos_inputhook' : 'interp_magic.pyos_inputhook', + 'newmemoryview' : 'newmemoryview.newmemoryview', } if sys.platform == 'win32': interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp' diff --git a/pypy/module/__pypy__/newmemoryview.py b/pypy/module/__pypy__/newmemoryview.py new file mode 100644 --- /dev/null +++ b/pypy/module/__pypy__/newmemoryview.py @@ -0,0 +1,99 @@ +# +# An app-level interface to tp_as_buffer->bf_getbuffer. +# + +from pypy.interpreter.error import oefmt +from pypy.interpreter.gateway import unwrap_spec +from pypy.objspace.std.memoryobject import BufferViewND + +pep3118_size_map = { + # from struct documentation https://docs.python.org/3/library/struct.html + 'x': 1, #padding + '?': 1, + 'c': 1, + 'b': 1, + 'B': 1, + 'h': 2, + 'H': 2, + 'i': 4, + 'I': 4, + 'l': 4, + 'L': 4, + 'q': 8, + 'Q': 8, + 'e': 2, + 'f': 4, + 'd': 8, + # pep 3118 extensions + # https://www.python.org/dev/peps/pep-3118/#additions-to-the-struct-string-syntax + 'g': 16, # long double - is this platform dependent? + 'Zf': 8, + 'Zd':16, + 'Zg':32, + # Unhandled: 's', 'w' (UCS-4), 'c' (usc-1), 'u' (usc-2), 'O' (PyObject*), +} + +@unwrap_spec(itemsize=int, format='text') +def newmemoryview(space, w_obj, itemsize, format, w_shape=None, w_strides=None): + ''' + newmemoryview(buf, itemsize, format, shape=None, strides=None) + ''' + if not space.isinstance_w(w_obj, space.w_memoryview): + raise oefmt(space.w_ValueError, "memoryview expected") + # minimal error checking + lgt = space.len_w(w_obj) + old_size = w_obj.getitemsize() + nbytes = lgt * old_size + if w_shape: + tot = 1 + shape = [] + for w_v in space.listview(w_shape): + v = space.int_w(w_v) + shape.append(v) + tot *= v + if tot * itemsize != nbytes: + raise oefmt(space.w_ValueError, + "shape/itemsize %s/%d does not match obj len/itemsize %d/%d", + str(shape), itemsize, lgt, old_size) + else: + if nbytes % itemsize != 0: + raise oefmt(space.w_ValueError, + "itemsize %d does not match obj len/itemsize %d/%d", + itemsize, lgt, old_size) + shape = [nbytes / itemsize,] + ndim = len(shape) + if w_strides: + strides = [] + for w_v in space.listview(w_strides): + v = space.int_w(w_v) + strides.append(v) + if not w_shape and len(strides) != 1: + raise oefmt(space.w_ValueError, + "strides must have one value if shape not provided") + if len(strides) != ndim: + raise oefmt(space.w_ValueError, + "shape %s does not match strides %s", + str(shape), str(strides)) + else: + # start from the right, c-order layout + strides = [itemsize] * ndim + for v in range(ndim - 2, -1, -1): + strides[v] = strides[v + 1] * shape[v + 1] + # check that the strides are not too big + for i in range(ndim): + if strides[i] * shape[i] > nbytes: + raise oefmt(space.w_ValueError, + "shape %s and strides %s exceed object size %d", + shape, strides, nbytes) + view = space.buffer_w(w_obj, 0) + return space.newmemoryview(FormatBufferViewND(view, format, ndim, shape, strides)) + +class FormatBufferViewND(BufferViewND): + _immutable_ = True + _attrs_ = ['readonly', 'parent', 'ndim', 'shape', 'strides', 'format'] + def __init__(self, parent, format, ndim, shape, strides): + BufferViewND.__init__(self, parent, ndim, shape, strides) + self.format = format + + def getformat(self): + return self.format diff --git a/pypy/module/__pypy__/test/test_newmemoryview.py b/pypy/module/__pypy__/test/test_newmemoryview.py new file mode 100644 --- /dev/null +++ b/pypy/module/__pypy__/test/test_newmemoryview.py @@ -0,0 +1,17 @@ + + +class AppTestMinimal: + spaceconfig = dict(usemodules=['__pypy__']) + + def test_newmemoryview(self): + from __pypy__ import newmemoryview + b = bytearray(12) + # The format can be anything, we only verify shape, strides, and itemsize + m = newmemoryview(memoryview(b), 2, 'T{<h:a}', shape=(2, 3)) + assert m.strides == (6, 2) + m = newmemoryview(memoryview(b), 2, 'T{<h:a}', shape=(2, 3), + strides=(6, 2)) + assert m.strides == (6, 2) + assert m.format == 'T{<h:a}' + + diff --git a/pypy/objspace/std/memoryobject.py b/pypy/objspace/std/memoryobject.py --- a/pypy/objspace/std/memoryobject.py +++ b/pypy/objspace/std/memoryobject.py @@ -309,3 +309,86 @@ return (_IsCContiguous(ndim, shape, strides, itemsize) or _IsFortranContiguous(ndim, shape, strides, itemsize)) return 0 + + +class IndirectView(BufferView): + """Base class for views into another BufferView""" + _immutable_ = True + _attrs_ = ['readonly', 'parent'] + + def getlength(self): + return self.parent.getlength() + + def as_str(self): + return self.parent.as_str() + + def as_str_and_offset_maybe(self): + return self.parent.as_str_and_offset_maybe() + + def getbytes(self, start, size): + return self.parent.getbytes(start, size) + + def setbytes(self, start, string): + self.parent.setbytes(start, string) + + def get_raw_address(self): + return self.parent.get_raw_address() + + def as_readbuf(self): + return self.parent.as_readbuf() + + def as_writebuf(self): + return self.parent.as_writebuf() + +class BufferView1D(IndirectView): + _immutable_ = True + _attrs_ = ['readonly', 'parent', 'format', 'itemsize'] + + def __init__(self, parent, format, itemsize): + self.parent = parent + self.readonly = parent.readonly + self.format = format + self.itemsize = itemsize + + def getformat(self): + return self.format + + def getitemsize(self): + return self.itemsize + + def getndim(self): + return 1 + + def getshape(self): + return [self.getlength() // self.itemsize] + + def getstrides(self): + return [self.itemsize] + +class BufferViewND(IndirectView): + _immutable_ = True + _attrs_ = ['readonly', 'parent', 'ndim', 'shape', 'strides'] + + def __init__(self, parent, ndim, shape, strides): + assert parent.getndim() == 1 + assert len(shape) == len(strides) == ndim + self.parent = parent + self.readonly = parent.readonly + self.ndim = ndim + self.shape = shape + self.strides = strides + + def getformat(self): + return self.parent.getformat() + + def getitemsize(self): + return self.parent.getitemsize() + + def getndim(self): + return self.ndim + + def getshape(self): + return self.shape + + def getstrides(self): + return self.strides _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit