Author: Matti Picus <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit