1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Xinchen Hui <laruence@php.net> |
16 | Sterling Hughes <sterling@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "zend.h"
21 #include "zend_sort.h"
22 #include <limits.h>
23
24 #define QSORT_STACK_SIZE (sizeof(size_t) * CHAR_BIT)
25
zend_qsort(void * base,size_t nmemb,size_t siz,compare_func_t compare,swap_func_t swp)26 ZEND_API void zend_qsort(void *base, size_t nmemb, size_t siz, compare_func_t compare, swap_func_t swp) /* {{{ */
27 {
28 void *begin_stack[QSORT_STACK_SIZE];
29 void *end_stack[QSORT_STACK_SIZE];
30 register char *begin;
31 register char *end;
32 register char *seg1;
33 register char *seg2;
34 register char *seg2p;
35 register int loop;
36 size_t offset;
37
38 begin_stack[0] = (char *) base;
39 end_stack[0] = (char *) base + ((nmemb - 1) * siz);
40
41 for (loop = 0; loop >= 0; --loop) {
42 begin = begin_stack[loop];
43 end = end_stack[loop];
44
45 while (begin < end) {
46 offset = (end - begin) >> Z_L(1);
47 swp(begin, begin + (offset - (offset % siz)));
48
49 seg1 = begin + siz;
50 seg2 = end;
51
52 while (1) {
53 for (; seg1 < seg2 && compare(begin, seg1) > 0;
54 seg1 += siz);
55
56 for (; seg2 >= seg1 && compare(seg2, begin) > 0;
57 seg2 -= siz);
58
59 if (seg1 >= seg2)
60 break;
61
62 swp(seg1, seg2);
63
64 seg1 += siz;
65 seg2 -= siz;
66 }
67
68 swp(begin, seg2);
69
70 seg2p = seg2;
71
72 if ((seg2p - begin) <= (end - seg2p)) {
73 if ((seg2p + siz) < end) {
74 begin_stack[loop] = seg2p + siz;
75 end_stack[loop++] = end;
76 }
77 end = seg2p - siz;
78 }
79 else {
80 if ((seg2p - siz) > begin) {
81 begin_stack[loop] = begin;
82 end_stack[loop++] = seg2p - siz;
83 }
84 begin = seg2p + siz;
85 }
86 }
87 }
88 }
89 /* }}} */
90
zend_sort_2(void * a,void * b,compare_func_t cmp,swap_func_t swp)91 static inline void zend_sort_2(void *a, void *b, compare_func_t cmp, swap_func_t swp) /* {{{ */ {
92 if (cmp(a, b) > 0) {
93 swp(a, b);
94 }
95 }
96 /* }}} */
97
zend_sort_3(void * a,void * b,void * c,compare_func_t cmp,swap_func_t swp)98 static inline void zend_sort_3(void *a, void *b, void *c, compare_func_t cmp, swap_func_t swp) /* {{{ */ {
99 if (!(cmp(a, b) > 0)) {
100 if (!(cmp(b, c) > 0)) {
101 return;
102 }
103 swp(b, c);
104 if (cmp(a, b) > 0) {
105 swp(a, b);
106 }
107 return;
108 }
109 if (!(cmp(c, b) > 0)) {
110 swp(a, c);
111 return;
112 }
113 swp(a, b);
114 if (cmp(b, c) > 0) {
115 swp(b, c);
116 }
117 }
118 /* }}} */
119
zend_sort_4(void * a,void * b,void * c,void * d,compare_func_t cmp,swap_func_t swp)120 static void zend_sort_4(void *a, void *b, void *c, void *d, compare_func_t cmp, swap_func_t swp) /* {{{ */ {
121 zend_sort_3(a, b, c, cmp, swp);
122 if (cmp(c, d) > 0) {
123 swp(c, d);
124 if (cmp(b, c) > 0) {
125 swp(b, c);
126 if (cmp(a, b) > 0) {
127 swp(a, b);
128 }
129 }
130 }
131 }
132 /* }}} */
133
zend_sort_5(void * a,void * b,void * c,void * d,void * e,compare_func_t cmp,swap_func_t swp)134 static void zend_sort_5(void *a, void *b, void *c, void *d, void *e, compare_func_t cmp, swap_func_t swp) /* {{{ */ {
135 zend_sort_4(a, b, c, d, cmp, swp);
136 if (cmp(d, e) > 0) {
137 swp(d, e);
138 if (cmp(c, d) > 0) {
139 swp(c, d);
140 if (cmp(b, c) > 0) {
141 swp(b, c);
142 if (cmp(a, b) > 0) {
143 swp(a, b);
144 }
145 }
146 }
147 }
148 }
149 /* }}} */
150
zend_insert_sort(void * base,size_t nmemb,size_t siz,compare_func_t cmp,swap_func_t swp)151 ZEND_API void zend_insert_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp) /* {{{ */{
152 switch (nmemb) {
153 case 0:
154 case 1:
155 break;
156 case 2:
157 zend_sort_2(base, (char *)base + siz, cmp, swp);
158 break;
159 case 3:
160 zend_sort_3(base, (char *)base + siz, (char *)base + siz + siz, cmp, swp);
161 break;
162 case 4:
163 {
164 size_t siz2 = siz + siz;
165 zend_sort_4(base, (char *)base + siz, (char *)base + siz2, (char *)base + siz + siz2, cmp, swp);
166 }
167 break;
168 case 5:
169 {
170 size_t siz2 = siz + siz;
171 zend_sort_5(base, (char *)base + siz, (char *)base + siz2, (char *)base + siz + siz2, (char *)base + siz2 + siz2, cmp, swp);
172 }
173 break;
174 default:
175 {
176 char *i, *j, *k;
177 char *start = (char *)base;
178 char *end = start + (nmemb * siz);
179 size_t siz2= siz + siz;
180 char *sentry = start + (6 * siz);
181 for (i = start + siz; i < sentry; i += siz) {
182 j = i - siz;
183 if (!(cmp(j, i) > 0)) {
184 continue;
185 }
186 while (j != start) {
187 j -= siz;
188 if (!(cmp(j, i) > 0)) {
189 j += siz;
190 break;
191 }
192 }
193 for (k = i; k > j; k -= siz) {
194 swp(k, k - siz);
195 }
196 }
197 for (i = sentry; i < end; i += siz) {
198 j = i - siz;
199 if (!(cmp(j, i) > 0)) {
200 continue;
201 }
202 do {
203 j -= siz2;
204 if (!(cmp(j, i) > 0)) {
205 j += siz;
206 if (!(cmp(j, i) > 0)) {
207 j += siz;
208 }
209 break;
210 }
211 if (j == start) {
212 break;
213 }
214 if (j == start + siz) {
215 j -= siz;
216 if (cmp(i, j) > 0) {
217 j += siz;
218 }
219 break;
220 }
221 } while (1);
222 for (k = i; k > j; k -= siz) {
223 swp(k, k - siz);
224 }
225 }
226 }
227 break;
228 }
229 }
230 /* }}} */
231
232 /* {{{ ZEND_API void zend_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp)
233 *
234 * Derived from LLVM's libc++ implementation of std::sort.
235 *
236 * ===========================================================================
237 * libc++ License
238 * ===========================================================================
239 *
240 * The libc++ library is dual licensed under both the University of Illinois
241 * "BSD-Like" license and the MIT license. As a user of this code you may
242 * choose to use it under either license. As a contributor, you agree to allow
243 * your code to be used under both.
244 *
245 * Full text of the relevant licenses is included below.
246 *
247 * ===========================================================================
248 *
249 * University of Illinois/NCSA
250 * Open Source License
251 *
252 * Copyright (c) 2009-2012 by the contributors listed at
253 * http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
254 *
255 * All rights reserved.
256 *
257 * Developed by:
258 *
259 * LLVM Team
260 *
261 * University of Illinois at Urbana-Champaign
262 *
263 * http://llvm.org
264 *
265 * Permission is hereby granted, free of charge, to any person obtaining a copy
266 * of this software and associated documentation files (the "Software"), to
267 * deal with the Software without restriction, including without limitation the
268 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
269 * sell copies of the Software, and to permit persons to whom the Software is
270 * furnished to do so, subject to the following conditions:
271 *
272 * * Redistributions of source code must retain the above copyright notice,
273 * this list of conditions and the following disclaimers.
274 *
275 * * Redistributions in binary form must reproduce the above copyright
276 * notice, this list of conditions and the following disclaimers in the
277 * documentation and/or other materials provided with the distribution.
278 *
279 * * Neither the names of the LLVM Team, University of Illinois at
280 * Urbana-Champaign, nor the names of its contributors may be used to
281 * endorse or promote products derived from this Software without
282 * specific prior written permission.
283 *
284 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
285 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
286 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
287 * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
288 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
289 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
290 * WITH THE SOFTWARE.
291 *
292 * ===========================================================================
293 *
294 * Copyright (c) 2009-2012 by the contributors listed at
295 * http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
296 *
297 * Permission is hereby granted, free of charge, to any person obtaining a copy
298 * of this software and associated documentation files (the "Software"), to
299 * deal in the Software without restriction, including without limitation the
300 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
301 * sell copies of the Software, and to permit persons to whom the Software is
302 * furnished to do so, subject to the following conditions:
303 *
304 * The above copyright notice and this permission notice shall be included in
305 * all copies or substantial portions of the Software.
306 *
307 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
308 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
309 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
310 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
311 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
312 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
313 * IN THE SOFTWARE.
314 */
zend_sort(void * base,size_t nmemb,size_t siz,compare_func_t cmp,swap_func_t swp)315 ZEND_API void zend_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp)
316 {
317 while (1) {
318 if (nmemb <= 16) {
319 zend_insert_sort(base, nmemb, siz, cmp, swp);
320 return;
321 } else {
322 char *i, *j;
323 char *start = (char *)base;
324 char *end = start + (nmemb * siz);
325 size_t offset = (nmemb >> Z_L(1));
326 char *pivot = start + (offset * siz);
327
328 if ((nmemb >> Z_L(10))) {
329 size_t delta = (offset >> Z_L(1)) * siz;
330 zend_sort_5(start, start + delta, pivot, pivot + delta, end - siz, cmp, swp);
331 } else {
332 zend_sort_3(start, pivot, end - siz, cmp, swp);
333 }
334 swp(start + siz, pivot);
335 pivot = start + siz;
336 i = pivot + siz;
337 j = end - siz;
338 while (1) {
339 while (cmp(pivot, i) > 0) {
340 i += siz;
341 if (UNEXPECTED(i == j)) {
342 goto done;
343 }
344 }
345 j -= siz;
346 if (UNEXPECTED(j == i)) {
347 goto done;
348 }
349 while (cmp(j, pivot) > 0) {
350 j -= siz;
351 if (UNEXPECTED(j == i)) {
352 goto done;
353 }
354 }
355 swp(i, j);
356 i += siz;
357 if (UNEXPECTED(i == j)) {
358 goto done;
359 }
360 }
361 done:
362 swp(pivot, i - siz);
363 if ((i - siz) - start < end - i) {
364 zend_sort(start, (i - start)/siz - 1, siz, cmp, swp);
365 base = i;
366 nmemb = (end - i)/siz;
367 } else {
368 zend_sort(i, (end - i)/siz, siz, cmp, swp);
369 nmemb = (i - start)/siz - 1;
370 }
371 }
372 }
373 }
374 /* }}} */
375