buffers.pxd revision 781d71db
1"""Python version-independent methods for C/Python buffers.
2
3This file was copied and adapted from mpi4py.
4
5Authors
6-------
7* MinRK
8"""
9
10#-----------------------------------------------------------------------------
11#  Copyright (c) 2010 Lisandro Dalcin
12#  All rights reserved.
13#  Used under BSD License: http://www.opensource.org/licenses/bsd-license.php
14#
15#  Retrieval:
16#  Jul 23, 2010 18:00 PST (r539)
17#  http://code.google.com/p/mpi4py/source/browse/trunk/src/MPI/asbuffer.pxi
18#
19#  Modifications from original:
20#  Copyright (c) 2010-2012 Brian Granger, Min Ragan-Kelley
21#
22#  Distributed under the terms of the New BSD License.  The full license is in
23#  the file COPYING.BSD, distributed as part of this software.
24#-----------------------------------------------------------------------------
25
26
27#-----------------------------------------------------------------------------
28# Python includes.
29#-----------------------------------------------------------------------------
30
31# get version-independent aliases:
32cdef extern from "pyversion_compat.h":
33    pass
34
35# Python 3 buffer interface (PEP 3118)
36cdef extern from "Python.h":
37    int PY_MAJOR_VERSION
38    int PY_MINOR_VERSION
39    ctypedef int Py_ssize_t
40    ctypedef struct PyMemoryViewObject:
41        pass
42    ctypedef struct Py_buffer:
43        void *buf
44        Py_ssize_t len
45        int readonly
46        char *format
47        int ndim
48        Py_ssize_t *shape
49        Py_ssize_t *strides
50        Py_ssize_t *suboffsets
51        Py_ssize_t itemsize
52        void *internal
53    cdef enum:
54        PyBUF_SIMPLE
55        PyBUF_WRITABLE
56        PyBUF_FORMAT
57        PyBUF_ANY_CONTIGUOUS
58    int  PyObject_CheckBuffer(object)
59    int  PyObject_GetBuffer(object, Py_buffer *, int) except -1
60    void PyBuffer_Release(Py_buffer *)
61    
62    int PyBuffer_FillInfo(Py_buffer *view, object obj, void *buf,
63                Py_ssize_t len, int readonly, int infoflags) except -1
64    object PyMemoryView_FromBuffer(Py_buffer *info)
65    
66    object PyMemoryView_FromObject(object)
67
68# Python 2 buffer interface (legacy)
69cdef extern from "Python.h":
70    ctypedef void const_void "const void"
71    Py_ssize_t Py_END_OF_BUFFER
72    int PyObject_CheckReadBuffer(object)
73    int PyObject_AsReadBuffer (object, const_void **, Py_ssize_t *) except -1
74    int PyObject_AsWriteBuffer(object, void **, Py_ssize_t *) except -1
75    
76    object PyBuffer_FromMemory(void *ptr, Py_ssize_t s)
77    object PyBuffer_FromReadWriteMemory(void *ptr, Py_ssize_t s)
78
79    object PyBuffer_FromObject(object, Py_ssize_t offset, Py_ssize_t size)
80    object PyBuffer_FromReadWriteObject(object, Py_ssize_t offset, Py_ssize_t size)
81
82
83#-----------------------------------------------------------------------------
84# asbuffer: C buffer from python object
85#-----------------------------------------------------------------------------
86
87
88cdef inline int memoryview_available():
89    return PY_MAJOR_VERSION >= 3 or (PY_MAJOR_VERSION >=2 and PY_MINOR_VERSION >= 7)
90
91cdef inline int oldstyle_available():
92    return PY_MAJOR_VERSION < 3
93
94
95cdef inline int check_buffer(object ob):
96    """Version independent check for whether an object is a buffer.
97    
98    Parameters
99    ----------
100    object : object
101        Any Python object
102
103    Returns
104    -------
105    int : 0 if no buffer interface, 3 if newstyle buffer interface, 2 if oldstyle.
106    """
107    if PyObject_CheckBuffer(ob):
108        return 3
109    if oldstyle_available():
110        return PyObject_CheckReadBuffer(ob) and 2
111    return 0
112
113
114cdef inline object asbuffer(object ob, int writable, int format,
115                            void **base, Py_ssize_t *size,
116                            Py_ssize_t *itemsize):
117    """Turn an object into a C buffer in a Python version-independent way.
118    
119    Parameters
120    ----------
121    ob : object
122        The object to be turned into a buffer.
123        Must provide a Python Buffer interface
124    writable : int
125        Whether the resulting buffer should be allowed to write
126        to the object.
127    format : int
128        The format of the buffer.  See Python buffer docs.
129    base : void **
130        The pointer that will be used to store the resulting C buffer.
131    size : Py_ssize_t *
132        The size of the buffer(s).
133    itemsize : Py_ssize_t *
134        The size of an item, if the buffer is non-contiguous.
135    
136    Returns
137    -------
138    An object describing the buffer format. Generally a str, such as 'B'.
139    """
140
141    cdef void *bptr = NULL
142    cdef Py_ssize_t blen = 0, bitemlen = 0
143    cdef Py_buffer view
144    cdef int flags = PyBUF_SIMPLE
145    cdef int mode = 0
146    
147    bfmt = None
148
149    mode = check_buffer(ob)
150    if mode == 0:
151        raise TypeError("%r does not provide a buffer interface."%ob)
152
153    if mode == 3:
154        flags = PyBUF_ANY_CONTIGUOUS
155        if writable:
156            flags |= PyBUF_WRITABLE
157        if format:
158            flags |= PyBUF_FORMAT
159        PyObject_GetBuffer(ob, &view, flags)
160        bptr = view.buf
161        blen = view.len
162        if format:
163            if view.format != NULL:
164                bfmt = view.format
165                bitemlen = view.itemsize
166        PyBuffer_Release(&view)
167    else: # oldstyle
168        if writable:
169            PyObject_AsWriteBuffer(ob, &bptr, &blen)
170        else:
171            PyObject_AsReadBuffer(ob, <const_void **>&bptr, &blen)
172        if format:
173            try: # numpy.ndarray
174                dtype = ob.dtype
175                bfmt = dtype.char
176                bitemlen = dtype.itemsize
177            except AttributeError:
178                try: # array.array
179                    bfmt = ob.typecode
180                    bitemlen = ob.itemsize
181                except AttributeError:
182                    if isinstance(ob, bytes):
183                        bfmt = b"B"
184                        bitemlen = 1
185                    else:
186                        # nothing found
187                        bfmt = None
188                        bitemlen = 0
189    if base: base[0] = <void *>bptr
190    if size: size[0] = <Py_ssize_t>blen
191    if itemsize: itemsize[0] = <Py_ssize_t>bitemlen
192    
193    if PY_MAJOR_VERSION >= 3 and bfmt is not None:
194        return bfmt.decode('ascii')
195    return bfmt
196
197
198cdef inline object asbuffer_r(object ob, void **base, Py_ssize_t *size):
199    """Wrapper for standard calls to asbuffer with a readonly buffer."""
200    asbuffer(ob, 0, 0, base, size, NULL)
201    return ob
202
203
204cdef inline object asbuffer_w(object ob, void **base, Py_ssize_t *size):
205    """Wrapper for standard calls to asbuffer with a writable buffer."""
206    asbuffer(ob, 1, 0, base, size, NULL)
207    return ob
208
209#------------------------------------------------------------------------------
210# frombuffer: python buffer/view from C buffer
211#------------------------------------------------------------------------------
212
213
214cdef inline object frombuffer_3(void *ptr, Py_ssize_t s, int readonly):
215    """Python 3 version of frombuffer.
216
217    This is the Python 3 model, but will work on Python >= 2.6. Currently,
218    we use it only on >= 3.0.
219    """
220    cdef Py_buffer pybuf
221    cdef Py_ssize_t *shape = [s]
222    cdef str astr=""
223    PyBuffer_FillInfo(&pybuf, astr, ptr, s, readonly, PyBUF_SIMPLE)
224    pybuf.format = "B"
225    pybuf.shape = shape
226    return PyMemoryView_FromBuffer(&pybuf)
227
228
229cdef inline object frombuffer_2(void *ptr, Py_ssize_t s, int readonly):
230    """Python 2 version of frombuffer. 
231
232    This must be used for Python <= 2.6, but we use it for all Python < 3.
233    """
234    
235    if oldstyle_available():
236        if readonly:
237            return PyBuffer_FromMemory(ptr, s)
238        else:
239            return PyBuffer_FromReadWriteMemory(ptr, s)
240    else:
241        raise NotImplementedError("Old style buffers not available.")
242
243
244cdef inline object frombuffer(void *ptr, Py_ssize_t s, int readonly):
245    """Create a Python Buffer/View of a C array. 
246    
247    Parameters
248    ----------
249    ptr : void *
250        Pointer to the array to be copied.
251    s : size_t
252        Length of the buffer.
253    readonly : int
254        whether the resulting object should be allowed to write to the buffer.
255    
256    Returns
257    -------
258    Python Buffer/View of the C buffer.
259    """
260    # oldstyle first priority for now
261    if oldstyle_available():
262        return frombuffer_2(ptr, s, readonly)
263    else:
264        return frombuffer_3(ptr, s, readonly)
265
266
267cdef inline object frombuffer_r(void *ptr, Py_ssize_t s):
268    """Wrapper for readonly view frombuffer."""
269    return frombuffer(ptr, s, 1)
270
271
272cdef inline object frombuffer_w(void *ptr, Py_ssize_t s):
273    """Wrapper for writable view frombuffer."""
274    return frombuffer(ptr, s, 0)
275
276#------------------------------------------------------------------------------
277# viewfromobject: python buffer/view from python object, refcounts intact
278# frombuffer(asbuffer(obj)) would lose track of refs
279#------------------------------------------------------------------------------
280
281cdef inline object viewfromobject(object obj, int readonly):
282    """Construct a Python Buffer/View object from another Python object.
283
284    This work in a Python version independent manner.
285    
286    Parameters
287    ----------
288    obj : object
289        The input object to be cast as a buffer
290    readonly : int
291        Whether the result should be prevented from overwriting the original.
292    
293    Returns
294    -------
295    Buffer/View of the original object.
296    """
297    if not memoryview_available():
298        if readonly:
299            return PyBuffer_FromObject(obj, 0, Py_END_OF_BUFFER)
300        else:
301            return PyBuffer_FromReadWriteObject(obj, 0, Py_END_OF_BUFFER)
302    else:
303        return PyMemoryView_FromObject(obj)
304
305
306cdef inline object viewfromobject_r(object obj):
307    """Wrapper for readonly viewfromobject."""
308    return viewfromobject(obj, 1)
309
310
311cdef inline object viewfromobject_w(object obj):
312    """Wrapper for writable viewfromobject."""
313    return viewfromobject(obj, 0)
314