--- src/lib.rs.before
+++ src/lib.rs
@@ -61,6 +61,52 @@
.call_method1(pyo3::intern!(py, "set_result"), (value,))?;
}
Ok(())
+}
+
+// Built-in containers need no Python iterator allocation. Read the current
+// length at each step, preserving list mutation by user-supplied pair iterators.
+enum HeaderItems<'py> {
+ List(Bound<'py, PyList>, usize),
+ Tuple(Bound<'py, pyo3::types::PyTuple>, usize),
+ Other(Bound<'py, pyo3::types::PyIterator>),
+}
+impl<'py> HeaderItems<'py> {
+ fn new(value: &Bound<'py, PyAny>) -> PyResult<Self> {
+ if value.is_exact_instance_of::<PyList>() {
+ Ok(Self::List(value.cast::<PyList>()?.clone(), 0))
+ } else if value.is_exact_instance_of::<pyo3::types::PyTuple>() {
+ Ok(Self::Tuple(
+ value.cast::<pyo3::types::PyTuple>()?.clone(),
+ 0,
+ ))
+ } else {
+ Ok(Self::Other(value.try_iter()?))
+ }
+ }
+}
+impl<'py> Iterator for HeaderItems<'py> {
+ type Item = PyResult<Bound<'py, PyAny>>;
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ Self::List(list, index) => {
+ if *index >= list.len() {
+ return None;
+ }
+ let item = list.get_item(*index);
+ *index += 1;
+ Some(item)
+ }
+ Self::Tuple(tuple, index) => {
+ if *index >= tuple.len() {
+ return None;
+ }
+ let item = tuple.get_item(*index);
+ *index += 1;
+ Some(item)
+ }
+ Self::Other(iterator) => iterator.next(),
+ }
+ }
}
fn header_pair<'py>(
@@ -786,7 +832,7 @@
let mut has_date = false;
let mut close = false;
if let Some(h) = msg.get_item(pyo3::intern!(py, "headers"))? {
- for item in h.try_iter()? {
+ for item in HeaderItems::new(&h)? {
let item = item?;
let (name, value) = header_pair(&item)?;
let name = name.as_bytes();
@@ -969,7 +1015,7 @@
let mut output = Vec::new();
let mut headers = Vec::new();
if let Some(h) = msg.get_item(pyo3::intern!(py, "headers"))? {
- for item in h.try_iter()? {
+ for item in HeaderItems::new(&h)? {
let (n, v) = header_pair(&item?)?;
let n = n.as_bytes().to_ascii_lowercase();
let v = v.as_bytes();