1"""
2Utility for changing itemsize of memoryviews, and getting
3numpy arrays from byte-arrays that should be interpreted with a different
4itemsize.
5
6Authors
7-------
8* MinRK
9"""
10
11#-----------------------------------------------------------------------------
12#  Copyright (c) 2010-2012 Brian Granger, Min Ragan-Kelley
13#
14#  This file is part of pyzmq
15#
16#  Distributed under the terms of the New BSD License.  The full license is in
17#  the file COPYING.BSD, distributed as part of this software.
18#-----------------------------------------------------------------------------
19
20from libc.stdlib cimport malloc
21from buffers cimport *
22
23cdef inline object _rebuffer(object obj, char * format, int itemsize):
24    """clobber the format & itemsize of a 1-D
25
26    This is the Python 3 model, but will work on Python >= 2.6. Currently,
27    we use it only on >= 3.0.
28    """
29    cdef Py_buffer view
30    cdef int flags = PyBUF_SIMPLE
31    cdef int mode = 0
32    # cdef Py_ssize_t *shape, *strides, *suboffsets
33    
34    mode = check_buffer(obj)
35    if mode == 0:
36        raise TypeError("%r does not provide a buffer interface."%obj)
37
38    if mode == 3:
39        flags = PyBUF_ANY_CONTIGUOUS
40        if format:
41            flags |= PyBUF_FORMAT
42        PyObject_GetBuffer(obj, &view, flags)
43        assert view.ndim <= 1, "Can only reinterpret 1-D memoryviews"
44        assert view.len % itemsize == 0, "Buffer of length %i not divisible into items of size %i"%(view.len, itemsize)
45        # hack the format
46        view.ndim = 1
47        view.format = format
48        view.itemsize = itemsize
49        view.strides = <Py_ssize_t *>malloc(sizeof(Py_ssize_t))
50        view.strides[0] = itemsize
51        view.shape = <Py_ssize_t *>malloc(sizeof(Py_ssize_t))
52        view.shape[0] = view.len/itemsize
53        view.suboffsets = <Py_ssize_t *>malloc(sizeof(Py_ssize_t))
54        view.suboffsets[0] = 0
55        # for debug: make buffer writable, for zero-copy testing
56        # view.readonly = 0
57        
58        return PyMemoryView_FromBuffer(&view)
59    else:
60        raise TypeError("This funciton is only for new-style buffer objects.")
61
62def rebuffer(obj, format, itemsize):
63    """Change the itemsize of a memoryview.
64    
65    Only for 1D contiguous buffers.
66    """
67    return _rebuffer(obj, format, itemsize)
68
69def array_from_buffer(view, dtype, shape):
70    """Get a numpy array from a memoryview, regardless of the itemsize of the original
71    memoryview.  This is important, because pyzmq does not send memoryview shape data
72    over the wire, so we need to change the memoryview itemsize before calling
73    asarray.
74    """
75    import numpy
76    A = numpy.array([],dtype=dtype)
77    ref = viewfromobject(A,0)
78    fmt = ref.format.encode()
79    buf = viewfromobject(view, 0)
80    buf = _rebuffer(view, fmt, ref.itemsize)
81    return numpy.asarray(buf, dtype=dtype).reshape(shape)
82
83def print_view_info(obj):
84    """simple utility for printing info on a new-style buffer object"""
85    cdef Py_buffer view
86    cdef int flags = PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT
87    cdef int mode = 0
88    
89    mode = check_buffer(obj)
90    if mode == 0:
91        raise TypeError("%r does not provide a buffer interface."%obj)
92
93    if mode == 3:
94        PyObject_GetBuffer(obj, &view, flags)
95        print <size_t>view.buf, view.len, view.format, view.ndim,
96        if view.ndim:
97            if view.shape:
98                print view.shape[0],
99            if view.strides:
100                print view.strides[0],
101            if view.suboffsets:
102                print view.suboffsets[0],
103        print
104