Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/_collections_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def _f(): pass
dict_itemiterator = type(iter({}.items()))
list_iterator = type(iter([]))
list_reverseiterator = type(iter(reversed([])))
memory_iterator = type(iter(memoryview(b'')))
memory_reverseiterator = type(reversed(memoryview(b'')))
range_iterator = type(iter(range(0)))
longrange_iterator = type(iter(range(1 << 1000)))
set_iterator = type(iter(set()))
Expand Down Expand Up @@ -325,6 +327,8 @@ def __subclasshook__(cls, C):
Iterator.register(dict_itemiterator)
Iterator.register(list_iterator)
Iterator.register(list_reverseiterator)
Iterator.register(memory_iterator)
Iterator.register(memory_reverseiterator)
Iterator.register(range_iterator)
Iterator.register(longrange_iterator)
Iterator.register(set_iterator)
Expand Down
6 changes: 4 additions & 2 deletions Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,10 @@ def test_reversed(self):
b = tp(self._source)
m = self._view(b)
aslist = list(reversed(m.tolist()))
self.assertEqual(list(reversed(m)), aslist)
self.assertEqual(list(reversed(m)), list(m[::-1]))
with self.subTest(view_type=type(m)):
self.assertTrue(hasattr(m, '__reversed__'))
self.assertEqual(list(reversed(m)), aslist)
self.assertEqual(list(reversed(m)), list(m[::-1]))

def test_toreadonly(self):
for tp in self._types:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :meth:`~object.__reversed__` to :class:`memoryview` objects. Patch by
Bénédikt Tran.
20 changes: 19 additions & 1 deletion Objects/clinic/memoryobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

166 changes: 117 additions & 49 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3286,6 +3286,7 @@ static PyMethodDef memory_methods[] = {
MEMORYVIEW__FROM_FLAGS_METHODDEF
{"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, memory_exit_doc},
MEMORYVIEW___REVERSED___METHODDEF
{NULL, NULL}
};

Expand All @@ -3294,6 +3295,7 @@ static PyMethodDef memory_methods[] = {
/**************************************************************************/

PyTypeObject _PyMemoryIter_Type;
PyTypeObject _PyMemoryRevIter_Type;

typedef struct {
PyObject_HEAD
Expand All @@ -3303,10 +3305,53 @@ typedef struct {
const char *it_fmt;
} memoryiterobject;

#define _PyMemoryViewIter_CAST(PTR) ((memoryiterobject *)(PTR))

static PyObject *
memoryiter_new(PyObject *self, int reversed)
{
assert(reversed == 0 || reversed == 1);

if (!PyMemoryView_Check(self)) {
PyErr_BadInternalCall();
return NULL;
}

PyMemoryViewObject *sequence = _PyMemoryView_CAST(self);
const Py_buffer *view = &sequence->view;
if (view->ndim == 0) {
PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
return NULL;
}
else if (view->ndim != 1) {
PyErr_SetString(PyExc_NotImplementedError,
"multi-dimensional sub-views are not implemented");
return NULL;
}
const char *fmt = adjust_fmt(view);
if (fmt == NULL) {
return NULL;
}

PyTypeObject *itertype = reversed
? &_PyMemoryRevIter_Type
: &_PyMemoryIter_Type;
memoryiterobject *it = PyObject_GC_New(memoryiterobject, itertype);
if (it == NULL) {
return NULL;
}
it->it_fmt = fmt;
it->it_length = memory_length(self);
it->it_index = reversed ? (it->it_length - 1) : 0;
it->it_seq = _PyMemoryView_CAST(Py_NewRef(self));
_PyObject_GC_TRACK(it);
return (PyObject *)(it);
}

static void
memoryiter_dealloc(PyObject *self)
{
memoryiterobject *it = (memoryiterobject *)self;
memoryiterobject *it = _PyMemoryViewIter_CAST(self);
_PyObject_GC_UNTRACK(it);
Py_XDECREF(it->it_seq);
PyObject_GC_Del(it);
Expand All @@ -3315,34 +3360,36 @@ memoryiter_dealloc(PyObject *self)
static int
memoryiter_traverse(PyObject *self, visitproc visit, void *arg)
{
memoryiterobject *it = (memoryiterobject *)self;
memoryiterobject *it = _PyMemoryViewIter_CAST(self);
Py_VISIT(it->it_seq);
return 0;
}

static inline PyObject *
memoryiter_iter_nth(PyMemoryViewObject *seq, Py_ssize_t nth, const char *fmt)
{
CHECK_RELEASED(seq);
const Py_buffer *view = &seq->view;
const char *head = (const char *)seq->view.buf;
const char *ptr = head + view->strides[0] * nth;
ptr = ADJUST_PTR(ptr, view->suboffsets, 0);
if (ptr == NULL) {
return NULL;
}
return unpack_single(seq, ptr, fmt);
}

static PyObject *
memoryiter_next(PyObject *self)
{
memoryiterobject *it = (memoryiterobject *)self;
PyMemoryViewObject *seq;
seq = it->it_seq;
memoryiterobject *it = _PyMemoryViewIter_CAST(self);
PyMemoryViewObject *seq = it->it_seq;
if (seq == NULL) {
return NULL;
}

if (it->it_index < it->it_length) {
CHECK_RELEASED(seq);
Py_buffer *view = &(seq->view);
char *ptr = (char *)seq->view.buf;

ptr += view->strides[0] * it->it_index++;
ptr = ADJUST_PTR(ptr, view->suboffsets, 0);
if (ptr == NULL) {
return NULL;
}
return unpack_single(seq, ptr, it->it_fmt);
return memoryiter_iter_nth(seq, it->it_index++, it->it_fmt);
}

it->it_seq = NULL;
Py_DECREF(seq);
return NULL;
Expand All @@ -3351,53 +3398,74 @@ memoryiter_next(PyObject *self)
static PyObject *
memory_iter(PyObject *seq)
{
if (!PyMemoryView_Check(seq)) {
PyErr_BadInternalCall();
return NULL;
}
PyMemoryViewObject *obj = (PyMemoryViewObject *)seq;
int ndims = obj->view.ndim;
if (ndims == 0) {
PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
return memoryiter_new(seq, 0);
}

PyTypeObject _PyMemoryIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "memory_iterator",
.tp_basicsize = sizeof(memoryiterobject),
.tp_dealloc = memoryiter_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = memoryiter_traverse,
.tp_iter = PyObject_SelfIter,
.tp_iternext = memoryiter_next,
};

/**************************************************************************/
/* Memoryview Reversed Iterator */
/**************************************************************************/

static PyObject *
memorview_reverse_iternext(PyObject *self)
{
memoryiterobject *it = _PyMemoryViewIter_CAST(self);
PyMemoryViewObject *seq = it->it_seq;
if (seq == NULL) {
return NULL;
}
if (ndims != 1) {
PyErr_SetString(PyExc_NotImplementedError,
"multi-dimensional sub-views are not implemented");
return NULL;
if (it->it_index >= 0 && it->it_index < it->it_length) {
// FEAT(picnixz): can we omit "it->it_index < it->it_length"?
return memoryiter_iter_nth(seq, it->it_index--, it->it_fmt);
}
it->it_seq = NULL;
Py_DECREF(seq);
return NULL;
}

const char *fmt = adjust_fmt(&obj->view);
if (fmt == NULL) {
return NULL;
}
/*[clinic input]
memoryview.__reversed__

memoryiterobject *it;
it = PyObject_GC_New(memoryiterobject, &_PyMemoryIter_Type);
if (it == NULL) {
return NULL;
}
it->it_fmt = fmt;
it->it_length = memory_length((PyObject *)obj);
it->it_index = 0;
it->it_seq = (PyMemoryViewObject*)Py_NewRef(obj);
_PyObject_GC_TRACK(it);
return (PyObject *)it;
Return a reverse iterator over this memory view.
[clinic start generated code]*/

static PyObject *
memoryview___reversed___impl(PyMemoryViewObject *self)
/*[clinic end generated code: output=25f9b4345f4720ad input=3c35e9267ad7fcc6]*/
{
// NOTE(picnixz): generic implementation via reversed(...) is faster
// if the iterator is not used or if zero or few items are iterated.
//
// When the number of items is sufficiently large (>= 2^5), benchmarks
// show that this implementation improves for-loop performances. Note
// that materializing the specialized reversed iterator is likely to
// be slower than materializing a generic reversed object instance.
return memoryiter_new(_PyObject_CAST(self), 1);
}

PyTypeObject _PyMemoryIter_Type = {

PyTypeObject _PyMemoryRevIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "memory_iterator",
.tp_name = "memory_reverseiterator",
.tp_basicsize = sizeof(memoryiterobject),
// methods
.tp_dealloc = memoryiter_dealloc,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = memoryiter_traverse,
.tp_iter = PyObject_SelfIter,
.tp_iternext = memoryiter_next,
.tp_iternext = memorview_reverse_iternext,
};


PyTypeObject PyMemoryView_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"memoryview", /* tp_name */
Expand Down
2 changes: 2 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2220,6 +2220,7 @@ extern PyTypeObject _PyAnextAwaitable_Type;
extern PyTypeObject _PyLegacyEventHandler_Type;
extern PyTypeObject _PyLineIterator;
extern PyTypeObject _PyMemoryIter_Type;
extern PyTypeObject _PyMemoryRevIter_Type;
extern PyTypeObject _PyPositionsIterator;
extern PyTypeObject _Py_GenericAliasIterType;

Expand Down Expand Up @@ -2328,6 +2329,7 @@ static PyTypeObject* static_types[] = {
&_PyLineIterator,
&_PyManagedBuffer_Type,
&_PyMemoryIter_Type,
&_PyMemoryRevIter_Type,
&_PyMethodWrapper_Type,
&_PyNamespace_Type,
&_PyNone_Type,
Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/cpython/globals-to-fix.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Objects/codeobject.c - _PyPositionsIterator -
Objects/genericaliasobject.c - _Py_GenericAliasIterType -
# Not in a .h file:
Objects/memoryobject.c - _PyMemoryIter_Type -
Objects/memoryobject.c - _PyMemoryRevIter_Type -
Objects/unicodeobject.c - _PyUnicodeASCIIIter_Type -
Objects/unionobject.c - _PyUnion_Type -
Python/context.c - _PyContextTokenMissing_Type -
Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ Modules/posixmodule.c - _Py_open_cloexec_works -
Modules/posixmodule.c - environ -
Objects/object.c - _Py_GenericAliasIterType -
Objects/object.c - _PyMemoryIter_Type -
Objects/object.c - _PyMemoryRevIter_Type -
Objects/object.c - _PyLineIterator -
Objects/object.c - _PyPositionsIterator -
Python/perf_trampoline.c - _Py_trampoline_func_start -
Expand Down
Loading