| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -10,7 +10,6 @@ extern "C" { | |
|
|
||
| typedef struct { | ||
| PyObject_HEAD | ||
| long index; | ||
| long start; | ||
| long step; | ||
| long len; | ||
| Expand Down | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -756,18 +756,19 @@ PyTypeObject PyRange_Type = { | |
| static PyObject * | ||
| rangeiter_next(_PyRangeIterObject *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 = result + r->step; | ||
| r->len--; | ||
| return PyLong_FromLong(result); | ||
| } | ||
| return NULL; | ||
| } | ||
|
|
||
| static PyObject * | ||
| rangeiter_len(_PyRangeIterObject *r, PyObject *Py_UNUSED(ignored)) | ||
| { | ||
| return PyLong_FromLong(r->len - r->index); | ||
| return PyLong_FromLong(r->len); | ||
| } | ||
|
|
||
| PyDoc_STRVAR(length_hint_doc, | ||
| Expand All | @@ -794,8 +795,8 @@ rangeiter_reduce(_PyRangeIterObject *r, PyObject *Py_UNUSED(ignored)) | |
| if (range == NULL) | ||
| goto err; | ||
| /* return the result */ | ||
| return Py_BuildValue( | ||
| "N(N)l", _PyEval_GetBuiltin(&_Py_ID(iter)), range, r->index); | ||
| return Py_BuildValue("N(N)O", _PyEval_GetBuiltin(&_Py_ID(iter)), | ||
| range, Py_None); | ||
| err: | ||
| Py_XDECREF(start); | ||
| Py_XDECREF(stop); | ||
| Expand All | @@ -814,7 +815,8 @@ rangeiter_setstate(_PyRangeIterObject *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; | ||
| } | ||
|
|
||
| Expand Down Expand Up | @@ -904,13 +906,11 @@ fast_range_iter(long start, long stop, long step, long len) | |
| it->start = start; | ||
| it->step = step; | ||
| it->len = len; | ||
| it->index = 0; | ||
| return (PyObject *)it; | ||
| } | ||
|
|
||
| typedef struct { | ||
| PyObject_HEAD | ||
| PyObject *index; | ||
| PyObject *start; | ||
| PyObject *step; | ||
| PyObject *len; | ||
| Expand All | @@ -919,7 +919,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 * | ||
| Expand All | @@ -946,8 +947,8 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) | |
| } | ||
|
|
||
| /* return the result */ | ||
| return Py_BuildValue( | ||
| "N(N)O", _PyEval_GetBuiltin(&_Py_ID(iter)), range, r->index); | ||
| return Py_BuildValue("N(N)O", _PyEval_GetBuiltin(&_Py_ID(iter)), | ||
| range, Py_None); | ||
| } | ||
|
|
||
| static PyObject * | ||
| Expand All | @@ -970,7 +971,22 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state) | |
| if (cmp > 0) | ||
| state = r->len; | ||
| } | ||
| Py_XSETREF(r->index, Py_NewRef(state)); | ||
| 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; | ||
| PyObject *new_len = PyNumber_Subtract(r->len, state); | ||
| if (new_len == NULL) { | ||
| Py_DECREF(new_start); | ||
| return NULL; | ||
| } | ||
| PyObject *tmp = r->start; | ||
| r->start = new_start; | ||
| Py_SETREF(r->len, new_len); | ||
| Py_DECREF(tmp); | ||
|
Comment thread
Comment on lines
+986
to
+989
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityI don't see a scenario where we can't just use Py_SETREF(r->len, new_len); Py_SETREF(r->start, new_start); (which would be a little easier to follow). Both new_len and new_start are definitely new references we own, so even if state was a borrowed reference to r->len everything would be all right. I'll leave it up to you though.
Sorry, something went wrong.
erlend-aasland reacted with thumbs up emoji
All reactions
Copy link
Copy Markdown
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityPy_SETREF can only help when you assign a single object attribute, or if the attributes are independent. Two sequential Py_SETREFs can be a sign of a bug. We do not check the type of state. It can be an arbitrary Python object with methods __lt__, __gt__, __mul__ and __rsub__. Therefore we do not control the types of new_len and new_start. Therefore we do not control the types of r->len and r->start. They can have __del__ methods which release the GIL after assignment in Py_SETREF. When the GIL is released, the iterator object can be used in other thread when it is in inconsistent state -- new r->len and old r->start. It never occurs in normal code (state is always an exact int), but if you try hard, you perhaps can reproduce this.
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityAha, that's a scenario I hadn't considered. Maybe the next time I am asked in some kind of Q&A or interview "do you have any regrets" I should mention __del__ methods.
Sorry, something went wrong.
erlend-aasland reacted with laugh emoji
All reactions
|
||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
| Expand All | @@ -987,7 +1003,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); | ||
| Expand All | @@ -997,29 +1012,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; | ||
| } | ||
|
|
||
| Expand Down Expand Up | @@ -1108,7 +1115,6 @@ range_iter(PyObject *seq) | |
| it->start = Py_NewRef(r->start); | ||
| it->step = Py_NewRef(r->step); | ||
| it->len = Py_NewRef(r->length); | ||
| it->index = Py_NewRef(_PyLong_GetZero()); | ||
| return (PyObject *)it; | ||
| } | ||
|
|
||
| Expand Down Expand Up | @@ -1186,7 +1192,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 = Py_NewRef(range->length); | ||
| Expand All | @@ -1210,7 +1216,6 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) | |
| if (!it->step) | ||
| goto create_failure; | ||
|
|
||
| it->index = Py_NewRef(_PyLong_GetZero()); | ||
| return (PyObject *)it; | ||
|
|
||
| create_failure: | ||
| Expand Down | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityI'm not sure we care, but we might -- am I right that this writes pickles that can't be read by 3.11 or before?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityNo, absolutely no. I would never propose such change.
Internals will be different, but the unpickled iterator wil produce the same values.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.