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

Reply via email to