From 699b263ba499f792609ca7b9c1a61cb7b7534f01 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Thu, 3 Aug 2023 19:54:59 +0200 Subject: [PATCH] Implement DoubleEndedIterator for PyTupleIterator --- newsfragments/3366.added.md | 1 + src/buffer.rs | 2 +- src/types/mod.rs | 1 + src/types/tuple.rs | 50 +++++++++++++++++++++++++++++++++---- 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 newsfragments/3366.added.md diff --git a/newsfragments/3366.added.md b/newsfragments/3366.added.md new file mode 100644 index 00000000000..ffc4eff8186 --- /dev/null +++ b/newsfragments/3366.added.md @@ -0,0 +1 @@ +Add implementation `DoubleEndedIterator` for `PyTupleIterator`. diff --git a/src/buffer.rs b/src/buffer.rs index eb48a7564c6..20c950aea1e 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -495,7 +495,7 @@ impl PyBuffer { err::error_on_minusone(py, unsafe { ffi::PyBuffer_ToContiguous( - target.as_ptr() as *mut raw::c_void, + target.as_mut_ptr() as *mut raw::c_void, #[cfg(Py_3_11)] &*self.0, #[cfg(not(Py_3_11))] diff --git a/src/types/mod.rs b/src/types/mod.rs index 6cf52593669..eb862655b30 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -75,6 +75,7 @@ pub mod iter { pub use super::dict::PyDictIterator; pub use super::frozenset::PyFrozenSetIterator; pub use super::set::PySetIterator; + pub use super::tuple::PyTupleIterator; } // Implementations core to all native types diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 687acae03f6..f6b85a360ae 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -232,16 +232,23 @@ pub struct PyTupleIterator<'a> { length: usize, } +impl<'a> PyTupleIterator<'a> { + unsafe fn get_item(&self, index: usize) -> &'a PyAny { + #[cfg(any(Py_LIMITED_API, PyPy))] + let item = self.tuple.get_item(index).expect("tuple.get failed"); + #[cfg(not(any(Py_LIMITED_API, PyPy)))] + let item = self.tuple.get_item_unchecked(index); + item + } +} + impl<'a> Iterator for PyTupleIterator<'a> { type Item = &'a PyAny; #[inline] - fn next(&mut self) -> Option<&'a PyAny> { + fn next(&mut self) -> Option { if self.index < self.length { - #[cfg(any(Py_LIMITED_API, PyPy))] - let item = self.tuple.get_item(self.index).expect("tuple.get failed"); - #[cfg(not(any(Py_LIMITED_API, PyPy)))] - let item = unsafe { self.tuple.get_item_unchecked(self.index) }; + let item = unsafe { self.get_item(self.index) }; self.index += 1; Some(item) } else { @@ -256,6 +263,18 @@ impl<'a> Iterator for PyTupleIterator<'a> { } } +impl<'a> DoubleEndedIterator for PyTupleIterator<'a> { + fn next_back(&mut self) -> Option { + if self.index < self.length { + let item = unsafe { self.get_item(self.length - 1) }; + self.length -= 1; + Some(item) + } else { + None + } + } +} + impl<'a> ExactSizeIterator for PyTupleIterator<'a> { fn len(&self) -> usize { self.length.saturating_sub(self.index) @@ -513,6 +532,27 @@ mod tests { }); } + #[test] + fn test_iter_rev() { + Python::with_gil(|py| { + let ob = (1, 2, 3).to_object(py); + let tuple: &PyTuple = ob.downcast(py).unwrap(); + assert_eq!(3, tuple.len()); + let mut iter = tuple.iter().rev(); + + assert_eq!(iter.size_hint(), (3, Some(3))); + + assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); + assert_eq!(iter.size_hint(), (2, Some(2))); + + assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); + assert_eq!(iter.size_hint(), (1, Some(1))); + + assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); + assert_eq!(iter.size_hint(), (0, Some(0))); + }); + } + #[test] fn test_into_iter() { Python::with_gil(|py| {