Changes to pep-3118.txt

This commit is contained in:
Travis E. Oliphant 2007-04-26 21:43:28 +00:00
parent ac9a2701b5
commit 5f33b4b632
1 changed files with 359 additions and 166 deletions

View File

@ -1,8 +1,9 @@
PEP: 3118 PEP: 3118
Title: Revising the buffer protocol Title: Revising the buffer protocol
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Travis Oliphant <oliphant@ee.byu.edu>, Carl Banks <pythondev@aerojockey.com> Authors: Travis Oliphant <oliphant@ee.byu.edu>, Carl Banks <pythondev@aerojockey.com>
Status: Draft Status: Draft
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst Content-Type: text/x-rst
@ -13,8 +14,8 @@ Abstract
======== ========
This PEP proposes re-designing the buffer interface (PyBufferProcs This PEP proposes re-designing the buffer interface (PyBufferProcs
function pointers) to improve the way Python allows memory sharing function pointers) to improve the way Python allows memory sharing in
in Python 3.0 Python 3.0
In particular, it is proposed that the character buffer portion In particular, it is proposed that the character buffer portion
of the API be elminated and the multiple-segment portion be of the API be elminated and the multiple-segment portion be
@ -73,7 +74,7 @@ has issues:
NumPy uses the notion of constant striding in each dimension as its NumPy uses the notion of constant striding in each dimension as its
basic concept of an array. With this concept, a simple sub-region basic concept of an array. With this concept, a simple sub-region
of a larger array can be described without copying the data. T of a larger array can be described without copying the data.
Thus, stride information is the additional information that must be Thus, stride information is the additional information that must be
shared. shared.
@ -151,7 +152,7 @@ Change the PyBufferProcs structure to
:: ::
typedef int (*getbufferproc)(PyObject *obj, struct bufferinfo *view, int flags) typedef int (*getbufferproc)(PyObject *obj, PyBuffer *view, int flags)
This function returns 0 on success and -1 on failure (and raises an This function returns 0 on success and -1 on failure (and raises an
error). The first variable is the "exporting" object. The second error). The first variable is the "exporting" object. The second
@ -160,54 +161,137 @@ then no information is returned but a lock on the memory is still
obtained. In this case, the corresponding releasebuffer should also obtained. In this case, the corresponding releasebuffer should also
be called with NULL. be called with NULL.
The third argument indicates what kind of buffer the exporter is allowed to return. It tells the The third argument indicates what kind of buffer the consumer is
exporter what elements the bufferinfo structure the consumer is going to make use of. This prepared to deal with and therefore what kind of buffer the exporter
allows the exporter to simplify and/or raise an error if it can't support the operation. is allowed to return. The new buffer interface allows for much more
complicated memory sharing possibilities. Some consumers may not be
able to handle all the complexibity but may want to see if the
exporter will let them take a simpler view to its memory.
It also allows the caller to make a request for a simple "view" and In addition, some exporters may not be able to share memory in every
receive it or have an error raised if it's not possible. possible way and may need to raise errors to signal to some consumers
that something is just not possible. These errors should be
PyErr_BufferError unless there is another error that is actually
causing the problem. The exporter can use flags information to
simplify how much of the PyBuffer structure is filled in with
non-default values and/or raise an error if the object can't support a
simpler view of its memory.
All of the following assume that at least buf, len, and readonly will be The exporter should always fill in all elements of the buffer
utilized by the caller. structure (with defaults if nothing else is requested).
Py_BUF_SIMPLE Some flags are useful for requesting a specific kind of memory
The returned buffer will only be assumed to be readable (the object segment, while others indicate to the exporter what kind of
may or may not have writeable memory). Only the buf, len, and information the consumer can deal with. If certain information is not
readonly variables may be accessed. The format will be asked for by the consumer, but the exporter cannot share its memory
assumed to be unsigned bytes. This is a "stand-alone" flag constant. without that information, then a PyErr_BufferError should be raised.
It never needs to be \|'d to the others.
Py_BUF_WRITEABLE
The returned buffer must be writeable. If it cannot be, then raise an error.
Py_BUF_READONLY PyBUF_SIMPLE
The returned buffer must be readonly and the underlying object should make
its memory readonly if that is possible.
Py_BUF_FORMAT This is the default flag state (0). The returned buffer may or may
The consumer will be using the format string information so make sure that not have writeable memory. The format will be assumed to be
member is filled correctly. unsigned bytes . This is a "stand-alone" flag constant. It never
needs to be |'d to the others. The exporter will raise an error if
it cannot provide such a contiguous buffer of bytes.
Py_BUF_SHAPE PyBUF_REQ_WRITEABLE
The consumer can (and might) make use of using the ndims and shape members of the structure
so make sure they are filled in correctly.
Py_BUF_STRIDES (implies SHAPE) The returned buffer must be writeable. If it is not writeable,
The consumer can (and might) make use of the strides member of the structure (as well then raise an error.
as ndims and shape)
Py_BUF_OFFSETS (implies STRIDES) PyBUF_REQ_LOCKDATA
The consumer can (and might) make use of the suboffsets member (as well as
ndims, shape, and strides)
Thus, the consumer simply wanting an contiguous chunk of bytes from The returned buffer must be readonly. If the object is already
the object would use Py_BUF_SIMPLE, while a consumer that understands read-only or it can make its memory read-only (and there are no
how to make use of the most complicated cases would use other views on the object) then it should do so and return the
Py_BUF_OFFSETS. buffer information. If the object does not have read-only memory
(or cannot make it read-only), then an error should be raised.
PyBUF_REQ_FORMAT
The returned buffer must have true format information if this flag
is provided. This would be used when the consumer is going to be
checking for what 'kind' of data is actually stored. An exporter
should always be able to provide this information if requested. If
format is not explicitly requested then the format must be returned
as NULL (which means "B")
PyBUF_REQ_ALIGNED (implies PyBUF_REQ_FORMAT)
The returned buffer must have all (primitive data-type entries)
aligned as the compiler would align them
PyBUF_ALW_ND
The returned buffer must provide shape information. The memory will
be assumed C-style contiguous (last dimension varies the fastest).
The exporter may raise an error if it cannot provide this kind of
contiguous buffer.
PyBUF_ALW_STRIDES (implies PyBUF_ALW_ND)
The returned buffer must provide strides information. This would be
used when the consumer can handle strided, discontiguous arrays.
Handling strides automatically assumes you can handle shape.
The exporter may raise an error if cannot provide a strided-only
representation of the data (i.e. without the suboffsets).
PyBUF_REQ_C_CONTIGUOUS
PyBUF_REQ_F_CONTIGUOUS
PyBUF_REQ_ANY_CONTIGUOUS
These flags indicate that the returned buffer must be respectively,
C-contiguous (last dimension varies the fastest), Fortran
contiguous (first dimension varies the fastest) or either one.
All of these flags imply PyBUF_ALW_STRIDES and guarantee that the
strides buffer info structure will be filled in correctly.
PyBUF_ALW_INDIRECT (implies PyBUF_ALW_STRIDES)
The returned buffer must have suboffsets information. This would
be used when the consumer can handle indirect array referencing
implied by these suboffsets.
Specialized combinations of flags for specific kinds of memory_sharing.
Multi-dimensional (but contiguous)
PyBUF_CONTIG (PyBUF_ALW_ND | PyBUF_REQ_WRITEABLE | PyBUF_REQ_ALIGNED)
PyBUF_CONTIG_RO (PyBUF_ALW_ND | PyBUF_REQ_ALIGNED)
PyBUF_CONTIG_LCK (PyBUF_ALW_ND | PyBUF_REQ_LOCKDATA | PyBUF_REQ_ALIGNED)
Multi-dimensional using strides but aligned
PyBUF_STRIDED (PyBUF_ALW_STRIDES | PyBUF_REQ_WRITEABLE | PyBUF_REQ_ALIGNED)
PyBUF_STRIDED_RO (PyBUF_ALW_STRIDES | PyBUF_REQ_ALIGNED)
PyBUF_STRIDED_LCK (PyBUF_ALW_STRIDES | PyBUF_REQ_LOCKDATA | PyBUF_REQ_ALIGNED)
Multi-dimensional using strides and not necessarily aligned
PyBUF_RECORDS (PyBUF_ALW_STRIDES | PyBUF_REQ_WRITEABLE | PyBUF_REQ_FORMAT)
PyBUF_RECORDS_RO (PyBUF_ALW_STRIDES | PyBUF_REQ_FORMAT)
PyBUF_RECORDS_LCK (PyBUF_ALW_STRIDES | PyBUF_REQ_LOCKDATA | PyBUF_REQ_FORMAT)
Multi-dimensional using sub-offsets
PyBUF_FULL (PyBUF_ALW_INDIRECT | PyBUF_REQ_WRITEABLE | PyBUF_REQ_FORMAT)
PyBUF_FULL_RO (PyBUF_ALW_INDIRECT | PyBUF_REQ_FORMAT)
PyBUF_FULL_LCK (PyBUF_ALW_INDIRECT | PyBUF_REQ_LOCKDATA | PyBUF_REQ_FORMAT)
Thus, the consumer simply wanting a contiguous chunk of bytes from
the object would use PyBUF_SIMPLE, while a consumer that understands
how to make use of the most complicated cases could use PyBUF_FULL
The format information is only guaranteed to be non-NULL if
PyBUF_REQ_FORMAT is in the flag argument, otherwise it is expected the
consumer will assume unsigned bytes.
There is a C-API that simple exporting objects can use to fill-in the There is a C-API that simple exporting objects can use to fill-in the
buffer info structure correctly according to the provided flags if a buffer info structure correctly according to the provided flags if a
contiguous chunk of memory is all that can be exported. contiguous chunk of "unsigned bytes" is all that can be exported.
The bufferinfo structure is:: The bufferinfo structure is::
@ -216,21 +300,23 @@ The bufferinfo structure is::
void *buf; void *buf;
Py_ssize_t len; Py_ssize_t len;
int readonly; int readonly;
char *format; const char *format;
int ndims; int ndim;
Py_ssize_t *shape; Py_ssize_t *shape;
Py_ssize_t *strides; Py_ssize_t *strides;
Py_ssize_t *suboffsets; Py_ssize_t *suboffsets;
int itemsize;
void *internal; void *internal;
}; } PyBuffer;
Before calling this function, the bufferinfo structure can be filled with Before calling this function, the bufferinfo structure can be filled
whatever. Upon return from getbufferproc, the bufferinfo structure is filled in with whatever. Upon return from getbufferproc, the bufferinfo
with relevant information about the buffer. This same bufferinfo structure is filled in with relevant information about the buffer.
structure must be passed to bf_releasebuffer (if available) when the This same bufferinfo structure must be passed to bf_releasebuffer (if
consumer is done with the memory. The caller is responsible for available) when the consumer is done with the memory. The caller is
keeping a reference to obj until releasebuffer is called (i.e. this responsible for keeping a reference to obj until releasebuffer is
call does not alter the reference count of obj). called (i.e. the call to bf_getbuffer does not alter the reference
count of obj).
The members of the bufferinfo structure are: The members of the bufferinfo structure are:
@ -243,32 +329,38 @@ len
bytes per item of memory. bytes per item of memory.
readonly readonly
an integer variable to hold whether or not the memory is an integer variable to hold whether or not the memory is readonly.
readonly. 1 means the memory is readonly, zero means the 1 means the memory is readonly, zero means the memory is
memory is writeable. writeable, and -1 means the memory was "locked" (changed from
writeable to readonly) when this PyBuffer structure was filled-in
and therefore should be unlocked when this PyBuffer structure is
"released" (this is supported only by some exporters).
format format
a NULL-terminated format-string (following the struct-style syntax a NULL-terminated format-string (following the struct-style syntax
including extensions) indicating what is in each element of including extensions) indicating what is in each element of
memory. The number of elements is len / itemsize, where itemsize memory. The number of elements is len / itemsize, where itemsize
is the number of bytes implied by the format. For standard is the number of bytes implied by the format. This can be NULL which
unsigned bytes use a format string of "B". implies standard unsigned bytes ("B").
ndims ndims
a variable storing the number of dimensions the memory represents. a variable storing the number of dimensions the memory represents.
Must be >=0. Must be >=0. A value of 0 means that shape and strides and suboffsets
must be NULL (i.e. the memory represents a scalar).
shape shape
an array of ``Py_ssize_t`` of length ``ndims`` indicating the an array of ``Py_ssize_t`` of length ``ndims`` indicating the
shape of the memory as an N-D array. Note that ``((*shape)[0] * shape of the memory as an N-D array. Note that ``((*shape)[0] *
... * (*shape)[ndims-1])*itemsize = len``. ... * (*shape)[ndims-1])*itemsize = len``. If ndims is 0 (indicating
a scalar), then this must be NULL.
strides strides
address of a ``Py_ssize_t*`` variable that will be filled with a address of a ``Py_ssize_t*`` variable that will be filled with a
pointer to an array of ``Py_ssize_t`` of length ``*ndims`` pointer to an array of ``Py_ssize_t`` of length ``ndims`` (or NULL
indicating the number of bytes to skip to get to the next element if ndims is 0). indicating the number of bytes to skip to get to
in each dimension. For C-style contiguous arrays (where the the next element in each dimension. If this is not requested by
last-dimension varies the fastest) this must be filled in. the caller (PyBUF_ALW_STRIDES is not set), then this should be set
to NULL which indicates a C-style contiguous array.
suboffsets suboffsets
address of a ``Py_ssize_t *`` variable that will be filled with a address of a ``Py_ssize_t *`` variable that will be filled with a
@ -279,11 +371,11 @@ suboffsets
suboffset value that it negative indicates that no de-referencing suboffset value that it negative indicates that no de-referencing
should occur (striding in a contiguous memory block). If all should occur (striding in a contiguous memory block). If all
suboffsets are negative (i.e. no de-referencing is needed, then suboffsets are negative (i.e. no de-referencing is needed, then
this must be NULL. this must be NULL (the default value).
For clarity, here is a function that returns a pointer to the For clarity, here is a function that returns a pointer to the
element in an N-D array pointed to by an N-dimesional index when element in an N-D array pointed to by an N-dimesional index when
there are both strides and suboffsets.:: there are both non-NULL strides and suboffsets.::
void* get_item_pointer(int ndim, void* buf, Py_ssize_t* strides, void* get_item_pointer(int ndim, void* buf, Py_ssize_t* strides,
Py_ssize_t* suboffsets, Py_ssize_t *indices) { Py_ssize_t* suboffsets, Py_ssize_t *indices) {
@ -304,28 +396,36 @@ suboffsets
the location of the starting pointer directly (i.e. buf would the location of the starting pointer directly (i.e. buf would
be modified). be modified).
itemsize
This is a storage for the itemsize of each element of the shared
memory. It is strictly un-necessary as it can be obtained using
PyBuffer_SizeFromFormat, however an exporter may know this
information without parsing the format string and it is necessary
to know the itemsize for proper interpretation of striding.
Therefore, storing it is more convenient and faster.
internal internal
This is for use internally by the exporting object. For example, This is for use internally by the exporting object. For example,
this might be re-cast as an integer by the exporter and used to this might be re-cast as an integer by the exporter and used to
store flags about whether or not the shape, strides, and suboffsets store flags about whether or not the shape, strides, and suboffsets
arrays must be freed when the buffer is released. The consumer arrays must be freed when the buffer is released. The consumer
should never touch this value. should never alter this value.
The exporter is responsible for making sure the memory pointed to by The exporter is responsible for making sure that any memory pointed to
buf, format, shape, strides, and suboffsets is valid until by buf, format, shape, strides, and suboffsets is valid until
releasebuffer is called. If the exporter wants to be able to change releasebuffer is called. If the exporter wants to be able to change
shape, strides, and/or suboffsets before releasebuffer is called then an object's shape, strides, and/or suboffsets before releasebuffer is
it should allocate those arrays when getbuffer is called (pointing to called then it should allocate those arrays when getbuffer is called
them in the buffer-info structure provided) and free them when (pointing to them in the buffer-info structure provided) and free them
releasebuffer is called. when releasebuffer is called.
The same bufferinfo struct should be used in the release-buffer The same bufferinfo struct should be used in the release-buffer
interface call. The caller is responsible for the memory of the interface call. The caller is responsible for the memory of the
bufferinfo structure itself. bufferinfo structure itself.
``typedef int (*releasebufferproc)(PyObject *obj, struct bufferinfo *view)`` ``typedef int (*releasebufferproc)(PyObject *obj, PyBuffer *view)``
Callers of getbufferproc must make sure that this function is Callers of getbufferproc must make sure that this function is
called when memory previously acquired from the object is no called when memory previously acquired from the object is no
longer needed. The exporter of the interface must make sure that longer needed. The exporter of the interface must make sure that
@ -348,7 +448,8 @@ each object.
All that is specifically required by the exporter, however, is to All that is specifically required by the exporter, however, is to
ensure that any memory shared through the bufferinfo structure remains ensure that any memory shared through the bufferinfo structure remains
valid until releasebuffer is called on the bufferinfo structure. valid until releasebuffer is called on the bufferinfo structure
exporting that memory.
New C-API calls are proposed New C-API calls are proposed
@ -362,7 +463,8 @@ Return 1 if the getbuffer function is available otherwise 0.
:: ::
int PyObject_GetBuffer(PyObject *obj, struct bufferinfo *view, int flags) int PyObject_GetBuffer(PyObject *obj, PyBuffer *view,
int flags)
This is a C-API version of the getbuffer function call. It checks to This is a C-API version of the getbuffer function call. It checks to
make sure object has the required function pointer and issues the make sure object has the required function pointer and issues the
@ -370,61 +472,85 @@ call. Returns -1 and raises an error on failure and returns 0 on
success. success.
:: ::
int PyObject_ReleaseBuffer(PyObject *obj, PyBuffer *view)
int PyObject_ReleaseBuffer(PyObject *obj, struct bufferinfo *view) This is a C-API version of the releasebuffer function call. It checks
to make sure the object has the required function pointer and issues
This is a C-API version of the releasebuffer function call. It checks to the call. Returns 0 on success and -1 (with an error raised) on
make sure the object has the required function pointer and issues the call. Returns 0 failure. This function always succeeds if there is no releasebuffer
on success and -1 (with an error raised) on failure. This function always function for the object.
succeeds if there is no releasebuffer function for the object.
:: ::
PyObject *PyObject_GetMemoryView(PyObject *obj) PyObject *PyObject_GetMemoryView(PyObject *obj)
Return a memory-view object from an object that defines the buffer interface. Return a memory-view object from an object that defines the buffer interface.
If make_ro is non-zero then request that the memory is made read-only until
release buffer is called.
A memory-view object is an extended buffer object that should replace A memory-view object is an extended buffer object that could replace
the buffer object in Python 3K. It's C-structure is:: the buffer object (but doesn't have to as that could be kept as a
simple 1-d memory-view object). It's C-structure is
::
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *base; PyObject *base;
struct bufferinfo view; int ndims;
int itemsize; Py_ssize_t *starts; /* slice starts */
int flags; Py_ssize_t *stops; /* slice stops */
Py_ssize_t *steps; /* slice steps */
} PyMemoryViewObject; } PyMemoryViewObject;
This is very similar to the current buffer object except offset has This is functionally similar to the current buffer object except only
been removed because ptr can just be modified by offset and a single a reference to base is kept. The actual memory for base must be
offset is not sufficient for the sub-offsets. Also the hash has been re-grabbed using the buffer-protocol, whenever it is actually
removed because using the buffer object as a hash even if it is needed (after which it is immediately released).
read-only is rarely useful.
Also, the format, ndims, shape, strides, and suboffsets have been The getbuffer and releasebuffer for this object use the underlying
added. These additions will allow multi-dimensional slicing of the base object (adjusted as needed using the slice information). If the
memory-view object which can be added at some point. This object number of dimensions of the base object (or the strides or the size)
always owns it's own shape, strides, and suboffsets arrays and it's has changed when a new view is requested, then the getbuffer will
own format string, but always borrows the memory from the object trigger an error. Methods on the MemoryView object will allow
pointed to by base. manual locking and unlocking of the underlying buffer.
The itemsize is a convenience and specifies the number of bytes This memory-view object will support mult-dimensional slicing and be
indicated by the format string if positive. the first object provided with Python to do so. Slices of the
memory-view object are other memory-view objects. When an "element"
from the memory-view is returned it is always a bytes object whose
format should be interpreted by the format attribute of the memoryview
object. The struct module can be used to "decode" the bytes in Python
if desired.
This object never reallocates ptr, shape, strides, subboffsets or The Python name will be
format and therefore does not need to keep track of how many views it
has exported. __builtin__.memory
Methods:
lock
unlock
__getitem__ (will support multi-dimensional slicing)
__setitem__ (will support multi-dimensional slicing)
tobytes (obtain a bytes-object of everything in the memory).
tolist (obtain a "nested" list of the memory. Everything
is interpreted into standard Python objects
as the struct module unpack would do).
Attributes (taken from the memory of the base object)
format
itemsize
shape
strides
suboffsets
size
readonly
ndim
It exports a view using the base object. It releases a view by
releasing the view on the base object. Because, it will never
re-allocate memory, it does not need to keep track of how many it has
exported but simple reference counting will suffice.
:: ::
int PyObject_SizeFromFormat(char *) int PyBuffer_SizeFromFormat(const char *)
Return the implied itemsize of the data-format area from a struct-style Return the implied itemsize of the data-format area from a struct-style
description. description.
@ -432,65 +558,82 @@ description.
:: ::
int PyObject_GetContiguous(PyObject *obj, void **buf, Py_ssize_t *len, int PyObject_GetContiguous(PyObject *obj, void **buf, Py_ssize_t *len,
int fortran) char **format, char fortran)
Return a contiguous chunk of memory representing the buffer. If a Return a contiguous chunk of memory representing the buffer. If a
copy is made then return 1. If no copy was needed return 0. If an copy is made then return 1. If no copy was needed return 0. If an
error occurred in probing the buffer interface, then return -1. The error occurred in probing the buffer interface, then return -1. The
contiguous chunk of memory is pointed to by ``*buf`` and the length of contiguous chunk of memory is pointed to by ``*buf`` and the length of
that memory is ``*len``. If the object is multi-dimensional, then if that memory is ``*len``. If the object is multi-dimensional, then if
fortran is 1, the first dimension of the underlying array will vary fortran is 'F', the first dimension of the underlying array will vary
the fastest in the buffer. If fortran is 0, then the last dimension the fastest in the buffer. If fortran is 'C', then the last dimension
will vary the fastest (C-style contiguous). If fortran is -1, then it will vary the fastest (C-style contiguous). If fortran is 'A', then it
does not matter and you will get whatever the object decides is more does not matter and you will get whatever the object decides is more
efficient. efficient. If a copy is made, then the memory must be freed by calling
``PyMem_Free``.
:: ::
int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len, int PyObject_CopyToObject(PyObject *obj, void *buf, Py_ssize_t len,
int fortran) char fortran)
Copy ``len`` bytes of data pointed to by the contiguous chunk of Copy ``len`` bytes of data pointed to by the contiguous chunk of
memory pointed to by ``buf`` into the buffer exported by obj. Return memory pointed to by ``buf`` into the buffer exported by obj. Return
0 on success and return -1 and raise an error on failure. If the 0 on success and return -1 and raise an error on failure. If the
object does not have a writeable buffer, then an error is raised. If object does not have a writeable buffer, then an error is raised. If
fortran is 1, then if the object is multi-dimensional, then the data fortran is 'F', then if the object is multi-dimensional, then the data
will be copied into the array in Fortran-style (first dimension varies will be copied into the array in Fortran-style (first dimension varies
the fastest). If fortran is 0, then the data will be copied into the the fastest). If fortran is 'C', then the data will be copied into the
array in C-style (last dimension varies the fastest). If fortran is -1, then array in C-style (last dimension varies the fastest). If fortran is 'A', then
it does not matter and the copy will be made in whatever way is more it does not matter and the copy will be made in whatever way is more
efficient. efficient.
The last two C-API calls allow a standard way of getting data in and
These last three C-API calls allow a standard way of getting data in and
out of Python objects into contiguous memory areas no matter how it is out of Python objects into contiguous memory areas no matter how it is
actually stored. These calls use the extended buffer interface to perform actually stored. These calls use the extended buffer interface to perform
their work. their work.
:: ::
int PyObject_IsContiguous(struct bufferinfo *view, int fortran); int PyBuffer_IsContiguous(PyBuffer *view, char fortran);
Return 1 if the memory defined by the view object is C-style (fortran = 0) Return 1 if the memory defined by the view object is C-style (fortran
or Fortran-style (fortran = 1) contiguous. Return 0 otherwise. = 'C') or Fortran-style (fortran = 'F') contiguous or either one
(fortran = 'A'). Return 0 otherwise.
::
int PyBuffer_IsAligned(PyBuffer *view);
Return 1 if the memory at all elements of the array implied by the
view object is aligned
:: ::
void PyObject_FillContiguousStrides(int *ndims, Py_ssize_t *shape, void PyBuffer_FillContiguousStrides(int *ndims, Py_ssize_t *shape,
int itemsize, int itemsize,
Py_ssize_t *strides, int fortran) Py_ssize_t *strides, char fortran)
Fill the strides array with byte-strides of a contiguous (C-style if Fill the strides array with byte-strides of a contiguous (C-style if
fortran is 0 or Fortran-style if fortran is 1) array of the given fortran is 0 or Fortran-style if fortran is 1) array of the given
shape with the given number of bytes per element. shape with the given number of bytes per element.
:: ::
int PyBuffer_FillInfo(PyBuffer *view, void *buf,
Py_ssize_t len, int readonly, int infoflags)
int PyObject_FillBufferInfo(struct bufferinfo *view, void *buf, Py_ssize_t len, Fills in a buffer-info structure correctly for an exporter that can
int readonly, int infoflags) only share a contiguous chunk of memory of "unsigned bytes" of the
given length. Returns 0 on success and -1 (with raising an error) on
error.
Fills in a buffer-info structure correctly for an exporter that can only share ::
a contiguous chunk of memory of "unsigned bytes" of the given length. Returns 0 on success PyErr_BufferError
and -1 (with raising an error) on error
A new error object for returning buffer errors which arise because an
exporter cannot provide the kind of buffer that a consumer expects.
This will also be raised when a consumer requests a buffer from an
object that does not provide the protocol.
Additions to the struct string-syntax Additions to the struct string-syntax
@ -521,22 +664,29 @@ Character Description
':name:' optional name of the preceeding element ':name:' optional name of the preceeding element
'X{}' pointer to a function (optional function 'X{}' pointer to a function (optional function
signature inside {}) signature inside {})
' \n\t' ignored (allow better readability) -- this may already be true ' \n\t' ignored (allow better readability)
-- this may already be true
================ =========== ================ ===========
The struct module will be changed to understand these as well and The struct module will be changed to understand these as well and
return appropriate Python objects on unpacking. Un-packing a return appropriate Python objects on unpacking. Unpacking a
long-double will return a decimal object or a ctypes long-double. long-double will return a decimal object or a ctypes long-double.
Unpacking 'u' or 'w' will return Python unicode. Unpacking a Unpacking 'u' or 'w' will return Python unicode. Unpacking a
multi-dimensional array will return a list of lists. Un-packing a multi-dimensional array will return a list (of lists if >1d).
pointer will return a ctypes pointer object. Un-packing a bit will Unpacking a pointer will return a ctypes pointer object. Unpacking a
return a Python Bool. Spaces in the struct-string syntax will be function pointer will return a ctypes call-object (perhaps). Unpacking
ignored. Unpacking a named-object will return a Python class with a bit will return a Python Bool. White-space in the struct-string
attributes having those names. syntax will be ignored if it isn't already. Unpacking a named-object
will return some kind of named-tuple-like object that acts like a
tuple but whose entries can also be accessed by name. Unpacking a
nested structure will return a nested tuple.
Endian-specification ('=','>','<') is also allowed inside the Endian-specification ('!', '@','=','>','<', '^') is also allowed
string so that it can change if needed. The previously-specified inside the string so that it can change if needed. The
endian string is in force until changed. The default endian is '='. previously-specified endian string is in force until changed. The
default endian is '@' which means native data-types and alignment. If
un-aligned, native data-types are requested, then the endian
specification is '^'.
According to the struct-module, a number can preceed a character According to the struct-module, a number can preceed a character
code to specify how many of that type there are. The code to specify how many of that type there are. The
@ -551,16 +701,21 @@ Examples of Data-Format Descriptions
==================================== ====================================
Here are some examples of C-structures and how they would be Here are some examples of C-structures and how they would be
represented using the struct-style syntax: represented using the struct-style syntax.
<named> is the constructor for a named-tuple (not-specified yet).
float float
'f' 'f' <--> Python float
complex double complex double
'Zd' 'Zd' <--> Python complex
RGB Pixel data RGB Pixel data
'BBB' or 'B:r: B:g: B:b:' 'BBB' <--> (int, int, int)
'B:r: B:g: B:b:' <--> <named>((int, int, int), ('r','g','b'))
Mixed endian (weird but possible) Mixed endian (weird but possible)
'>i:big: <i:little:' '>i:big: <i:little:' <--> <named>((int, int), ('big', 'little'))
Nested structure Nested structure
:: ::
@ -586,7 +741,9 @@ Nested array
int ival; int ival;
double data[16*4]; double data[16*4];
} }
'i:ival: (16,4)d:data:' """i:ival:
(16,4)d:data:
"""
Code to be affected Code to be affected
@ -662,10 +819,12 @@ Examples
========= =========
Ex. 1 Ex. 1
---------- -----------
This example shows how an image object that uses contiguous lines might expose its buffer.:: This example shows how an image object that uses contiguous lines might expose its buffer.::
::
struct rgba { struct rgba {
unsigned char r, g, b, a; unsigned char r, g, b, a;
}; };
@ -688,7 +847,9 @@ In order to access, say, the red value of the pixel at x=30, y=50, you'd use "li
So what does ImageObject's getbuffer do? Leaving error checking out:: So what does ImageObject's getbuffer do? Leaving error checking out::
int Image_getbuffer(PyObject *self, struct bufferinfo *view, int flags) { ::
int Image_getbuffer(PyObject *self, PyBuffer *view, int flags) {
static Py_ssize_t suboffsets[2] = { -1, 0 }; static Py_ssize_t suboffsets[2] = { -1, 0 };
@ -710,7 +871,7 @@ So what does ImageObject's getbuffer do? Leaving error checking out::
} }
int Image_releasebuffer(PyObject *self, struct bufferinfo *view) { int Image_releasebuffer(PyObject *self, PyBuffer *view) {
self->view_count--; self->view_count--;
return 0; return 0;
} }
@ -721,9 +882,11 @@ Ex. 2
This example shows how an object that wants to expose a contiguous This example shows how an object that wants to expose a contiguous
chunk of memory (which will never be re-allocated while the object is chunk of memory (which will never be re-allocated while the object is
alive) would do that.:: alive) would do that.
int myobject_getbuffer(PyObject *self, struct bufferinfo *view, int flags) { ::
int myobject_getbuffer(PyObject *self, PyBuffer *view, int flags) {
void *buf; void *buf;
Py_ssize_t len; Py_ssize_t len;
@ -736,18 +899,19 @@ alive) would do that.::
return PyObject_FillBufferInfo(view, buf, len, readonly, flags); return PyObject_FillBufferInfo(view, buf, len, readonly, flags);
} }
/* No releasebuffer is necessary because the memory will never /* No releasebuffer is necessary because the memory will never
be re-allocated so the locking mechanism is not needed be re-allocated so the locking mechanism is not needed
*/ */
Ex. 3 Ex. 3
----------- -----------
A consumer that wants to only get a simple contiguous chunk of bytes A consumer that wants to only get a simple contiguous chunk of bytes
from a Python object, obj would do the following:: from a Python object, obj would do the following:
::
struct bufferinfo view; PyBuffer view;
int ret; int ret;
if (PyObject_GetBuffer(obj, &view, Py_BUF_SIMPLE) < 0) { if (PyObject_GetBuffer(obj, &view, Py_BUF_SIMPLE) < 0) {
@ -767,6 +931,35 @@ from a Python object, obj would do the following::
} }
Ex. 4
-----------
A consumer that wants to be able to use any object's memory but is
writing an algorithm that only handle contiguous memory could do the following:
::
void *buf;
Py_ssize_t len;
char *format;
if (PyObject_GetContiguous(obj, &buf, &len, &format, 0) < 0) {
/* error return */
}
/* process memory pointed to by buffer if format is correct */
/* Optional:
if, after processing, we want to copy data from buffer back
into the the object
we could do
*/
if (PyObject_CopyToObject(obj, buf, len, 0) < 0) {
/* error return */
}
Copyright Copyright