xref: /redis-3.2.3/src/pqsort.c (revision 4942145d)
1 /* The following is the NetBSD libc qsort implementation modified in order to
2  * support partial sorting of ranges for Redis.
3  *
4  * Copyright(C) 2009-2010 Salvatore Sanfilippo. All rights reserved.
5  *
6  * The original copyright notice follows. */
7 
8 
9 /*	$NetBSD: qsort.c,v 1.19 2009/01/30 23:38:44 lukem Exp $	*/
10 
11 /*-
12  * Copyright (c) 1992, 1993
13  *	The Regents of the University of California.  All rights reserved.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  * 3. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include <sys/types.h>
41 
42 #include <assert.h>
43 #include <errno.h>
44 #include <stdlib.h>
45 
46 static inline char	*med3 (char *, char *, char *,
47     int (*)(const void *, const void *));
48 static inline void	 swapfunc (char *, char *, size_t, int);
49 
50 #define min(a, b)	(a) < (b) ? a : b
51 
52 /*
53  * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
54  */
55 #define swapcode(TYPE, parmi, parmj, n) { 		\
56 	size_t i = (n) / sizeof (TYPE); 		\
57 	TYPE *pi = (TYPE *)(void *)(parmi); 		\
58 	TYPE *pj = (TYPE *)(void *)(parmj); 		\
59 	do { 						\
60 		TYPE	t = *pi;			\
61 		*pi++ = *pj;				\
62 		*pj++ = t;				\
63         } while (--i > 0);				\
64 }
65 
66 #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
67 	es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
68 
69 static inline void
70 swapfunc(char *a, char *b, size_t n, int swaptype)
71 {
72 
73 	if (swaptype <= 1)
74 		swapcode(long, a, b, n)
75 	else
76 		swapcode(char, a, b, n)
77 }
78 
79 #define swap(a, b)						\
80 	if (swaptype == 0) {					\
81 		long t = *(long *)(void *)(a);			\
82 		*(long *)(void *)(a) = *(long *)(void *)(b);	\
83 		*(long *)(void *)(b) = t;			\
84 	} else							\
85 		swapfunc(a, b, es, swaptype)
86 
87 #define vecswap(a, b, n) if ((n) > 0) swapfunc((a), (b), (size_t)(n), swaptype)
88 
89 static inline char *
90 med3(char *a, char *b, char *c,
91     int (*cmp) (const void *, const void *))
92 {
93 
94 	return cmp(a, b) < 0 ?
95 	       (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
96               :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
97 }
98 
99 static void
100 _pqsort(void *a, size_t n, size_t es,
101     int (*cmp) (const void *, const void *), void *lrange, void *rrange)
102 {
103 	char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
104 	size_t d, r;
105 	int swaptype, swap_cnt, cmp_result;
106 
107 loop:	SWAPINIT(a, es);
108 	swap_cnt = 0;
109 	if (n < 7) {
110 		for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
111 			for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
112 			     pl -= es)
113 				swap(pl, pl - es);
114 		return;
115 	}
116 	pm = (char *) a + (n / 2) * es;
117 	if (n > 7) {
118 		pl = (char *) a;
119 		pn = (char *) a + (n - 1) * es;
120 		if (n > 40) {
121 			d = (n / 8) * es;
122 			pl = med3(pl, pl + d, pl + 2 * d, cmp);
123 			pm = med3(pm - d, pm, pm + d, cmp);
124 			pn = med3(pn - 2 * d, pn - d, pn, cmp);
125 		}
126 		pm = med3(pl, pm, pn, cmp);
127 	}
128 	swap(a, pm);
129 	pa = pb = (char *) a + es;
130 
131 	pc = pd = (char *) a + (n - 1) * es;
132 	for (;;) {
133 		while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) {
134 			if (cmp_result == 0) {
135 				swap_cnt = 1;
136 				swap(pa, pb);
137 				pa += es;
138 			}
139 			pb += es;
140 		}
141 		while (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) {
142 			if (cmp_result == 0) {
143 				swap_cnt = 1;
144 				swap(pc, pd);
145 				pd -= es;
146 			}
147 			pc -= es;
148 		}
149 		if (pb > pc)
150 			break;
151 		swap(pb, pc);
152 		swap_cnt = 1;
153 		pb += es;
154 		pc -= es;
155 	}
156 	if (swap_cnt == 0) {  /* Switch to insertion sort */
157 		for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
158 			for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
159 			     pl -= es)
160 				swap(pl, pl - es);
161 		return;
162 	}
163 
164 	pn = (char *) a + n * es;
165 	r = min(pa - (char *) a, pb - pa);
166 	vecswap(a, pb - r, r);
167 	r = min((size_t)(pd - pc), pn - pd - es);
168 	vecswap(pb, pn - r, r);
169 	if ((r = pb - pa) > es) {
170                 void *_l = a, *_r = ((unsigned char*)a)+r-1;
171                 if (!((lrange < _l && rrange < _l) ||
172                     (lrange > _r && rrange > _r)))
173 		    _pqsort(a, r / es, es, cmp, lrange, rrange);
174         }
175 	if ((r = pd - pc) > es) {
176                 void *_l, *_r;
177 
178 		/* Iterate rather than recurse to save stack space */
179 		a = pn - r;
180 		n = r / es;
181 
182                 _l = a;
183                 _r = ((unsigned char*)a)+r-1;
184                 if (!((lrange < _l && rrange < _l) ||
185                     (lrange > _r && rrange > _r)))
186 		    goto loop;
187 	}
188 /*		qsort(pn - r, r / es, es, cmp);*/
189 }
190 
191 void
192 pqsort(void *a, size_t n, size_t es,
193     int (*cmp) (const void *, const void *), size_t lrange, size_t rrange)
194 {
195     _pqsort(a,n,es,cmp,((unsigned char*)a)+(lrange*es),
196                        ((unsigned char*)a)+((rrange+1)*es)-1);
197 }
198