1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey JavaScript engine.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #ifndef Sort_h__
41 : #define Sort_h__
42 :
43 : #include "jstypes.h"
44 :
45 : namespace js {
46 :
47 : namespace detail {
48 :
49 : template<typename T>
50 : JS_ALWAYS_INLINE void
51 13254 : CopyNonEmptyArray(T *dst, const T *src, size_t nelems)
52 : {
53 13254 : JS_ASSERT(nelems != 0);
54 13254 : const T *end = src + nelems;
55 37567 : do {
56 37567 : *dst++ = *src++;
57 : } while (src != end);
58 13254 : }
59 :
60 : /* Helper function for MergeSort. */
61 : template<typename T, typename Comparator>
62 : JS_ALWAYS_INLINE bool
63 12383 : MergeArrayRuns(T *dst, const T *src, size_t run1, size_t run2, Comparator c)
64 : {
65 12383 : JS_ASSERT(run1 >= 1);
66 12383 : JS_ASSERT(run2 >= 1);
67 :
68 : /* Copy runs already in sorted order. */
69 12383 : const T *b = src + run1;
70 : bool lessOrEqual;
71 12383 : if (!c(b[-1], b[0], &lessOrEqual))
72 0 : return false;
73 :
74 12383 : if (!lessOrEqual) {
75 : /* Runs are not already sorted, merge them. */
76 274317 : for (const T *a = src;;) {
77 274317 : if (!c(*a, *b, &lessOrEqual))
78 0 : return false;
79 274317 : if (lessOrEqual) {
80 145303 : *dst++ = *a++;
81 145303 : if (!--run1) {
82 5136 : src = b;
83 5136 : break;
84 : }
85 : } else {
86 129014 : *dst++ = *b++;
87 129014 : if (!--run2) {
88 6243 : src = a;
89 6243 : break;
90 : }
91 : }
92 : }
93 : }
94 12383 : CopyNonEmptyArray(dst, src, run1 + run2);
95 12383 : return true;
96 : }
97 :
98 : } /* namespace detail */
99 :
100 : /*
101 : * Sort the array using the merge sort algorithm. The scratch should point to
102 : * a temporary storage that can hold nelems elements.
103 : *
104 : * The comparator must provide the () operator with the following signature:
105 : *
106 : * bool operator()(const T& a, const T& a, bool *lessOrEqualp);
107 : *
108 : * It should return true on success and set *lessOrEqualp to the result of
109 : * a <= b operation. If it returns false, the sort terminates immediately with
110 : * the false result. In this case the content of the array and scratch is
111 : * arbitrary.
112 : */
113 : template<typename T, typename Comparator>
114 : bool
115 34711 : MergeSort(T *array, size_t nelems, T *scratch, Comparator c)
116 : {
117 34711 : const size_t INS_SORT_LIMIT = 3;
118 :
119 34711 : if (nelems <= 1)
120 1157 : return true;
121 :
122 : /*
123 : * Apply insertion sort to small chunks to reduce the number of merge
124 : * passes needed.
125 : */
126 79473 : for (size_t lo = 0; lo < nelems; lo += INS_SORT_LIMIT) {
127 45937 : size_t hi = lo + INS_SORT_LIMIT;
128 45937 : if (hi >= nelems)
129 33545 : hi = nelems;
130 104057 : for (size_t i = lo + 1; i != hi; i++) {
131 65884 : for (size_t j = i; ;) {
132 : bool lessOrEqual;
133 65884 : if (!c(array[j - 1], array[j], &lessOrEqual))
134 18 : return false;
135 65866 : if (lessOrEqual)
136 32540 : break;
137 33326 : T tmp = array[j - 1];
138 33326 : array[j - 1] = array[j];
139 33326 : array[j] = tmp;
140 33326 : if (--j == lo)
141 25580 : break;
142 : }
143 : }
144 : }
145 :
146 33536 : T *vec1 = array;
147 33536 : T *vec2 = scratch;
148 34826 : for (size_t run = INS_SORT_LIMIT; run < nelems; run *= 2) {
149 13673 : for (size_t lo = 0; lo < nelems; lo += 2 * run) {
150 12682 : size_t hi = lo + run;
151 12682 : if (hi >= nelems) {
152 299 : detail::CopyNonEmptyArray(vec2 + lo, vec1 + lo, nelems - lo);
153 299 : break;
154 : }
155 12383 : size_t run2 = (run <= nelems - hi) ? run : nelems - hi;
156 12383 : if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c))
157 0 : return false;
158 : }
159 1290 : T *swap = vec1;
160 1290 : vec1 = vec2;
161 1290 : vec2 = swap;
162 : }
163 33536 : if (vec1 == scratch)
164 572 : detail::CopyNonEmptyArray(array, scratch, nelems);
165 33536 : return true;
166 : }
167 :
168 : } /* namespace js */
169 :
170 : #endif
|