Header And Logo

PostgreSQL
| The world's most advanced open source database.

qsort_arg.c

Go to the documentation of this file.
00001 /*
00002  *  qsort_arg.c: qsort with a passthrough "void *" argument
00003  *
00004  *  Modifications from vanilla NetBSD source:
00005  *    Add do ... while() macro fix
00006  *    Remove __inline, _DIAGASSERTs, __P
00007  *    Remove ill-considered "swap_cnt" switch to insertion sort,
00008  *    in favor of a simple check for presorted input.
00009  *
00010  *  CAUTION: if you change this file, see also qsort.c, gen_qsort_tuple.pl
00011  *
00012  *  src/port/qsort_arg.c
00013  */
00014 
00015 /*  $NetBSD: qsort.c,v 1.13 2003/08/07 16:43:42 agc Exp $   */
00016 
00017 /*-
00018  * Copyright (c) 1992, 1993
00019  *  The Regents of the University of California.  All rights reserved.
00020  *
00021  * Redistribution and use in source and binary forms, with or without
00022  * modification, are permitted provided that the following conditions
00023  * are met:
00024  * 1. Redistributions of source code must retain the above copyright
00025  *    notice, this list of conditions and the following disclaimer.
00026  * 2. Redistributions in binary form must reproduce the above copyright
00027  *    notice, this list of conditions and the following disclaimer in the
00028  *    documentation and/or other materials provided with the distribution.
00029  * 3. Neither the name of the University nor the names of its contributors
00030  *    may be used to endorse or promote products derived from this software
00031  *    without specific prior written permission.
00032  *
00033  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00034  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00035  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00036  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00037  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00038  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00039  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00040  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00041  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00042  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00043  * SUCH DAMAGE.
00044  */
00045 
00046 #include "c.h"
00047 
00048 
00049 static char *med3(char *a, char *b, char *c,
00050      qsort_arg_comparator cmp, void *arg);
00051 static void swapfunc(char *, char *, size_t, int);
00052 
00053 /*
00054  * Qsort routine based on J. L. Bentley and M. D. McIlroy,
00055  * "Engineering a sort function",
00056  * Software--Practice and Experience 23 (1993) 1249-1265.
00057  * We have modified their original by adding a check for already-sorted input,
00058  * which seems to be a win per discussions on pgsql-hackers around 2006-03-21.
00059  */
00060 #define swapcode(TYPE, parmi, parmj, n) \
00061 do {        \
00062     size_t i = (n) / sizeof (TYPE);         \
00063     TYPE *pi = (TYPE *)(void *)(parmi);         \
00064     TYPE *pj = (TYPE *)(void *)(parmj);         \
00065     do {                        \
00066         TYPE    t = *pi;            \
00067         *pi++ = *pj;                \
00068         *pj++ = t;              \
00069         } while (--i > 0);              \
00070 } while (0)
00071 
00072 #define SWAPINIT(a, es) swaptype = ((char *)(a) - (char *)0) % sizeof(long) || \
00073     (es) % sizeof(long) ? 2 : (es) == sizeof(long)? 0 : 1;
00074 
00075 static void
00076 swapfunc(char *a, char *b, size_t n, int swaptype)
00077 {
00078     if (swaptype <= 1)
00079         swapcode(long, a, b, n);
00080     else
00081         swapcode(char, a, b, n);
00082 }
00083 
00084 #define swap(a, b)                      \
00085     if (swaptype == 0) {                    \
00086         long t = *(long *)(void *)(a);          \
00087         *(long *)(void *)(a) = *(long *)(void *)(b);    \
00088         *(long *)(void *)(b) = t;           \
00089     } else                          \
00090         swapfunc(a, b, es, swaptype)
00091 
00092 #define vecswap(a, b, n) if ((n) > 0) swapfunc((a), (b), (size_t)(n), swaptype)
00093 
00094 static char *
00095 med3(char *a, char *b, char *c, qsort_arg_comparator cmp, void *arg)
00096 {
00097     return cmp(a, b, arg) < 0 ?
00098         (cmp(b, c, arg) < 0 ? b : (cmp(a, c, arg) < 0 ? c : a))
00099         : (cmp(b, c, arg) > 0 ? b : (cmp(a, c, arg) < 0 ? a : c));
00100 }
00101 
00102 void
00103 qsort_arg(void *a, size_t n, size_t es, qsort_arg_comparator cmp, void *arg)
00104 {
00105     char       *pa,
00106                *pb,
00107                *pc,
00108                *pd,
00109                *pl,
00110                *pm,
00111                *pn;
00112     int         d,
00113                 r,
00114                 swaptype,
00115                 presorted;
00116 
00117 loop:SWAPINIT(a, es);
00118     if (n < 7)
00119     {
00120         for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
00121             for (pl = pm; pl > (char *) a && cmp(pl - es, pl, arg) > 0;
00122                  pl -= es)
00123                 swap(pl, pl - es);
00124         return;
00125     }
00126     presorted = 1;
00127     for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
00128     {
00129         if (cmp(pm - es, pm, arg) > 0)
00130         {
00131             presorted = 0;
00132             break;
00133         }
00134     }
00135     if (presorted)
00136         return;
00137     pm = (char *) a + (n / 2) * es;
00138     if (n > 7)
00139     {
00140         pl = (char *) a;
00141         pn = (char *) a + (n - 1) * es;
00142         if (n > 40)
00143         {
00144             d = (n / 8) * es;
00145             pl = med3(pl, pl + d, pl + 2 * d, cmp, arg);
00146             pm = med3(pm - d, pm, pm + d, cmp, arg);
00147             pn = med3(pn - 2 * d, pn - d, pn, cmp, arg);
00148         }
00149         pm = med3(pl, pm, pn, cmp, arg);
00150     }
00151     swap(a, pm);
00152     pa = pb = (char *) a + es;
00153     pc = pd = (char *) a + (n - 1) * es;
00154     for (;;)
00155     {
00156         while (pb <= pc && (r = cmp(pb, a, arg)) <= 0)
00157         {
00158             if (r == 0)
00159             {
00160                 swap(pa, pb);
00161                 pa += es;
00162             }
00163             pb += es;
00164         }
00165         while (pb <= pc && (r = cmp(pc, a, arg)) >= 0)
00166         {
00167             if (r == 0)
00168             {
00169                 swap(pc, pd);
00170                 pd -= es;
00171             }
00172             pc -= es;
00173         }
00174         if (pb > pc)
00175             break;
00176         swap(pb, pc);
00177         pb += es;
00178         pc -= es;
00179     }
00180     pn = (char *) a + n * es;
00181     r = Min(pa - (char *) a, pb - pa);
00182     vecswap(a, pb - r, r);
00183     r = Min(pd - pc, pn - pd - es);
00184     vecswap(pb, pn - r, r);
00185     if ((r = pb - pa) > es)
00186         qsort_arg(a, r / es, es, cmp, arg);
00187     if ((r = pd - pc) > es)
00188     {
00189         /* Iterate rather than recurse to save stack space */
00190         a = pn - r;
00191         n = r / es;
00192         goto loop;
00193     }
00194 /*      qsort_arg(pn - r, r / es, es, cmp, arg);*/
00195 }