From 2fa0b0d756ec0ad5bd66be48dcf6411e879ffb73 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Fri, 27 Aug 2021 06:03:46 +0300
Subject: [PATCH 1/8] bpo-45026: More compact range iterator

---
 Lib/test/test_range.py | 18 ++++++++++
 Objects/rangeobject.c  | 77 ++++++++++++++++++++----------------------
 2 files changed, 55 insertions(+), 40 deletions(-)

diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py
index 107c0e2e11c7ce..9cc864af874633 100644
--- a/Lib/test/test_range.py
+++ b/Lib/test/test_range.py
@@ -421,6 +421,24 @@ def test_large_exhausted_iterator_pickling(self):
             self.assertEqual(list(i), [])
             self.assertEqual(list(i2), [])
 
+    def test_iterator_unpickle_compat(self):
+        testcases = [
+            b'c__builtin__\niter\n(c__builtin__\nxrange\n(I10\nI20\nI2\ntRtRI2\nb.',
+            b'c__builtin__\niter\n(c__builtin__\nxrange\n(K\nK\x14K\x02tRtRK\x02b.',
+            b'\x80\x02c__builtin__\niter\nc__builtin__\nxrange\nK\nK\x14K\x02\x87R\x85RK\x02b.',
+            b'\x80\x03cbuiltins\niter\ncbuiltins\nrange\nK\nK\x14K\x02\x87R\x85RK\x02b.',
+            b'\x80\x04\x951\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x8c\x04iter\x93\x8c\x08builtins\x8c\x05range\x93K\nK\x14K\x02\x87R\x85RK\x02b.',
+
+            b'c__builtin__\niter\n(c__builtin__\nxrange\n(L-36893488147419103232L\nI20\nI2\ntRtRL18446744073709551623L\nb.',
+            b'c__builtin__\niter\n(c__builtin__\nxrange\n(L-36893488147419103232L\nK\x14K\x02tRtRL18446744073709551623L\nb.',
+            b'\x80\x02c__builtin__\niter\nc__builtin__\nxrange\n\x8a\t\x00\x00\x00\x00\x00\x00\x00\x00\xfeK\x14K\x02\x87R\x85R\x8a\t\x07\x00\x00\x00\x00\x00\x00\x00\x01b.',
+            b'\x80\x03cbuiltins\niter\ncbuiltins\nrange\n\x8a\t\x00\x00\x00\x00\x00\x00\x00\x00\xfeK\x14K\x02\x87R\x85R\x8a\t\x07\x00\x00\x00\x00\x00\x00\x00\x01b.',
+            b'\x80\x04\x95C\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x8c\x04iter\x93\x8c\x08builtins\x8c\x05range\x93\x8a\t\x00\x00\x00\x00\x00\x00\x00\x00\xfeK\x14K\x02\x87R\x85R\x8a\t\x07\x00\x00\x00\x00\x00\x00\x00\x01b.',
+        ]
+        for t in testcases:
+            it = pickle.loads(t)
+            self.assertEqual(list(it), [14, 16, 18])
+
     def test_odd_bug(self):
         # This used to raise a "SystemError: NULL result without error"
         # because the range validation step was eating the exception
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 3e05707b1cee69..f74c83522ec9de 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -766,7 +766,6 @@ PyTypeObject PyRange_Type = {
 
 typedef struct {
         PyObject_HEAD
-        long    index;
         long    start;
         long    step;
         long    len;
@@ -775,18 +774,19 @@ typedef struct {
 static PyObject *
 rangeiter_next(rangeiterobject *r)
 {
-    if (r->index < r->len)
-        /* cast to unsigned to avoid possible signed overflow
-           in intermediate calculations. */
-        return PyLong_FromLong((long)(r->start +
-                                      (unsigned long)(r->index++) * r->step));
+    if (r->len > 0) {
+        long result = r->start;
+        r->start += r->step;
+        r->len--;
+        return PyLong_FromLong(result);
+    }
     return NULL;
 }
 
 static PyObject *
 rangeiter_len(rangeiterobject *r, PyObject *Py_UNUSED(ignored))
 {
-    return PyLong_FromLong(r->len - r->index);
+    return PyLong_FromLong(r->len);
 }
 
 PyDoc_STRVAR(length_hint_doc,
@@ -813,8 +813,8 @@ rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored))
     if (range == NULL)
         goto err;
     /* return the result */
-    return Py_BuildValue("N(N)i", _PyEval_GetBuiltinId(&PyId_iter),
-                         range, r->index);
+    return Py_BuildValue("N(N)O", _PyEval_GetBuiltinId(&PyId_iter),
+                         range, Py_None);
 err:
     Py_XDECREF(start);
     Py_XDECREF(stop);
@@ -833,7 +833,8 @@ rangeiter_setstate(rangeiterobject *r, PyObject *state)
         index = 0;
     else if (index > r->len)
         index = r->len; /* exhausted iterator */
-    r->index = index;
+    r->start += index * r->step;
+    r->len -= index;
     Py_RETURN_NONE;
 }
 
@@ -931,13 +932,11 @@ fast_range_iter(long start, long stop, long step)
         return NULL;
     }
     it->len = (long)ulen;
-    it->index = 0;
     return (PyObject *)it;
 }
 
 typedef struct {
     PyObject_HEAD
-    PyObject *index;
     PyObject *start;
     PyObject *step;
     PyObject *len;
@@ -946,7 +945,8 @@ typedef struct {
 static PyObject *
 longrangeiter_len(longrangeiterobject *r, PyObject *no_args)
 {
-    return PyNumber_Subtract(r->len, r->index);
+    Py_INCREF(r->len);
+    return r->len;
 }
 
 static PyObject *
@@ -976,7 +976,7 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored))
 
     /* return the result */
     return Py_BuildValue("N(N)O", _PyEval_GetBuiltinId(&PyId_iter),
-                         range, r->index);
+                         range, Py_None);
 }
 
 static PyObject *
@@ -999,8 +999,18 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
         if (cmp > 0)
             state = r->len;
     }
-    Py_INCREF(state);
-    Py_XSETREF(r->index, state);
+    PyObject *new_len = PyNumber_Subtract(r->len, state);
+    if (new_len == NULL)
+        return NULL;
+    Py_SETREF(r->len, new_len);
+    PyObject *product = PyNumber_Multiply(state, r->step);
+    if (product == NULL)
+        return NULL;
+    PyObject *new_start = PyNumber_Add(r->start, product);
+    Py_DECREF(product);
+    if (new_start == NULL)
+        return NULL;
+    Py_SETREF(r->start, new_start);
     Py_RETURN_NONE;
 }
 
@@ -1017,7 +1027,6 @@ static PyMethodDef longrangeiter_methods[] = {
 static void
 longrangeiter_dealloc(longrangeiterobject *r)
 {
-    Py_XDECREF(r->index);
     Py_XDECREF(r->start);
     Py_XDECREF(r->step);
     Py_XDECREF(r->len);
@@ -1027,29 +1036,21 @@ longrangeiter_dealloc(longrangeiterobject *r)
 static PyObject *
 longrangeiter_next(longrangeiterobject *r)
 {
-    PyObject *product, *new_index, *result;
-    if (PyObject_RichCompareBool(r->index, r->len, Py_LT) != 1)
+    if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1)
         return NULL;
 
-    new_index = PyNumber_Add(r->index, _PyLong_GetOne());
-    if (!new_index)
+    PyObject *new_start = PyNumber_Add(r->start, r->step);
+    if (new_start == NULL) {
         return NULL;
-
-    product = PyNumber_Multiply(r->index, r->step);
-    if (!product) {
-        Py_DECREF(new_index);
-        return NULL;
-    }
-
-    result = PyNumber_Add(r->start, product);
-    Py_DECREF(product);
-    if (result) {
-        Py_SETREF(r->index, new_index);
     }
-    else {
-        Py_DECREF(new_index);
+    PyObject *new_len = PyNumber_Subtract(r->len, _PyLong_GetOne());
+    if (new_len == NULL) {
+        Py_DECREF(new_start);
+        return NULL;
     }
-
+    PyObject *result = r->start;
+    r->start = new_start;
+    Py_SETREF(r->len, new_len);
     return result;
 }
 
@@ -1128,11 +1129,9 @@ range_iter(PyObject *seq)
     it->start = r->start;
     it->step = r->step;
     it->len = r->length;
-    it->index = _PyLong_GetZero();
     Py_INCREF(it->start);
     Py_INCREF(it->step);
     Py_INCREF(it->len);
-    Py_INCREF(it->index);
     return (PyObject *)it;
 }
 
@@ -1210,7 +1209,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored))
     it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);
     if (it == NULL)
         return NULL;
-    it->index = it->start = it->step = NULL;
+    it->start = it->step = NULL;
 
     /* start + (len - 1) * step */
     it->len = range->length;
@@ -1235,8 +1234,6 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored))
     if (!it->step)
         goto create_failure;
 
-    it->index = _PyLong_GetZero();
-    Py_INCREF(it->index);
     return (PyObject *)it;
 
 create_failure:

From 1cd01387ed050ecd71fee1196bb66a93d796abdb Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Fri, 27 Aug 2021 06:35:33 +0300
Subject: [PATCH 2/8] Fix sizeof test.

---
 Lib/test/test_sys.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index dba4928ec261ac..d99190ca96b1c5 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1336,7 +1336,8 @@ def delx(self): del self.__x
         # PyCapsule
         # XXX
         # rangeiterator
-        check(iter(range(1)), size('4l'))
+        check(iter(range(1)), size('3l'))
+        check(iter(range(2**65)), size('3P'))
         # reverse
         check(reversed(''), size('nP'))
         # range

From 3133f664f1cd7c04ae60c7ca51b6ea3224024ac9 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Sun, 29 Aug 2021 15:23:51 +0300
Subject: [PATCH 3/8] Add explicit tests for __setstate__.

---
 Lib/test/test_range.py | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py
index c94dba799adedc..177890b0f7e728 100644
--- a/Lib/test/test_range.py
+++ b/Lib/test/test_range.py
@@ -450,6 +450,20 @@ def test_iterator_unpickle_compat(self):
             it = pickle.loads(t)
             self.assertEqual(list(it), [14, 16, 18])
 
+    def test_iterator_setstate(self):
+        it = iter(range(10, 20, 2))
+        it.__setstate__(2)
+        self.assertEqual(list(it), [14, 16, 18])
+        it = reversed(range(10, 20, 2))
+        it.__setstate__(3)
+        self.assertEqual(list(it), [12, 10])
+        it = iter(range(-2**65, 20, 2))
+        it.__setstate__(2**64 + 7)
+        self.assertEqual(list(it), [14, 16, 18])
+        it = reversed(range(10, 2**65, 2))
+        it.__setstate__(2**64 - 7)
+        self.assertEqual(list(it), [12, 10])
+
     def test_odd_bug(self):
         # This used to raise a "SystemError: NULL result without error"
         # because the range validation step was eating the exception

From 7a42474da799a5b75ac661247c817771c18ee5a3 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Sun, 29 Aug 2021 15:55:28 +0300
Subject: [PATCH 4/8] Add a NEWS entry.

---
 .../Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst

diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst b/Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst
new file mode 100644
index 00000000000000..481ab53e4f5197
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst	
@@ -0,0 +1,3 @@
+Optimize the :class:`range` object iterator. It is now smaller, faster
+iteration of ranges containing large numbers. Smaller pickles, faster
+unpickling.

From 177da8c11e973bc6649d0c3d5ee06da54d020804 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Sun, 5 Sep 2021 14:26:29 +0300
Subject: [PATCH 5/8] Keep stop instead of len.

---
 Objects/rangeobject.c | 156 ++++++++++++++++++++----------------------
 1 file changed, 73 insertions(+), 83 deletions(-)

diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 03e16aec4c653a..0c8d156fee37c6 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -767,17 +767,44 @@ PyTypeObject PyRange_Type = {
 typedef struct {
         PyObject_HEAD
         long    start;
+        long    stop;
         long    step;
-        long    len;
 } rangeiterobject;
 
+/* Return number of items in range (lo, hi, step).  step != 0
+ * required.  The result always fits in an unsigned long.
+ */
+static unsigned long
+get_len_of_range(long lo, long hi, long step)
+{
+    /* -------------------------------------------------------------
+    If step > 0 and lo >= hi, or step < 0 and lo <= hi, the range is empty.
+    Else for step > 0, if n values are in the range, the last one is
+    lo + (n-1)*step, which must be <= hi-1.  Rearranging,
+    n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
+    the proper value.  Since lo < hi in this case, hi-lo-1 >= 0, so
+    the RHS is non-negative and so truncation is the same as the
+    floor.  Letting M be the largest positive long, the worst case
+    for the RHS numerator is hi=M, lo=-M-1, and then
+    hi-lo-1 = M-(-M-1)-1 = 2*M.  Therefore unsigned long has enough
+    precision to compute the RHS exactly.  The analysis for step < 0
+    is similar.
+    ---------------------------------------------------------------*/
+    assert(step != 0);
+    if (step > 0 && lo < hi)
+        return 1UL + (hi - 1UL - lo) / step;
+    else if (step < 0 && lo > hi)
+        return 1UL + (lo - 1UL - hi) / (0UL - step);
+    else
+        return 0UL;
+}
+
 static PyObject *
 rangeiter_next(rangeiterobject *r)
 {
-    if (r->len > 0) {
+    if (r->step > 0 ? r->start < r->stop : r->start > r->stop) {
         long result = r->start;
         r->start += r->step;
-        r->len--;
         return PyLong_FromLong(result);
     }
     return NULL;
@@ -786,7 +813,8 @@ rangeiter_next(rangeiterobject *r)
 static PyObject *
 rangeiter_len(rangeiterobject *r, PyObject *Py_UNUSED(ignored))
 {
-    return PyLong_FromLong(r->len);
+    unsigned long ulen = get_len_of_range(r->start, r->stop, r->step);
+    return PyLong_FromUnsignedLong(ulen);
 }
 
 PyDoc_STRVAR(length_hint_doc,
@@ -802,7 +830,7 @@ rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored))
     start = PyLong_FromLong(r->start);
     if (start == NULL)
         goto err;
-    stop = PyLong_FromLong(r->start + r->len * r->step);
+    stop = PyLong_FromLong(r->stop);
     if (stop == NULL)
         goto err;
     step = PyLong_FromLong(r->step);
@@ -831,10 +859,12 @@ rangeiter_setstate(rangeiterobject *r, PyObject *state)
     /* silently clip the index value */
     if (index < 0)
         index = 0;
-    else if (index > r->len)
-        index = r->len; /* exhausted iterator */
+    else {
+        unsigned long ulen = get_len_of_range(r->start, r->stop, r->step);
+        if ((unsigned long)index > ulen)
+            index = (long)ulen; /* exhausted iterator */
+    }
     r->start += index * r->step;
-    r->len -= index;
     Py_RETURN_NONE;
 }
 
@@ -884,34 +914,6 @@ PyTypeObject PyRangeIter_Type = {
         0,                                      /* tp_members */
 };
 
-/* Return number of items in range (lo, hi, step).  step != 0
- * required.  The result always fits in an unsigned long.
- */
-static unsigned long
-get_len_of_range(long lo, long hi, long step)
-{
-    /* -------------------------------------------------------------
-    If step > 0 and lo >= hi, or step < 0 and lo <= hi, the range is empty.
-    Else for step > 0, if n values are in the range, the last one is
-    lo + (n-1)*step, which must be <= hi-1.  Rearranging,
-    n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
-    the proper value.  Since lo < hi in this case, hi-lo-1 >= 0, so
-    the RHS is non-negative and so truncation is the same as the
-    floor.  Letting M be the largest positive long, the worst case
-    for the RHS numerator is hi=M, lo=-M-1, and then
-    hi-lo-1 = M-(-M-1)-1 = 2*M.  Therefore unsigned long has enough
-    precision to compute the RHS exactly.  The analysis for step < 0
-    is similar.
-    ---------------------------------------------------------------*/
-    assert(step != 0);
-    if (step > 0 && lo < hi)
-        return 1UL + (hi - 1UL - lo) / step;
-    else if (step < 0 && lo > hi)
-        return 1UL + (lo - 1UL - hi) / (0UL - step);
-    else
-        return 0UL;
-}
-
 /* Initialize a rangeiter object.  If the length of the rangeiter object
    is not representable as a C long, OverflowError is raised. */
 
@@ -922,46 +924,38 @@ fast_range_iter(long start, long stop, long step, long len)
     if (it == NULL)
         return NULL;
     it->start = start;
+    it->stop = stop;
     it->step = step;
-    it->len = len;
     return (PyObject *)it;
 }
 
 typedef struct {
     PyObject_HEAD
     PyObject *start;
+    PyObject *stop;
     PyObject *step;
-    PyObject *len;
 } longrangeiterobject;
 
 static PyObject *
 longrangeiter_len(longrangeiterobject *r, PyObject *no_args)
 {
-    Py_INCREF(r->len);
-    return r->len;
+    return compute_range_length(r->start, r->stop, r->step);
 }
 
 static PyObject *
 longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored))
 {
-    PyObject *product, *stop=NULL;
     PyObject *range;
 
-    /* create a range object for pickling.  Must calculate the "stop" value */
-    product = PyNumber_Multiply(r->len, r->step);
-    if (product == NULL)
-        return NULL;
-    stop = PyNumber_Add(r->start, product);
-    Py_DECREF(product);
-    if (stop ==  NULL)
-        return NULL;
+    /* create a range object for pickling. */
     Py_INCREF(r->start);
+    Py_INCREF(r->stop);
     Py_INCREF(r->step);
     range =  (PyObject*)make_range_object(&PyRange_Type,
-                               r->start, stop, r->step);
+                               r->start, r->stop, r->step);
     if (range == NULL) {
         Py_DECREF(r->start);
-        Py_DECREF(stop);
+        Py_DECREF(r->stop);
         Py_DECREF(r->step);
         return NULL;
     }
@@ -978,23 +972,28 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
     int cmp;
 
     /* clip the value */
-    cmp = PyObject_RichCompareBool(state, zero, Py_LT);
+    cmp = PyObject_RichCompareBool(state, zero, Py_LE);
     if (cmp < 0)
         return NULL;
     if (cmp > 0) {
-        state = zero;
+        Py_RETURN_NONE;
     }
-    else {
-        cmp = PyObject_RichCompareBool(r->len, state, Py_LT);
-        if (cmp < 0)
-            return NULL;
-        if (cmp > 0)
-            state = r->len;
+    PyObject *length = compute_range_length(r->start, r->stop, r->step);
+    if (length == NULL) {
+        return NULL;
     }
-    PyObject *new_len = PyNumber_Subtract(r->len, state);
-    if (new_len == NULL)
+    cmp = PyObject_RichCompareBool(length, state, Py_LE);
+    if (cmp < 0) {
+        Py_DECREF(length);
         return NULL;
-    Py_SETREF(r->len, new_len);
+    }
+    if (cmp > 0) {
+        state = length;
+    }
+    else {
+        Py_INCREF(state);
+        Py_DECREF(length);
+    }
     PyObject *product = PyNumber_Multiply(state, r->step);
     if (product == NULL)
         return NULL;
@@ -1020,29 +1019,24 @@ static void
 longrangeiter_dealloc(longrangeiterobject *r)
 {
     Py_XDECREF(r->start);
+    Py_XDECREF(r->stop);
     Py_XDECREF(r->step);
-    Py_XDECREF(r->len);
     PyObject_Free(r);
 }
 
 static PyObject *
 longrangeiter_next(longrangeiterobject *r)
 {
-    if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1)
+    int s = _PyLong_Sign(r->step);
+    if (PyObject_RichCompareBool(r->start, r->stop, s > 0 ? Py_LT : Py_GT) != 1)
         return NULL;
 
     PyObject *new_start = PyNumber_Add(r->start, r->step);
     if (new_start == NULL) {
         return NULL;
     }
-    PyObject *new_len = PyNumber_Subtract(r->len, _PyLong_GetOne());
-    if (new_len == NULL) {
-        Py_DECREF(new_start);
-        return NULL;
-    }
     PyObject *result = r->start;
     r->start = new_start;
-    Py_SETREF(r->len, new_len);
     return result;
 }
 
@@ -1129,11 +1123,11 @@ range_iter(PyObject *seq)
         return NULL;
 
     it->start = r->start;
+    it->stop = r->stop;
     it->step = r->step;
-    it->len = r->length;
     Py_INCREF(it->start);
+    Py_INCREF(it->stop);
     Py_INCREF(it->step);
-    Py_INCREF(it->len);
     return (PyObject *)it;
 }
 
@@ -1142,7 +1136,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored))
 {
     rangeobject *range = (rangeobject*) seq;
     longrangeiterobject *it;
-    PyObject *sum, *diff, *product;
+    PyObject *product;
     long lstart, lstop, lstep, new_start, new_stop;
     unsigned long ulen;
 
@@ -1213,22 +1207,18 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored))
         return NULL;
     it->start = it->step = NULL;
 
-    /* start + (len - 1) * step */
-    it->len = range->length;
-    Py_INCREF(it->len);
-
-    diff = PyNumber_Subtract(it->len, _PyLong_GetOne());
-    if (!diff)
+    /* new_stop = start - step */
+    it->stop = PyNumber_Subtract(range->start, range->step);
+    if (!it->stop)
         goto create_failure;
 
-    product = PyNumber_Multiply(diff, range->step);
-    Py_DECREF(diff);
+    /* new_start = new_stop + len * step */
+    product = PyNumber_Multiply(range->length, range->step);
     if (!product)
         goto create_failure;
 
-    sum = PyNumber_Add(range->start, product);
+    it->start = PyNumber_Add(it->stop, product);
     Py_DECREF(product);
-    it->start = sum;
     if (!it->start)
         goto create_failure;
 

From dced0e69e4b703ae9706a7084f082cef7fc46aee Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Fri, 10 Sep 2021 19:33:40 +0300
Subject: [PATCH 6/8] Fix refleaks.

---
 Objects/rangeobject.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 0c8d156fee37c6..29d40167dd79f2 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -990,13 +990,11 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
     if (cmp > 0) {
         state = length;
     }
-    else {
-        Py_INCREF(state);
-        Py_DECREF(length);
-    }
     PyObject *product = PyNumber_Multiply(state, r->step);
-    if (product == NULL)
+    Py_DECREF(length);
+    if (product == NULL) {
         return NULL;
+    }
     PyObject *new_start = PyNumber_Add(r->start, product);
     Py_DECREF(product);
     if (new_start == NULL)

From 189ca23ec32e674016073c453c75c44bd54a8bd9 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Sun, 27 Nov 2022 17:30:24 +0200
Subject: [PATCH 7/8] Unify code with range-iter.

---
 Objects/rangeobject.c      | 2 +-
 Python/bytecodes.c         | 2 +-
 Python/generated_cases.c.h | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 523d99ae47229a..6d98c080bd75d2 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -786,7 +786,7 @@ rangeiter_next(_PyRangeIterObject *r)
 {
     if (r->step > 0 ? r->start < r->stop : r->start > r->stop) {
         long result = r->start;
-        r->start += r->step;
+        r->start = result + r->step;
         return PyLong_FromLong(result);
     }
     return NULL;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 1c1e1ad879019e..15952aad2f7c2c 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2627,7 +2627,7 @@ dummy_func(
             }
             else {
                 long value = r->start;
-                r->start += r->step;
+                r->start = value + r->step;
                 if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) {
                     goto error;
                 }
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index c766dad9e25b13..e86975fbc93e3a 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2645,7 +2645,7 @@
             }
             else {
                 long value = r->start;
-                r->start += r->step;
+                r->start = value + r->step;
                 if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) {
                     goto error;
                 }

From d3159110639c8b0468b84d9b58955ce751681310 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Tue, 29 Nov 2022 09:14:21 +0200
Subject: [PATCH 8/8] Microoptimize small range iteration.

---
 Objects/rangeobject.c      | 6 +++---
 Python/bytecodes.c         | 6 +++---
 Python/generated_cases.c.h | 6 +++---
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 6d98c080bd75d2..fe2998a61c0a7e 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -784,9 +784,9 @@ get_len_of_range(long lo, long hi, long step)
 static PyObject *
 rangeiter_next(_PyRangeIterObject *r)
 {
-    if (r->step > 0 ? r->start < r->stop : r->start > r->stop) {
-        long result = r->start;
-        r->start = result + r->step;
+    long result = r->start, step = r->step;
+    if (step > 0 ? result < r->stop : result > r->stop) {
+        r->start = result + step;
         return PyLong_FromLong(result);
     }
     return NULL;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 15952aad2f7c2c..75cdfdbf7ba0ce 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2620,14 +2620,14 @@ dummy_func(
             STAT_INC(FOR_ITER, hit);
             _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER];
             assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST);
-            if (r->step > 0 ? r->start >= r->stop : r->start <= r->stop) {
+            long value = r->start, step = r->step;
+            if (step > 0 ? value >= r->stop : value <= r->stop) {
                 STACK_SHRINK(1);
                 Py_DECREF(r);
                 JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
             }
             else {
-                long value = r->start;
-                r->start = value + r->step;
+                r->start = value + step;
                 if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) {
                     goto error;
                 }
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index e86975fbc93e3a..d92b42e5253a3a 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2638,14 +2638,14 @@
             STAT_INC(FOR_ITER, hit);
             _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER];
             assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST);
-            if (r->step > 0 ? r->start >= r->stop : r->start <= r->stop) {
+            long value = r->start, step = r->step;
+            if (step > 0 ? value >= r->stop : value <= r->stop) {
                 STACK_SHRINK(1);
                 Py_DECREF(r);
                 JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
             }
             else {
-                long value = r->start;
-                r->start = value + r->step;
+                r->start = value + step;
                 if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) {
                     goto error;
                 }