Skip to content

Commit d178d66

Browse files
committed
keylist disabling as per main list
1 parent f10eb37 commit d178d66

File tree

2 files changed

+91
-45
lines changed

2 files changed

+91
-45
lines changed

Objects/listobject.c

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2893,7 +2893,12 @@ order of two equal elements is maintained).
28932893
If a key function is given, apply it once to each list item and sort them,
28942894
ascending or descending, according to their function values.
28952895
2896+
Alternative to key function is supplying list to keylist argument,
2897+
which will determine sort order and will be modified in place.
2898+
28962899
The reverse flag can be set to sort in descending order.
2900+
2901+
Both key and keylist can not be used at the same time.
28972902
[clinic start generated code]*/
28982903

28992904
static PyObject *
@@ -2911,6 +2916,11 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, PyObject *keylist,
29112916
PyObject *result = NULL; /* guilty until proved innocent */
29122917
Py_ssize_t i;
29132918
PyObject **keys;
2919+
// keylist vars
2920+
PyListObject *self_kl;
2921+
Py_ssize_t keylist_ob_size, keylist_allocated;
2922+
PyObject **keylist_ob_item;
2923+
int keylist_frozen = 0;
29142924

29152925
assert(self != NULL);
29162926
assert(PyList_Check(self));
@@ -2926,12 +2936,6 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, PyObject *keylist,
29262936
"Only one of key and keylist can be provided.");
29272937
return result;
29282938
}
2929-
if (!PyList_Check(keylist)) {
2930-
PyErr_Format(PyExc_TypeError,
2931-
"'%.200s' object is not list",
2932-
Py_TYPE(keylist)->tp_name);
2933-
return result;
2934-
}
29352939
}
29362940

29372941
/* The list is temporarily made empty, so that mutations performed
@@ -2945,36 +2949,24 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, PyObject *keylist,
29452949
Py_SET_SIZE(self, 0);
29462950
FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item, NULL);
29472951
self->allocated = -1; /* any operation will reset it to >= 0 */
2948-
PyObject **keylist_ob_item;
2952+
29492953
if (keyfunc == NULL && keylist == NULL) {
29502954
keys = NULL;
29512955
lo.keys = saved_ob_item;
29522956
lo.values = NULL;
29532957
}
29542958
else {
2955-
if (saved_ob_size < MERGESTATE_TEMP_SIZE/2)
2956-
/* Leverage stack space we allocated but won't otherwise use */
2957-
keys = &ms.temparray[saved_ob_size+1];
2958-
else {
2959-
keys = PyMem_Malloc(sizeof(PyObject *) * saved_ob_size);
2960-
if (keys == NULL) {
2961-
PyErr_NoMemory();
2962-
goto keyfunc_fail;
2963-
}
2964-
}
2965-
2966-
if (keylist != NULL) {
2967-
if (saved_ob_size != Py_SIZE(keylist)) {
2968-
PyErr_SetString(PyExc_ValueError,
2969-
"Lengths of input list and keylist differ.");
2970-
goto keyfunc_fail;
2971-
}
2972-
keylist_ob_item = ((PyListObject *) keylist)->ob_item;
2973-
for (i = 0; i < saved_ob_size; i++) {
2974-
keys[i] = keylist_ob_item[i];
2959+
if (keyfunc != NULL) {
2960+
if (saved_ob_size < MERGESTATE_TEMP_SIZE/2)
2961+
/* Leverage stack space we allocated but won't otherwise use */
2962+
keys = &ms.temparray[saved_ob_size+1];
2963+
else {
2964+
keys = PyMem_Malloc(sizeof(PyObject *) * saved_ob_size);
2965+
if (keys == NULL) {
2966+
PyErr_NoMemory();
2967+
goto keyfunc_fail;
2968+
}
29752969
}
2976-
}
2977-
else {
29782970
for (i = 0; i < saved_ob_size ; i++) {
29792971
keys[i] = PyObject_CallOneArg(keyfunc, saved_ob_item[i]);
29802972
if (keys[i] == NULL) {
@@ -2986,11 +2978,34 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, PyObject *keylist,
29862978
}
29872979
}
29882980
}
2981+
else {
2982+
assert(keylist != NULL);
2983+
if (!PyList_Check(keylist)) {
2984+
PyErr_Format(PyExc_TypeError,
2985+
"'%.200s' object is not a list",
2986+
Py_TYPE(keylist)->tp_name);
2987+
goto keyfunc_fail;
2988+
}
2989+
self_kl = ((PyListObject *) keylist);
2990+
// Disable keylist modifications via same methodology as for main list
2991+
keylist_ob_size = Py_SIZE(self_kl);
2992+
keylist_ob_item = self_kl->ob_item;
2993+
keylist_allocated = self_kl->allocated;
2994+
Py_SET_SIZE(self_kl, 0);
2995+
FT_ATOMIC_STORE_PTR_RELEASE(self_kl->ob_item, NULL);
2996+
self_kl->allocated = -1; /* any operation will reset it to >= 0 */
2997+
2998+
keylist_frozen = 1;
2999+
if (saved_ob_size != keylist_ob_size) {
3000+
PyErr_SetString(PyExc_ValueError,
3001+
"Lengths of input list and keylist differ.");
3002+
goto keylist_fail;
3003+
}
3004+
keys = keylist_ob_item;
3005+
}
29893006
lo.keys = keys;
29903007
lo.values = saved_ob_item;
29913008
}
2992-
2993-
29943009
/* The pre-sort check: here's where we decide which compare function to use.
29953010
* How much optimization is safe? We test for homogeneity with respect to
29963011
* several properties that are expensive to check at compare-time, and
@@ -3148,16 +3163,9 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, PyObject *keylist,
31483163
succeed:
31493164
result = Py_None;
31503165
fail:
3151-
if (keys != NULL) {
3152-
if (keylist != NULL) {
3153-
for (i = 0; i < saved_ob_size ; i++) {
3154-
keylist_ob_item[i] = keys[i];
3155-
}
3156-
}
3157-
else {
3158-
for (i = 0; i < saved_ob_size; i++)
3159-
Py_DECREF(keys[i]);
3160-
}
3166+
if (keyfunc != NULL) {
3167+
for (i = 0; i < saved_ob_size; i++)
3168+
Py_DECREF(keys[i]);
31613169
if (saved_ob_size >= MERGESTATE_TEMP_SIZE/2)
31623170
PyMem_Free(keys);
31633171
}
@@ -3175,6 +3183,40 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, PyObject *keylist,
31753183

31763184
merge_freemem(&ms);
31773185

3186+
keylist_fail:
3187+
if (keylist_frozen) {
3188+
if (self_kl->allocated != -1 && result != NULL) {
3189+
/* The user mucked with the keylist during the sort,
3190+
* and we don't already have another error to report.
3191+
*/
3192+
PyErr_SetString(PyExc_ValueError, "keylist modified during sort");
3193+
result = NULL;
3194+
}
3195+
3196+
if (reverse && saved_ob_size > 1)
3197+
reverse_slice(keylist_ob_item, keylist_ob_item + keylist_ob_size);
3198+
3199+
final_ob_item = self_kl->ob_item;
3200+
i = Py_SIZE(self_kl);
3201+
Py_SET_SIZE(self_kl, keylist_ob_size);
3202+
FT_ATOMIC_STORE_PTR_RELEASE(self_kl->ob_item, keylist_ob_item);
3203+
FT_ATOMIC_STORE_SSIZE_RELAXED(self_kl->allocated, keylist_allocated);
3204+
if (final_ob_item != NULL) {
3205+
/* we cannot use list_clear() for this because it does not
3206+
guarantee that the list is really empty when it returns */
3207+
while (--i >= 0) {
3208+
Py_XDECREF(final_ob_item[i]);
3209+
}
3210+
#ifdef Py_GIL_DISABLED
3211+
ensure_shared_on_resize(self_kl);
3212+
bool use_qsbr = _PyObject_GC_IS_SHARED(self_kl);
3213+
#else
3214+
bool use_qsbr = false;
3215+
#endif
3216+
free_list_items(final_ob_item, use_qsbr);
3217+
}
3218+
}
3219+
31783220
keyfunc_fail:
31793221
final_ob_item = self->ob_item;
31803222
i = Py_SIZE(self);

Python/bltinmodule.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2643,8 +2643,10 @@ sorted as builtin_sorted
26432643
26442644
Return a new list containing all items from the iterable in ascending order.
26452645
2646-
A custom key function can be supplied to customize the sort order, and the
2647-
reverse flag can be set to request the result in descending order.
2646+
A custom key function can be supplied to customize the sort order.
2647+
Alternatively, keylist argument accepts a list on which sort order is based.
2648+
The reverse flag can be set to request the result in descending order.
2649+
Both key and keylist can not be used at the same time.
26482650
[end disabled clinic input]*/
26492651

26502652
PyDoc_STRVAR(builtin_sorted__doc__,
@@ -2653,8 +2655,10 @@ PyDoc_STRVAR(builtin_sorted__doc__,
26532655
"\n"
26542656
"Return a new list containing all items from the iterable in ascending order.\n"
26552657
"\n"
2656-
"A custom key function can be supplied to customize the sort order, and the\n"
2657-
"reverse flag can be set to request the result in descending order.");
2658+
"A custom key function can be supplied to customize the sort order.\n"
2659+
"Alternatively, keylist argument accepts a list on which sort order is based.\n"
2660+
"The reverse flag can be set to request the result in descending order.\n"
2661+
"Both key and keylist can not be used at the same time.");
26582662

26592663
#define BUILTIN_SORTED_METHODDEF \
26602664
{"sorted", _PyCFunction_CAST(builtin_sorted), METH_FASTCALL | METH_KEYWORDS, builtin_sorted__doc__},

0 commit comments

Comments
 (0)