/* * Memoryview object implementation * -------------------------------- * * This implementation is a complete rewrite contributed by Stefan Krah in * Python 3.3. Substantial credit goes to Antoine Pitrou (who had already * fortified and rewritten the previous implementation) and Nick Coghlan * (who came up with the idea of the ManagedBuffer) for analyzing the complex * ownership rules. * */ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_memoryobject.h" // _PyManagedBuffer_Type #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_strhex.h" // _Py_strhex_with_sep() #include <stddef.h> // offsetof() /*[clinic input] class memoryview "PyMemoryViewObject *" "&PyMemoryView_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=e2e49d2192835219]*/ #include "clinic/memoryobject.c.h" /****************************************************************************/ /* ManagedBuffer Object */ /****************************************************************************/ /* ManagedBuffer Object: --------------------- The purpose of this object is to facilitate the handling of chained memoryviews that have the same underlying exporting object. PEP-3118 allows the underlying object to change while a view is exported. This could lead to unexpected results when constructing a new memoryview from an existing memoryview. Rather than repeatedly redirecting buffer requests to the original base object, all chained memoryviews use a single buffer snapshot. This snapshot is generated by the constructor _PyManagedBuffer_FromObject(). Ownership rules: ---------------- The master buffer inside a managed buffer is filled in by the original base object. shape, strides, suboffsets and format are read-only for all consumers. A memoryview's buffer is a private copy of the exporter's buffer. shape, strides and suboffsets belong to the memoryview and are thus writable. If a memoryview itself exports several buffers via memory_getbuf(), all buffer copies share shape, strides and suboffsets. In this case, the arrays are NOT writable. Reference count assumptions: ---------------------------- The 'obj' member of a Py_buffer must either be NULL or refer to the exporting base object. In the Python codebase, all getbufferprocs return a new reference to view.obj (example: bytes_buffer_getbuffer()). PyBuffer_Release() decrements view.obj (if non-NULL), so the releasebufferprocs must NOT decrement view.obj. */ static inline _PyManagedBufferObject * mbuf_alloc(void) { … } static PyObject * _PyManagedBuffer_FromObject(PyObject *base, int flags) { … } static void mbuf_release(_PyManagedBufferObject *self) { … } static void mbuf_dealloc(PyObject *_self) { … } static int mbuf_traverse(PyObject *_self, visitproc visit, void *arg) { … } static int mbuf_clear(PyObject *_self) { … } PyTypeObject _PyManagedBuffer_Type = …; /****************************************************************************/ /* MemoryView Object */ /****************************************************************************/ /* In the process of breaking reference cycles mbuf_release() can be called before memory_release(). */ #define BASE_INACCESSIBLE(mv) … #define CHECK_RELEASED(mv) … #define CHECK_RELEASED_INT(mv) … #define CHECK_RESTRICTED(mv) … #define CHECK_RESTRICTED_INT(mv) … /* See gh-92888. These macros signal that we need to check the memoryview again due to possible read after frees. */ #define CHECK_RELEASED_AGAIN(mv) … #define CHECK_RELEASED_INT_AGAIN(mv) … #define CHECK_LIST_OR_TUPLE(v) … #define VIEW_ADDR(mv) … /* Check for the presence of suboffsets in the first dimension. */ #define HAVE_PTR(suboffsets, dim) … /* Adjust ptr if suboffsets are present. */ #define ADJUST_PTR(ptr, suboffsets, dim) … /* Memoryview buffer properties */ #define MV_C_CONTIGUOUS(flags) … #define MV_F_CONTIGUOUS(flags) … #define MV_ANY_CONTIGUOUS(flags) … /* Fast contiguity test. Caller must ensure suboffsets==NULL and ndim==1. */ #define MV_CONTIGUOUS_NDIM1(view) … /* getbuffer() requests */ #define REQ_INDIRECT(flags) … #define REQ_C_CONTIGUOUS(flags) … #define REQ_F_CONTIGUOUS(flags) … #define REQ_ANY_CONTIGUOUS(flags) … #define REQ_STRIDES(flags) … #define REQ_SHAPE(flags) … #define REQ_WRITABLE(flags) … #define REQ_FORMAT(flags) … /**************************************************************************/ /* Copy memoryview buffers */ /**************************************************************************/ /* The functions in this section take a source and a destination buffer with the same logical structure: format, itemsize, ndim and shape are identical, with ndim > 0. NOTE: All buffers are assumed to have PyBUF_FULL information, which is the case for memoryviews! */ /* Assumptions: ndim >= 1. The macro tests for a corner case that should perhaps be explicitly forbidden in the PEP. */ #define HAVE_SUBOFFSETS_IN_LAST_DIM(view) … static inline int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src) { … } /* This is not a general function for determining format equivalence. It is used in copy_single() and copy_buffer() to weed out non-matching formats. Skipping the '@' character is specifically used in slice assignments, where the lvalue is already known to have a single character format. This is a performance hack that could be rewritten (if properly benchmarked). */ static inline int equiv_format(const Py_buffer *dest, const Py_buffer *src) { … } /* Two shapes are equivalent if they are either equal or identical up to a zero element at the same position. For example, in NumPy arrays the shapes [1, 0, 5] and [1, 0, 7] are equivalent. */ static inline int equiv_shape(const Py_buffer *dest, const Py_buffer *src) { … } /* Check that the logical structure of the destination and source buffers is identical. */ static int equiv_structure(const Py_buffer *dest, const Py_buffer *src) { … } /* Base case for recursive multi-dimensional copying. Contiguous arrays are copied with very little overhead. Assumptions: ndim == 1, mem == NULL or sizeof(mem) == shape[0] * itemsize. */ static void copy_base(const Py_ssize_t *shape, Py_ssize_t itemsize, char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets, char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets, char *mem) { … } /* Recursively copy a source buffer to a destination buffer. The two buffers have the same ndim, shape and itemsize. */ static void copy_rec(const Py_ssize_t *shape, Py_ssize_t ndim, Py_ssize_t itemsize, char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets, char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets, char *mem) { … } /* Faster copying of one-dimensional arrays. */ static int copy_single(PyMemoryViewObject *self, const Py_buffer *dest, const Py_buffer *src) { … } /* Recursively copy src to dest. Both buffers must have the same basic structure. Copying is atomic, the function never fails with a partial copy. */ static int copy_buffer(const Py_buffer *dest, const Py_buffer *src) { … } /* Initialize strides for a C-contiguous array. */ static inline void init_strides_from_shape(Py_buffer *view) { … } /* Initialize strides for a Fortran-contiguous array. */ static inline void init_fortran_strides_from_shape(Py_buffer *view) { … } /* Copy src to a contiguous representation. order is one of 'C', 'F' (Fortran) or 'A' (Any). Assumptions: src has PyBUF_FULL information, src->ndim >= 1, len(mem) == src->len. */ static int buffer_to_contiguous(char *mem, const Py_buffer *src, char order) { … } /****************************************************************************/ /* Constructors */ /****************************************************************************/ /* Initialize values that are shared with the managed buffer. */ static inline void init_shared_values(Py_buffer *dest, const Py_buffer *src) { … } /* Copy shape and strides. Reconstruct missing values. */ static void init_shape_strides(Py_buffer *dest, const Py_buffer *src) { … } static inline void init_suboffsets(Py_buffer *dest, const Py_buffer *src) { … } /* len = product(shape) * itemsize */ static inline void init_len(Py_buffer *view) { … } /* Initialize memoryview buffer properties. */ static void init_flags(PyMemoryViewObject *mv) { … } /* Allocate a new memoryview and perform basic initialization. New memoryviews are exclusively created through the mbuf_add functions. */ static inline PyMemoryViewObject * memory_alloc(int ndim) { … } /* Return a new memoryview that is registered with mbuf. If src is NULL, use mbuf->master as the underlying buffer. Otherwise, use src. The new memoryview has full buffer information: shape and strides are always present, suboffsets as needed. Arrays are copied to the memoryview's ob_array field. */ static PyObject * mbuf_add_view(_PyManagedBufferObject *mbuf, const Py_buffer *src) { … } /* Register an incomplete view: shape, strides, suboffsets and flags still need to be initialized. Use 'ndim' instead of src->ndim to determine the size of the memoryview's ob_array. Assumption: ndim <= PyBUF_MAX_NDIM. */ static PyObject * mbuf_add_incomplete_view(_PyManagedBufferObject *mbuf, const Py_buffer *src, int ndim) { … } /* Expose a raw memory area as a view of contiguous bytes. flags can be PyBUF_READ or PyBUF_WRITE. view->format is set to "B" (unsigned bytes). The memoryview has complete buffer information. */ PyObject * PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags) { … } /* Create a memoryview from a given Py_buffer. For simple byte views, PyMemoryView_FromMemory() should be used instead. This function is the only entry point that can create a master buffer without full information. Because of this fact init_shape_strides() must be able to reconstruct missing values. */ PyObject * PyMemoryView_FromBuffer(const Py_buffer *info) { … } /* Create a memoryview from an object that implements the buffer protocol, using the given flags. If the object is a memoryview, the new memoryview must be registered with the same managed buffer. Otherwise, a new managed buffer is created. */ static PyObject * PyMemoryView_FromObjectAndFlags(PyObject *v, int flags) { … } /* Create a memoryview from an object that implements the buffer protocol, using the given flags. If the object is a memoryview, the new memoryview must be registered with the same managed buffer. Otherwise, a new managed buffer is created. */ PyObject * _PyMemoryView_FromBufferProc(PyObject *v, int flags, getbufferproc bufferproc) { … } /* Create a memoryview from an object that implements the buffer protocol. If the object is a memoryview, the new memoryview must be registered with the same managed buffer. Otherwise, a new managed buffer is created. */ PyObject * PyMemoryView_FromObject(PyObject *v) { … } /* Copy the format string from a base object that might vanish. */ static int mbuf_copy_format(_PyManagedBufferObject *mbuf, const char *fmt) { … } /* Return a memoryview that is based on a contiguous copy of src. Assumptions: src has PyBUF_FULL_RO information, src->ndim > 0. Ownership rules: 1) As usual, the returned memoryview has a private copy of src->shape, src->strides and src->suboffsets. 2) src->format is copied to the master buffer and released in mbuf_dealloc(). The releasebufferproc of the bytes object is NULL, so it does not matter that mbuf_release() passes the altered format pointer to PyBuffer_Release(). */ static PyObject * memory_from_contiguous_copy(const Py_buffer *src, char order) { … } /* Return a new memoryview object based on a contiguous exporter with buffertype={PyBUF_READ, PyBUF_WRITE} and order={'C', 'F'ortran, or 'A'ny}. The logical structure of the input and output buffers is the same (i.e. tolist(input) == tolist(output)), but the physical layout in memory can be explicitly chosen. As usual, if buffertype=PyBUF_WRITE, the exporter's buffer must be writable, otherwise it may be writable or read-only. If the exporter is already contiguous with the desired target order, the memoryview will be directly based on the exporter. Otherwise, if the buffertype is PyBUF_READ, the memoryview will be based on a new bytes object. If order={'C', 'A'ny}, use 'C' order, 'F'ortran order otherwise. */ PyObject * PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) { … } /*[clinic input] @classmethod memoryview.__new__ object: object Create a new memoryview object which references the given object. [clinic start generated code]*/ static PyObject * memoryview_impl(PyTypeObject *type, PyObject *object) /*[clinic end generated code: output=7de78e184ed66db8 input=f04429eb0bdf8c6e]*/ { … } /*[clinic input] @classmethod memoryview._from_flags object: object flags: int Create a new memoryview object which references the given object. [clinic start generated code]*/ static PyObject * memoryview__from_flags_impl(PyTypeObject *type, PyObject *object, int flags) /*[clinic end generated code: output=bf71f9906c266ee2 input=f5f82fd0e744356b]*/ { … } /****************************************************************************/ /* Previously in abstract.c */ /****************************************************************************/ Py_buffer_full; int PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char order) { … } /****************************************************************************/ /* Release/GC management */ /****************************************************************************/ /* Inform the managed buffer that this particular memoryview will not access the underlying buffer again. If no other memoryviews are registered with the managed buffer, the underlying buffer is released instantly and marked as inaccessible for both the memoryview and the managed buffer. */ static void _memory_release(PyMemoryViewObject *self) { … } /*[clinic input] memoryview.release Release the underlying buffer exposed by the memoryview object. [clinic start generated code]*/ static PyObject * memoryview_release_impl(PyMemoryViewObject *self) /*[clinic end generated code: output=d0b7e3ba95b7fcb9 input=bc71d1d51f4a52f0]*/ { … } static void memory_dealloc(PyObject *_self) { … } static int memory_traverse(PyObject *_self, visitproc visit, void *arg) { … } static int memory_clear(PyObject *_self) { … } static PyObject * memory_enter(PyObject *self, PyObject *args) { … } static PyObject * memory_exit(PyObject *self, PyObject *args) { … } /****************************************************************************/ /* Casting format and shape */ /****************************************************************************/ #define IS_BYTE_FORMAT(f) … static inline Py_ssize_t get_native_fmtchar(char *result, const char *fmt) { … } static inline const char * get_native_fmtstr(const char *fmt) { … } /* Cast a memoryview's data type to 'format'. The input array must be C-contiguous. At least one of input-format, output-format must have byte size. The output array is 1-D, with the same byte length as the input array. Thus, view->len must be a multiple of the new itemsize. */ static int cast_to_1D(PyMemoryViewObject *mv, PyObject *format) { … } /* The memoryview must have space for 3*len(seq) elements. */ static Py_ssize_t copy_shape(Py_ssize_t *shape, const PyObject *seq, Py_ssize_t ndim, Py_ssize_t itemsize) { … } /* Cast a 1-D array to a new shape. The result array will be C-contiguous. If the result array does not have exactly the same byte length as the input array, raise ValueError. */ static int cast_to_ND(PyMemoryViewObject *mv, const PyObject *shape, int ndim) { … } static int zero_in_shape(PyMemoryViewObject *mv) { … } /* Cast a copy of 'self' to a different view. The input view must be C-contiguous. The function always casts the input view to a 1-D output according to 'format'. At least one of input-format, output-format must have byte size. If 'shape' is given, the 1-D view from the previous step will be cast to a C-contiguous view with new shape and strides. All casts must result in views that will have the exact byte size of the original input. Otherwise, an error is raised. */ /*[clinic input] memoryview.cast format: unicode shape: object = NULL Cast a memoryview to a new format or shape. [clinic start generated code]*/ static PyObject * memoryview_cast_impl(PyMemoryViewObject *self, PyObject *format, PyObject *shape) /*[clinic end generated code: output=bae520b3a389cbab input=138936cc9041b1a3]*/ { … } /*[clinic input] memoryview.toreadonly Return a readonly version of the memoryview. [clinic start generated code]*/ static PyObject * memoryview_toreadonly_impl(PyMemoryViewObject *self) /*[clinic end generated code: output=2c7e056f04c99e62 input=dc06d20f19ba236f]*/ { … } /**************************************************************************/ /* getbuffer */ /**************************************************************************/ static int memory_getbuf(PyObject *_self, Py_buffer *view, int flags) { … } static void memory_releasebuf(PyObject *_self, Py_buffer *view) { … } /* Buffer methods */ static PyBufferProcs memory_as_buffer = …; /****************************************************************************/ /* Optimized pack/unpack for all native format specifiers */ /****************************************************************************/ /* Fix exceptions: 1) Include format string in the error message. 2) OverflowError -> ValueError. 3) The error message from PyNumber_Index() is not ideal. */ static int type_error_int(const char *fmt) { … } static int value_error_int(const char *fmt) { … } static int fix_error_int(const char *fmt) { … } /* Accept integer objects or objects with an __index__() method. */ static long pylong_as_ld(PyObject *item) { … } static unsigned long pylong_as_lu(PyObject *item) { … } static long long pylong_as_lld(PyObject *item) { … } static unsigned long long pylong_as_llu(PyObject *item) { … } static Py_ssize_t pylong_as_zd(PyObject *item) { … } static size_t pylong_as_zu(PyObject *item) { … } /* Timings with the ndarray from _testbuffer.c indicate that using the struct module is around 15x slower than the two functions below. */ #define UNPACK_SINGLE(dest, ptr, type) … /* Unpack a single item. 'fmt' can be any native format character in struct module syntax. This function is very sensitive to small changes. With this layout gcc automatically generates a fast jump table. */ static inline PyObject * unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) { … } #define PACK_SINGLE(ptr, src, type) … /* Pack a single item. 'fmt' can be any native format character in struct module syntax. */ static int pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt) { … } /****************************************************************************/ /* unpack using the struct module */ /****************************************************************************/ /* For reasonable performance it is necessary to cache all objects required for unpacking. An unpacker can handle the format passed to unpack_from(). Invariant: All pointer fields of the struct should either be NULL or valid pointers. */ struct unpacker { … }; static struct unpacker * unpacker_new(void) { … } static void unpacker_free(struct unpacker *x) { … } /* Return a new unpacker for the given format. */ static struct unpacker * struct_get_unpacker(const char *fmt, Py_ssize_t itemsize) { … } /* unpack a single item */ static PyObject * struct_unpack_single(const char *ptr, struct unpacker *x) { … } /****************************************************************************/ /* Representations */ /****************************************************************************/ /* allow explicit form of native format */ static inline const char * adjust_fmt(const Py_buffer *view) { … } /* Base case for multi-dimensional unpacking. Assumption: ndim == 1. */ static PyObject * tolist_base(PyMemoryViewObject *self, const char *ptr, const Py_ssize_t *shape, const Py_ssize_t *strides, const Py_ssize_t *suboffsets, const char *fmt) { … } /* Unpack a multi-dimensional array into a nested list. Assumption: ndim >= 1. */ static PyObject * tolist_rec(PyMemoryViewObject *self, const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape, const Py_ssize_t *strides, const Py_ssize_t *suboffsets, const char *fmt) { … } /* Return a list representation of the memoryview. Currently only buffers with native format strings are supported. */ /*[clinic input] memoryview.tolist Return the data in the buffer as a list of elements. [clinic start generated code]*/ static PyObject * memoryview_tolist_impl(PyMemoryViewObject *self) /*[clinic end generated code: output=a6cda89214fd5a1b input=21e7d0c1860b211a]*/ { … } /*[clinic input] memoryview.tobytes order: str(accept={str, NoneType}, c_default="NULL") = 'C' Return the data in the buffer as a byte string. Order can be {'C', 'F', 'A'}. When order is 'C' or 'F', the data of the original array is converted to C or Fortran order. For contiguous views, 'A' returns an exact copy of the physical memory. In particular, in-memory Fortran order is preserved. For non-contiguous views, the data is converted to C first. order=None is the same as order='C'. [clinic start generated code]*/ static PyObject * memoryview_tobytes_impl(PyMemoryViewObject *self, const char *order) /*[clinic end generated code: output=1288b62560a32a23 input=0efa3ddaeda573a8]*/ { … } /*[clinic input] memoryview.hex sep: object = NULL An optional single character or byte to separate hex bytes. bytes_per_sep: int = 1 How many bytes between separators. Positive values count from the right, negative values count from the left. Return the data in the buffer as a str of hexadecimal numbers. Example: >>> value = memoryview(b'\xb9\x01\xef') >>> value.hex() 'b901ef' >>> value.hex(':') 'b9:01:ef' >>> value.hex(':', 2) 'b9:01ef' >>> value.hex(':', -2) 'b901:ef' [clinic start generated code]*/ static PyObject * memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep, int bytes_per_sep) /*[clinic end generated code: output=430ca760f94f3ca7 input=539f6a3a5fb56946]*/ { … } static PyObject * memory_repr(PyObject *_self) { … } /**************************************************************************/ /* Indexing and slicing */ /**************************************************************************/ static char * lookup_dimension(const Py_buffer *view, char *ptr, int dim, Py_ssize_t index) { … } /* Get the pointer to the item at index. */ static char * ptr_from_index(const Py_buffer *view, Py_ssize_t index) { … } /* Get the pointer to the item at tuple. */ static char * ptr_from_tuple(const Py_buffer *view, PyObject *tup) { … } /* Return the item at index. In a one-dimensional view, this is an object with the type specified by view->format. Otherwise, the item is a sub-view. The function is used in memory_subscript() and memory_as_sequence. */ static PyObject * memory_item(PyObject *_self, Py_ssize_t index) { … } /* Return the item at position *key* (a tuple of indices). */ static PyObject * memory_item_multi(PyMemoryViewObject *self, PyObject *tup) { … } static inline int init_slice(Py_buffer *base, PyObject *key, int dim) { … } static int is_multislice(PyObject *key) { … } static Py_ssize_t is_multiindex(PyObject *key) { … } /* mv[obj] returns an object holding the data for one element if obj fully indexes the memoryview or another memoryview object if it does not. 0-d memoryview objects can be referenced using mv[...] or mv[()] but not with anything else. */ static PyObject * memory_subscript(PyObject *_self, PyObject *key) { … } static int memory_ass_sub(PyObject *_self, PyObject *key, PyObject *value) { … } static Py_ssize_t memory_length(PyObject *_self) { … } /* As mapping */ static PyMappingMethods memory_as_mapping = …; /* As sequence */ static PySequenceMethods memory_as_sequence = …; /**************************************************************************/ /* Comparisons */ /**************************************************************************/ #define MV_COMPARE_EX … #define MV_COMPARE_NOT_IMPL … /* Translate a StructError to "not equal". Preserve other exceptions. */ static int fix_struct_error_int(void) { … } /* Unpack and compare single items of p and q using the struct module. */ static int struct_unpack_cmp(const char *p, const char *q, struct unpacker *unpack_p, struct unpacker *unpack_q) { … } /* Unpack and compare single items of p and q. If both p and q have the same single element native format, the comparison uses a fast path (gcc creates a jump table and converts memcpy into simple assignments on x86/x64). Otherwise, the comparison is delegated to the struct module, which is 30-60x slower. */ #define CMP_SINGLE(p, q, type) … static inline int unpack_cmp(const char *p, const char *q, char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { … } /* Base case for recursive array comparisons. Assumption: ndim == 1. */ static int cmp_base(const char *p, const char *q, const Py_ssize_t *shape, const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets, const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets, char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { … } /* Recursively compare two multi-dimensional arrays that have the same logical structure. Assumption: ndim >= 1. */ static int cmp_rec(const char *p, const char *q, Py_ssize_t ndim, const Py_ssize_t *shape, const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets, const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets, char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { … } static PyObject * memory_richcompare(PyObject *v, PyObject *w, int op) { … } /**************************************************************************/ /* Hash */ /**************************************************************************/ static Py_hash_t memory_hash(PyObject *_self) { … } /**************************************************************************/ /* getters */ /**************************************************************************/ static PyObject * _IntTupleFromSsizet(int len, Py_ssize_t *vals) { … } static PyObject * memory_obj_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_nbytes_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_format_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_itemsize_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_shape_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_strides_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_suboffsets_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_readonly_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_ndim_get(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_c_contiguous(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_f_contiguous(PyObject *_self, void *Py_UNUSED(ignored)) { … } static PyObject * memory_contiguous(PyObject *_self, void *Py_UNUSED(ignored)) { … } PyDoc_STRVAR(memory_obj_doc, "The underlying object of the memoryview."); PyDoc_STRVAR(memory_nbytes_doc, "The amount of space in bytes that the array would use in\n" " a contiguous representation."); PyDoc_STRVAR(memory_readonly_doc, "A bool indicating whether the memory is read only."); PyDoc_STRVAR(memory_itemsize_doc, "The size in bytes of each element of the memoryview."); PyDoc_STRVAR(memory_format_doc, "A string containing the format (in struct module style)\n" " for each element in the view."); PyDoc_STRVAR(memory_ndim_doc, "An integer indicating how many dimensions of a multi-dimensional\n" " array the memory represents."); PyDoc_STRVAR(memory_shape_doc, "A tuple of ndim integers giving the shape of the memory\n" " as an N-dimensional array."); PyDoc_STRVAR(memory_strides_doc, "A tuple of ndim integers giving the size in bytes to access\n" " each element for each dimension of the array."); PyDoc_STRVAR(memory_suboffsets_doc, "A tuple of integers used internally for PIL-style arrays."); PyDoc_STRVAR(memory_c_contiguous_doc, "A bool indicating whether the memory is C contiguous."); PyDoc_STRVAR(memory_f_contiguous_doc, "A bool indicating whether the memory is Fortran contiguous."); PyDoc_STRVAR(memory_contiguous_doc, "A bool indicating whether the memory is contiguous."); PyDoc_STRVAR(memory_exit_doc, "__exit__($self, /, *exc_info)\n--\n\n" "Release the underlying buffer exposed by the memoryview object."); static PyGetSetDef memory_getsetlist[] = …; static PyMethodDef memory_methods[] = …; /**************************************************************************/ /* Memoryview Iterator */ /**************************************************************************/ PyTypeObject _PyMemoryIter_Type; memoryiterobject; static void memoryiter_dealloc(PyObject *self) { … } static int memoryiter_traverse(PyObject *self, visitproc visit, void *arg) { … } static PyObject * memoryiter_next(PyObject *self) { … } static PyObject * memory_iter(PyObject *seq) { … } PyTypeObject _PyMemoryIter_Type = …; PyTypeObject PyMemoryView_Type = …;