Skip to content

Commit

Permalink
BUG: Make timsort deal with zero length elements.
Browse files Browse the repository at this point in the history
The elements of arrays of unicode, string, and void type may have zero
length and such types do not need sorting. This fixes a segfault in
timsort due to integer division by zero by checking the element size and
returning immediately when it is zero.
  • Loading branch information
charris committed Feb 7, 2019
1 parent c009e1b commit 5c0c3eb
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 20 deletions.
60 changes: 42 additions & 18 deletions numpy/core/src/npysort/timsort.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,12 @@ timsort_@suff@(void *start, npy_intp num, void *varr)
npy_intp l, n, stack_ptr, minrun;
run stack[TIMSORT_STACK_SIZE];
buffer_@suff@ buffer;

/* Items that have zero size don't make sense to sort */
if (len == 0) {
return 0;
}

buffer.pw = NULL;
buffer.size = 0;
buffer.len = len;
Expand Down Expand Up @@ -1341,12 +1347,11 @@ timsort_@suff@(void *start, npy_intp num, void *varr)
if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

ret = 0;
cleanup:

cleanup:
if (buffer.pw != NULL) {
free(buffer.pw);
}

return ret;
}

Expand Down Expand Up @@ -1696,6 +1701,12 @@ atimsort_@suff@(void *start, npy_intp *tosort, npy_intp num, void *varr)
npy_intp l, n, stack_ptr, minrun;
run stack[TIMSORT_STACK_SIZE];
buffer_intp buffer;

/* Items that have zero size don't make sense to sort */
if (len == 0) {
return 0;
}

buffer.pw = NULL;
buffer.size = 0;
stack_ptr = 0;
Expand All @@ -1719,12 +1730,11 @@ atimsort_@suff@(void *start, npy_intp *tosort, npy_intp num, void *varr)
if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

ret = 0;
cleanup:

cleanup:
if (buffer.pw != NULL) {
free(buffer.pw);
}

return ret;
}

Expand Down Expand Up @@ -2121,46 +2131,54 @@ npy_force_collapse(char *arr, run *stack, npy_intp *stack_ptr,
int
npy_timsort(void *start, npy_intp num, void *varr)
{
size_t len = PyArray_ITEMSIZE(varr);
PyArray_CompareFunc *cmp = PyArray_DESCR(varr)->f->compare;
PyArrayObject *arr = varr;
size_t len = PyArray_ITEMSIZE(arr);
PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
int ret;
npy_intp l, n, stack_ptr, minrun;
run stack[TIMSORT_STACK_SIZE];
buffer_char buffer;

/* Items that have zero size don't make sense to sort */
if (len == 0) {
return 0;
}

buffer.pw = NULL;
buffer.size = 0;
buffer.len = len;
stack_ptr = 0;
minrun = compute_min_run_short(num);

/* used for insertion sort and gallop key */
ret = resize_buffer_char(&buffer, len);

if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

for (l = 0; l < num;) {
n = npy_count_run(start, l, num, minrun, buffer.pw, len, cmp, varr);
n = npy_count_run(start, l, num, minrun, buffer.pw, len, cmp, arr);

/* both s and l are scaled by len */
stack[stack_ptr].s = l;
stack[stack_ptr].l = n;
++stack_ptr;
ret = npy_try_collapse(start, stack, &stack_ptr, &buffer, len, cmp, varr);
ret = npy_try_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr);

if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

l += n;
}

ret = npy_force_collapse(start, stack, &stack_ptr, &buffer, len, cmp, varr);
ret = npy_force_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr);

if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

ret = 0;
cleanup:

cleanup:
if (buffer.pw != NULL) {
free(buffer.pw);
}

return ret;
}

Expand Down Expand Up @@ -2509,42 +2527,48 @@ npy_aforce_collapse(char *arr, npy_intp *tosort, run *stack,
int
npy_atimsort(void *start, npy_intp *tosort, npy_intp num, void *varr)
{
size_t len = PyArray_ITEMSIZE(varr);
PyArray_CompareFunc *cmp = PyArray_DESCR(varr)->f->compare;
PyArrayObject *arr = varr;
size_t len = PyArray_ITEMSIZE(arr);
PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
int ret;
npy_intp l, n, stack_ptr, minrun;
run stack[TIMSORT_STACK_SIZE];
buffer_intp buffer;

/* Items that have zero size don't make sense to sort */
if (len == 0) {
return 0;
}

buffer.pw = NULL;
buffer.size = 0;
stack_ptr = 0;
minrun = compute_min_run_short(num);

for (l = 0; l < num;) {
n = npy_acount_run(start, tosort, l, num, minrun, len, cmp, varr);
n = npy_acount_run(start, tosort, l, num, minrun, len, cmp, arr);
/* both s and l are scaled by len */
stack[stack_ptr].s = l;
stack[stack_ptr].l = n;
++stack_ptr;
ret = npy_atry_collapse(start, tosort, stack, &stack_ptr, &buffer, len, cmp,
varr);
arr);

if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

l += n;
}

ret = npy_aforce_collapse(start, tosort, stack, &stack_ptr, &buffer, len,
cmp, varr);
cmp, arr);

if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }

ret = 0;
cleanup:

cleanup:
if (buffer.pw != NULL) {
free(buffer.pw);
}

return ret;
}
4 changes: 2 additions & 2 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -1395,10 +1395,10 @@ def _test_sort_partition(self, name, kinds, **kwargs):
sort_func(zs, kind=kind, **kwargs)

def test_sort(self):
self._test_sort_partition('sort', kinds='qhm')
self._test_sort_partition('sort', kinds='qhmt')

def test_argsort(self):
self._test_sort_partition('argsort', kinds='qhm')
self._test_sort_partition('argsort', kinds='qhmt')

def test_partition(self):
self._test_sort_partition('partition', kinds=['introselect'], kth=2)
Expand Down

0 comments on commit 5c0c3eb

Please sign in to comment.