Skip to content

Commit 6e99700

Browse files
committed
[MERGE #5670 @kfarnung] Remove invalid optimization from xplat qsort
Merge pull request #5670 from kfarnung:xplatsort If the pivot ends up being the highest number, this optimization concludes that the array is already sorted. Since this algorithm is only used for arrays of length 513 and higher, the likelihood of hitting is basically 100%. Introduced: #2382 Fixes: #5667
2 parents 0f40413 + 80ad90e commit 6e99700

File tree

5 files changed

+111
-7
lines changed

5 files changed

+111
-7
lines changed

lib/Common/DataStructures/QuickSort.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,24 +244,32 @@ namespace JsUtil
244244
const size_t pivot = (nmemb - 1) * size;
245245
// make last element the median(pivot)
246246
CCQ_SWAP(base + pivot, base + ((nmemb / 2) * size), size);
247+
247248
// standard qsort pt. below
248-
for (size_t i = 0; i < pivot; i+= size)
249+
size_t i = 0;
250+
for (; i < nmemb / 2 * size; i+= size)
249251
{
252+
// During the first half, count equal values as below the pivot
250253
if (comparer(context, base + i, base + pivot) <= 0)
251254
{
252255
CCQ_SWAP(base + i, base + (pos * size), size);
253256
pos++;
254257
}
255258
}
256259

257-
// issue the last change
258-
CCQ_SWAP(base + (pos * size), base + pivot, size);
259-
260-
if (pos >= nmemb - 1)
260+
for (; i < pivot; i+= size)
261261
{
262-
return; // looks like it was either all sorted OR nothing to sort
262+
// During the second half, count equal values as above the pivot
263+
if (comparer(context, base + i, base + pivot) < 0)
264+
{
265+
CCQ_SWAP(base + i, base + (pos * size), size);
266+
pos++;
267+
}
263268
}
264269

270+
// issue the last change
271+
CCQ_SWAP(base + (pos * size), base + pivot, size);
272+
265273
Sort(base, pos++, size, comparer, context);
266274
Sort(base + (pos * size), nmemb - pos, size, comparer, context);
267275
}

test/Array/array_qsortr_random.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
function getRandomArray(size)
7+
{
8+
const arr = [];
9+
10+
for (let i = 0; i < size; ++i)
11+
{
12+
arr[i] = Math.random() * 100 | 0;
13+
}
14+
15+
return arr;
16+
}
17+
18+
function testRandomSort(size)
19+
{
20+
const unsorted = getRandomArray(size);
21+
22+
// Copy the array and sort it
23+
const sorted = unsorted.slice();
24+
sorted.sort(function (a, b){ return a - b;});
25+
26+
// Verify that the array is sorted
27+
for (let i = 1; i < size; ++i)
28+
{
29+
// Sort has not completed correctly
30+
if (sorted[i] < sorted[i - 1])
31+
{
32+
WScript.Echo(`Unsorted: ${unsorted}`);
33+
WScript.Echo(`Sorted: ${sorted}`);
34+
throw new Error(`Array is not sorted correctly at index '${i}'`);
35+
}
36+
}
37+
}
38+
39+
function stressTestSort(iterations, size = 1000)
40+
{
41+
for (let i = 0; i < iterations; ++i)
42+
{
43+
testRandomSort(size);
44+
}
45+
}
46+
47+
// Test 1000 random arrays of 1000 elements, print out the failures.
48+
stressTestSort(1000, 1000);
49+
50+
WScript.Echo("PASS");

test/Array/bug_gh5667.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
// Test a bug fix for the xplat qsort implementation
7+
// https://github.com/Microsoft/ChakraCore/issues/5667
8+
function testArray(size)
9+
{
10+
// Create an array with all the same value
11+
const arr = new Array(size);
12+
arr.fill(100);
13+
14+
// Change the second to last value to be smaller
15+
arr[arr.length - 2] = 99;
16+
17+
// Sort the array
18+
arr.sort((a, b) => a - b);
19+
20+
// Verify that the array is sorted
21+
for (let i = 1; i < arr.length; ++i)
22+
{
23+
if (arr[i] < arr[i - 1])
24+
{
25+
// Sort has not completed correctly
26+
throw new Error (`Array is not sorted correctly at index '${i}'`);
27+
}
28+
}
29+
}
30+
31+
testArray(512);
32+
testArray(513);
33+
34+
WScript.Echo("PASS");

test/Array/rlexe.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,4 +775,16 @@
775775
<files>bug16717501.js</files>
776776
</default>
777777
</test>
778+
<test>
779+
<default>
780+
<files>bug_gh5667.js</files>
781+
<tags>exclude_windows</tags>
782+
</default>
783+
</test>
784+
<test>
785+
<default>
786+
<files>test_qsortr_random.js</files>
787+
<tags>exclude_windows</tags>
788+
</default>
789+
</test>
778790
</regress-exe>

test/es6/ES6TypedArrayExtensions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,7 @@ var tests = [
14861486
if (WScript.Platform.OS == "win32") { // Windows
14871487
assert.areEqual([9,8,7,2,10,5,4,3,1,6], getTypedArray(10).sort(sortCallbackMalformed), "%TypedArrayPrototype%.sort basic behavior with a sort callback which returns random values");
14881488
} else { // xplat
1489-
assert.areEqual([2,9,8,7,10,4,1,3,5,6], getTypedArray(10).sort(sortCallbackMalformed), "%TypedArrayPrototype%.sort basic behavior with a sort callback which returns random values");
1489+
assert.areEqual([2,9,10,8,7,5,4,3,6,1], getTypedArray(10).sort(sortCallbackMalformed), "%TypedArrayPrototype%.sort basic behavior with a sort callback which returns random values");
14901490
}
14911491

14921492
assert.throws(function() { sort.call(); }, TypeError, "Calling %TypedArrayPrototype%.sort with no this throws TypeError", "'this' is not a typed array object");

0 commit comments

Comments
 (0)