Skip to content

Commit b99f762

Browse files
committed
Merged in py3k-buffer branch to main line. All objects now use the buffer protocol in PEP 3118.
1 parent 3de862d commit b99f762

22 files changed

+1730
-686
lines changed

Include/Python.h

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
#include "rangeobject.h"
7878
#include "stringobject.h"
7979
#include "bufferobject.h"
80+
#include "memoryobject.h"
8081
#include "tupleobject.h"
8182
#include "listobject.h"
8283
#include "dictobject.h"

Include/abstract.h

+110
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,12 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
475475
This is the equivalent of the Python statement: del o[key].
476476
*/
477477

478+
/* old buffer API
479+
FIXME: usage of these should all be replaced in Python itself
480+
but for backwards compatibility we will implement them.
481+
Their usage without a corresponding "unlock" mechansim
482+
may create issues (but they would already be there). */
483+
478484
PyAPI_FUNC(int) PyObject_AsCharBuffer(PyObject *obj,
479485
const char **buffer,
480486
Py_ssize_t *buffer_len);
@@ -527,6 +533,110 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
527533
an exception set.
528534
*/
529535

536+
/* new buffer API */
537+
538+
#define PyObject_CheckBuffer(obj) \
539+
(((obj)->ob_type->tp_as_buffer != NULL) && \
540+
((obj)->ob_type->tp_as_buffer->bf_getbuffer != NULL))
541+
542+
/* Return 1 if the getbuffer function is available, otherwise
543+
return 0 */
544+
545+
PyAPI_FUNC(int) PyObject_GetBuffer(PyObject *obj, PyBuffer *view,
546+
int flags);
547+
548+
/* This is a C-API version of the getbuffer function call. It checks
549+
to make sure object has the required function pointer and issues the
550+
call. Returns -1 and raises an error on failure and returns 0 on
551+
success
552+
*/
553+
554+
555+
PyAPI_FUNC(void) PyObject_ReleaseBuffer(PyObject *obj, PyBuffer *view);
556+
557+
558+
/* C-API version of the releasebuffer function call. It
559+
checks to make sure the object has the required function
560+
pointer and issues the call. The obj must have the buffer
561+
interface or this function will cause a segfault (i.e. it
562+
is assumed to be called only after a corresponding
563+
getbuffer which already verified the existence of the
564+
tp_as_buffer pointer).
565+
566+
Returns 0 on success and -1 (with an error raised) on
567+
failure. This function always succeeds (as a NO-OP) if
568+
there is no releasebuffer function for the object so that
569+
it can always be called when the consumer is done with the
570+
buffer
571+
*/
572+
573+
PyAPI_FUNC(void *) PyBuffer_GetPointer(PyBuffer *view, Py_ssize_t *indices);
574+
575+
/* Get the memory area pointed to by the indices for the buffer given.
576+
Note that view->ndim is the assumed size of indices
577+
*/
578+
579+
PyAPI_FUNC(int) PyBuffer_SizeFromFormat(const char *);
580+
581+
/* Return the implied itemsize of the data-format area from a
582+
struct-style description */
583+
584+
585+
586+
PyAPI_FUNC(int) PyBuffer_ToContiguous(void *buf, PyBuffer *view,
587+
Py_ssize_t len, char fort);
588+
589+
PyAPI_FUNC(int) PyBuffer_FromContiguous(PyBuffer *view, void *buf,
590+
Py_ssize_t len, char fort);
591+
592+
593+
/* Copy len bytes of data from the contiguous chunk of memory
594+
pointed to by buf into the buffer exported by obj. Return
595+
0 on success and return -1 and raise a PyBuffer_Error on
596+
error (i.e. the object does not have a buffer interface or
597+
it is not working).
598+
599+
If fortran is 'F', then if the object is multi-dimensional,
600+
then the data will be copied into the array in
601+
Fortran-style (first dimension varies the fastest). If
602+
fortran is 'C', then the data will be copied into the array
603+
in C-style (last dimension varies the fastest). If fortran
604+
is 'A', then it does not matter and the copy will be made
605+
in whatever way is more efficient.
606+
607+
*/
608+
609+
PyAPI_FUNC(int) PyObject_CopyData(PyObject *dest, PyObject *src);
610+
611+
/* Copy the data from the src buffer to the buffer of destination
612+
*/
613+
614+
PyAPI_FUNC(int) PyBuffer_IsContiguous(PyBuffer *view, char fortran);
615+
616+
617+
PyAPI_FUNC(void) PyBuffer_FillContiguousStrides(int ndims,
618+
Py_ssize_t *shape,
619+
Py_ssize_t *strides,
620+
int itemsize,
621+
char fort);
622+
623+
/* Fill the strides array with byte-strides of a contiguous
624+
(Fortran-style if fortran is 'F' or C-style otherwise)
625+
array of the given shape with the given number of bytes
626+
per element.
627+
*/
628+
629+
PyAPI_FUNC(int) PyBuffer_FillInfo(PyBuffer *view, void *buf,
630+
Py_ssize_t len, int readonly,
631+
int flags);
632+
633+
/* Fills in a buffer-info structure correctly for an exporter
634+
that can only share a contiguous chunk of memory of
635+
"unsigned bytes" of the given length. Returns 0 on success
636+
and -1 (with raising an error) on error.
637+
*/
638+
639+
530640
/* Iterators */
531641

532642
PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *);

Include/bytesobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern "C" {
2121
/* Object layout */
2222
typedef struct {
2323
PyObject_VAR_HEAD
24+
int ob_exports; /* how many buffer exports */
2425
Py_ssize_t ob_alloc; /* How many bytes allocated */
2526
char *ob_bytes;
2627
} PyBytesObject;

Include/memoryobject.h

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
2+
/* Memory object interface */
3+
4+
#ifndef Py_MEMORYOBJECT_H
5+
#define Py_MEMORYOBJECT_H
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
typedef struct {
11+
PyObject_HEAD
12+
PyObject *base;
13+
PyBuffer view;
14+
} PyMemoryViewObject;
15+
16+
17+
PyAPI_DATA(PyTypeObject) PyMemoryView_Type;
18+
19+
#define PyMemory_Check(op) (Py_Type(op) == &PyMemoryView_Type)
20+
#define PyMemoryView(op) (((PyMemoryViewObject *)(op))->view)
21+
22+
#define Py_END_OF_MEMORY (-1)
23+
24+
PyAPI_FUNC(PyObject *) PyMemoryView_GetContiguous(PyObject *base, int buffertype,
25+
char fort);
26+
27+
/* Return a contiguous chunk of memory representing the buffer
28+
from an object in a memory view object. If a copy is made then the
29+
base object for the memory view will be a *new* bytes object.
30+
31+
Otherwise, the base-object will be the object itself and no
32+
data-copying will be done.
33+
34+
The buffertype argument can be PyBUF_READ, PyBUF_WRITE,
35+
PyBUF_UPDATEIFCOPY to determine whether the returned buffer
36+
should be READONLY, WRITEABLE, or set to update the
37+
original buffer if a copy must be made. If buffertype is
38+
PyBUF_WRITE and the buffer is not contiguous an error will
39+
be raised. In this circumstance, the user can use
40+
PyBUF_UPDATEIFCOPY to ensure that a a writeable temporary
41+
contiguous buffer is returned. The contents of this
42+
contiguous buffer will be copied back into the original
43+
object after the memoryview object is deleted as long as
44+
the original object is writeable and allows setting its
45+
memory to "readonly". If this is not allowed by the
46+
original object, then a BufferError is raised.
47+
48+
If the object is multi-dimensional and if fortran is 'F',
49+
the first dimension of the underlying array will vary the
50+
fastest in the buffer. If fortran is 'C', then the last
51+
dimension will vary the fastest (C-style contiguous). If
52+
fortran is 'A', then it does not matter and you will get
53+
whatever the object decides is more efficient.
54+
55+
A new reference is returned that must be DECREF'd when finished.
56+
*/
57+
58+
PyAPI_FUNC(PyObject *) PyMemoryView_FromObject(PyObject *base);
59+
60+
PyAPI_FUNC(PyObject *) PyMemoryView_FromMemory(PyBuffer *info);
61+
/* create new if bufptr is NULL
62+
will be a new bytesobject in base */
63+
64+
#ifdef __cplusplus
65+
}
66+
#endif
67+
#endif /* !Py_MEMORYOBJECT_H */

Include/object.h

+57-10
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,59 @@ typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
140140
typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
141141
typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
142142

143-
/* ssize_t-based buffer interface */
144-
typedef Py_ssize_t (*readbufferproc)(PyObject *, Py_ssize_t, void **);
145-
typedef Py_ssize_t (*writebufferproc)(PyObject *, Py_ssize_t, void **);
146-
typedef Py_ssize_t (*segcountproc)(PyObject *, Py_ssize_t *);
147-
typedef Py_ssize_t (*charbufferproc)(PyObject *, Py_ssize_t, char **);
143+
144+
/* buffer interface */
145+
typedef struct bufferinfo {
146+
void *buf;
147+
Py_ssize_t len;
148+
Py_ssize_t itemsize;
149+
int readonly;
150+
int ndim;
151+
char *format;
152+
Py_ssize_t *shape;
153+
Py_ssize_t *strides;
154+
Py_ssize_t *suboffsets;
155+
void *internal;
156+
} PyBuffer;
157+
158+
typedef int (*getbufferproc)(PyObject *, PyBuffer *, int);
159+
typedef void (*releasebufferproc)(PyObject *, PyBuffer *);
160+
161+
/* Flags for getting buffers */
162+
#define PyBUF_SIMPLE 0
163+
#define PyBUF_CHARACTER 1
164+
#define PyBUF_WRITEABLE 0x0002
165+
#define PyBUF_LOCKDATA 0x0004
166+
#define PyBUF_FORMAT 0x0008
167+
#define PyBUF_ND 0x0010
168+
#define PyBUF_STRIDES (0x0020 | PyBUF_ND)
169+
#define PyBUF_C_CONTIGUOUS (0x0040 | PyBUF_STRIDES)
170+
#define PyBUF_F_CONTIGUOUS (0x0080 | PyBUF_STRIDES)
171+
#define PyBUF_ANY_CONTIGUOUS (0x0100 | PyBUF_STRIDES)
172+
#define PyBUF_INDIRECT (0x0200 | PyBUF_STRIDES)
173+
174+
#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITEABLE)
175+
#define PyBUF_CONTIG_RO (PyBUF_ND)
176+
#define PyBUF_CONTIG_LCK (PyBUF_ND | PyBUF_LOCKDATA)
177+
178+
#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITEABLE)
179+
#define PyBUF_STRIDED_RO (PyBUF_STRIDES)
180+
#define PyBUF_STRIDED_LCK (PyBUF_STRIDES | PyBUF_LOCKDATA)
181+
182+
#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITEABLE | PyBUF_FORMAT)
183+
#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT)
184+
#define PyBUF_RECORDS_LCK (PyBUF_STRIDES | PyBUF_LOCKDATA | PyBUF_FORMAT)
185+
186+
#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITEABLE | PyBUF_FORMAT)
187+
#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT)
188+
#define PyBUF_FULL_LCK (PyBUF_INDIRECT | PyBUF_LOCKDATA | PyBUF_FORMAT)
189+
190+
191+
#define PyBUF_READ 0x100
192+
#define PyBUF_WRITE 0x200
193+
#define PyBUF_SHADOW 0x400
194+
195+
/* End buffer interface */
148196

149197
typedef int (*objobjproc)(PyObject *, PyObject *);
150198
typedef int (*visitproc)(PyObject *, void *);
@@ -218,14 +266,13 @@ typedef struct {
218266
objobjargproc mp_ass_subscript;
219267
} PyMappingMethods;
220268

269+
221270
typedef struct {
222-
readbufferproc bf_getreadbuffer;
223-
writebufferproc bf_getwritebuffer;
224-
segcountproc bf_getsegcount;
225-
charbufferproc bf_getcharbuffer;
271+
getbufferproc bf_getbuffer;
272+
releasebufferproc bf_releasebuffer;
273+
inquiry bf_multisegment;
226274
} PyBufferProcs;
227275

228-
229276
typedef void (*freefunc)(void *);
230277
typedef void (*destructor)(PyObject *);
231278
typedef int (*printfunc)(PyObject *, FILE *, int);

Include/pyerrors.h

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError;
138138
PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError;
139139
PyAPI_DATA(PyObject *) PyExc_ValueError;
140140
PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError;
141+
PyAPI_DATA(PyObject *) PyExc_BufferError;
141142
#ifdef MS_WINDOWS
142143
PyAPI_DATA(PyObject *) PyExc_WindowsError;
143144
#endif

Makefile.pre.in

+2
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ OBJECT_OBJS= \
300300
Objects/listobject.o \
301301
Objects/longobject.o \
302302
Objects/dictobject.o \
303+
Objects/memoryobject.o \
303304
Objects/methodobject.o \
304305
Objects/moduleobject.o \
305306
Objects/object.o \
@@ -533,6 +534,7 @@ PYTHON_HEADERS= \
533534
Include/iterobject.h \
534535
Include/listobject.h \
535536
Include/longobject.h \
537+
Include/memoryobject.h \
536538
Include/methodobject.h \
537539
Include/modsupport.h \
538540
Include/moduleobject.h \

Modules/_ctypes/_ctypes.c

+18-21
Original file line numberDiff line numberDiff line change
@@ -739,22 +739,33 @@ CharArray_set_raw(CDataObject *self, PyObject *value)
739739
{
740740
char *ptr;
741741
Py_ssize_t size;
742+
int rel = 0;
743+
PyBuffer view;
744+
742745
if (PyBuffer_Check(value)) {
743-
size = Py_Type(value)->tp_as_buffer->bf_getreadbuffer(value, 0, (void *)&ptr);
744-
if (size < 0)
745-
return -1;
746+
if (PyObject_GetBuffer(value, &view, PyBUF_SIMPLE) < 0)
747+
return -1;
748+
size = view.len;
749+
ptr = view.buf;
750+
rel = 1;
746751
} else if (-1 == PyString_AsStringAndSize(value, &ptr, &size)) {
747752
return -1;
748753
}
749754
if (size > self->b_size) {
750755
PyErr_SetString(PyExc_ValueError,
751756
"string too long");
752-
return -1;
757+
goto fail;
753758
}
754759

755760
memcpy(self->b_ptr, ptr, size);
756761

762+
if (rel)
763+
PyObject_ReleaseBuffer(value, &view);
757764
return 0;
765+
fail:
766+
if (rel)
767+
PyObject_ReleaseBuffer(value, &view);
768+
return -1;
758769
}
759770

760771
static PyObject *
@@ -2072,29 +2083,15 @@ static PyMemberDef CData_members[] = {
20722083
{ NULL },
20732084
};
20742085

2075-
static Py_ssize_t CData_GetBuffer(PyObject *_self, Py_ssize_t seg, void **pptr)
2086+
static int CData_GetBuffer(PyObject *_self, PyBuffer *view, int flags)
20762087
{
20772088
CDataObject *self = (CDataObject *)_self;
2078-
if (seg != 0) {
2079-
/* Hm. Must this set an exception? */
2080-
return -1;
2081-
}
2082-
*pptr = self->b_ptr;
2083-
return self->b_size;
2084-
}
2085-
2086-
static Py_ssize_t CData_GetSegcount(PyObject *_self, Py_ssize_t *lenp)
2087-
{
2088-
if (lenp)
2089-
*lenp = 1;
2090-
return 1;
2089+
return PyBuffer_FillInfo(view, self->b_ptr, self->b_size, 0, flags);
20912090
}
20922091

20932092
static PyBufferProcs CData_as_buffer = {
20942093
CData_GetBuffer,
2095-
CData_GetBuffer,
2096-
CData_GetSegcount,
2097-
NULL,
2094+
NULL,
20982095
};
20992096

21002097
/*

0 commit comments

Comments
 (0)