]> pd.if.org Git - pdclib/blob - functions/stdlib/qsort.c
Thanks where thanks is due.
[pdclib] / functions / stdlib / qsort.c
1 /* $Id$ */
2
3 /* qsort( void *, size_t, size_t, int(*)( const void *, const void * ) )
4
5    This file is part of the Public Domain C Library (PDCLib).
6    Permission is granted to use, modify, and / or redistribute at will.
7 */
8
9 #include <stdlib.h>
10
11 #ifndef REGTEST
12
13 /* This implementation is taken from Paul Edward's PDPCLIB.
14
15    Original code is credited to Raymond Gardner, Englewood CO.
16    Minor mods are credited to Paul Edwards.
17    Some reformatting and simplification done by Martin Baute.
18    All code is still Public Domain.
19 */
20
21 /* Wrapper for _PDCLIB_memswp protects against multiple argument evaluation. */
22 static inline void memswp( char * i, char * j, size_t size )
23 {
24     _PDCLIB_memswp( i, j, size );
25 }
26
27 /* For small sets, insertion sort is faster than quicksort.
28    T is the threshold below which insertion sort will be used.
29    Must be 3 or larger.
30 */
31 #define T 7
32
33 /* Macros for handling the QSort stack */
34 #define PREPARE_STACK char * stack[STACKSIZE]; char * * stackptr = stack
35 #define PUSH( base, limit ) stackptr[0] = base; stackptr[1] = limit; stackptr += 2
36 #define POP( base, limit ) stackptr -= 2; base = stackptr[0]; limit = stackptr[1]
37 /* TODO: Stack usage is log2( nmemb ) (minus what T shaves off the worst case).
38          Worst-case nmemb is platform dependent and should probably be 
39          configured through _PDCLIB_config.h.
40 */
41 #define STACKSIZE 64
42
43 void qsort( void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) )
44 {
45     char * i;
46     char * j;
47     _PDCLIB_size_t thresh = T * size;
48     char * base_          = (char *)base;
49     char * limit          = base_ + nmemb * size;
50     PREPARE_STACK;
51
52     for ( ;; )
53     {
54         if ( (size_t)( limit - base_ ) > thresh ) /* QSort for more than T elements. */
55         {
56             /* We work from second to last - first will be pivot element. */
57             i = base_ + size;
58             j = limit - size;
59             /* We swap first with middle element, then sort that with second
60                and last element so that eventually first element is the median
61                of the three - avoiding pathological pivots.
62                TODO: Instead of middle element, chose one randomly.
63             */
64             memswp( ( ( ( (size_t)( limit - base_ ) ) / size ) / 2 ) * size + base_, base_, size );
65             if ( compar( i, j ) > 0 ) memswp( i, j, size );
66             if ( compar( base_, j ) > 0 ) memswp( base_, j, size );
67             if ( compar( i, base_ ) > 0 ) memswp( i, base_, size );
68             /* Now we have the median for pivot element, entering main Quicksort. */
69             for ( ;; )
70             {
71                 do
72                 {
73                     /* move i right until *i >= pivot */
74                     i += size;
75                 } while ( compar( i, base_ ) < 0 );
76                 do
77                 {
78                     /* move j left until *j <= pivot */
79                     j -= size;
80                 } while ( compar( j, base_ ) > 0 );
81                 if ( i > j )
82                 {
83                     /* break loop if pointers crossed */
84                     break;
85                 }
86                 /* else swap elements, keep scanning */
87                 memswp( i, j, size );
88             }
89             /* move pivot into correct place */
90             memswp( base_, j, size );
91             /* larger subfile base / limit to stack, sort smaller */
92             if ( j - base_ > limit - i )
93             {
94                 /* left is larger */
95                 PUSH( base_, j );
96                 base_ = i;
97             }
98             else
99             {
100                 /* right is larger */
101                 PUSH( i, limit );
102                 limit = j;
103             }
104         }
105         else /* insertion sort for less than T elements              */
106         {
107             for ( j = base_, i = j + size; i < limit; j = i, i += size )
108             {
109                 for ( ; compar( j, j + size ) > 0; j -= size )
110                 {
111                     memswp( j, j + size, size );
112                     if ( j == base_ )
113                     {
114                         break;
115                     }
116                 }
117             }
118             if ( stackptr != stack )           /* if any entries on stack  */
119             {
120                 POP( base_, limit );
121             }
122             else                       /* else stack empty, done   */
123             {
124                 break;
125             }
126         }
127     }
128 }
129
130 #endif
131
132 #ifdef TEST
133 #include <_PDCLIB_test.h>
134 #include <string.h>
135 #include <limits.h>
136
137 static int compare( const void * left, const void * right )
138 {
139     return *( (unsigned char *)left ) - *( (unsigned char *)right );
140 }
141
142 int main( void )
143 {
144     char presort[] = { "shreicnyjqpvozxmbt" };
145     char sorted1[] = { "bcehijmnopqrstvxyz" };
146     char sorted2[] = { "bticjqnyozpvreshxm" };
147     char s[19];
148     strcpy( s, presort );
149     qsort( s, 18, 1, compare );
150     TESTCASE( strcmp( s, sorted1 ) == 0 );
151     strcpy( s, presort );
152     qsort( s, 9, 2, compare );
153     TESTCASE( strcmp( s, sorted2 ) == 0 );
154     strcpy( s, presort );
155     qsort( s, 1, 1, compare );
156     TESTCASE( strcmp( s, presort ) == 0 );
157 #if __BSD_VISIBLE
158     puts( "qsort.c: Skipping test #4 for BSD as it goes into endless loop here." );
159 #else
160     qsort( s, 100, 0, compare );
161     TESTCASE( strcmp( s, presort ) == 0 );
162 #endif
163     return TEST_RESULTS;
164 }
165
166 #endif