]> pd.if.org Git - pdclib/blob - functions/stdlib/qsort.c
Intermediate work, checked in for safekeeping as I pick up working on this again.
[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: This is platform-dependent */
38 #define STACKSIZE 40
39
40 void qsort( void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) )
41 {
42     char * i;
43     char * j;
44     _PDCLIB_ptrdiff_t thresh  = T * size;
45     char * base_              = (char *)base;
46     char * limit              = base_ + nmemb * size;
47     PREPARE_STACK;
48
49     for ( ;; )
50     {
51         if ( limit - base_ > thresh ) /* QSort for more than T elements. */
52         {
53             /* We work from second to last - first will be pivot element. */
54             i = base_ + size;
55             j = limit - size;
56             /* We swap first with middle element, then sort that with second
57                and last element so that eventually first element is the median
58                of the three - avoiding pathological pivots.
59                TODO: Instead of middle element, chose one randomly.
60             */
61             memswp( ( ( ( (size_t)( limit - base_ ) ) / size ) / 2 ) * size + base_, base_, size );
62             if ( compar( i, j ) > 0 ) memswp( i, j, size );
63             if ( compar( base_, j ) > 0 ) memswp( base_, j, size );
64             if ( compar( i, base_ ) > 0 ) memswp( i, base_, size );
65             /* Now we have the median for pivot element, entering main Quicksort. */
66             for ( ;; )
67             {
68                 do
69                 {
70                     /* move i right until *i >= pivot */
71                     i += size;
72                 } while ( compar( i, base_ ) < 0 );
73                 do
74                 {
75                     /* move j left until *j <= pivot */
76                     j -= size;
77                 } while ( compar( j, base_ ) > 0 );
78                 if ( i > j )
79                 {
80                     /* break loop if pointers crossed */
81                     break;
82                 }
83                 /* else swap elements, keep scanning */
84                 memswp( i, j, size );
85             }
86             /* move pivot into correct place */
87             memswp( base_, j, size );
88             /* larger subfile base / limit to stack, sort smaller */
89             if ( j - base_ > limit - i )
90             {
91                 /* left is larger */
92                 PUSH( base_, j );
93                 base_ = i;
94             }
95             else
96             {
97                 /* right is larger */
98                 PUSH( i, limit );
99                 limit = j;
100             }
101         }
102         else /* insertion sort for less than T elements              */
103         {
104             for ( j = base_, i = j + size; i < limit; j = i, i += size )
105             {
106                 for ( ; compar( j, j + size ) > 0; j -= size )
107                 {
108                     memswp( j, j + size, size );
109                     if ( j == base_ )
110                     {
111                         break;
112                     }
113                 }
114             }
115             if ( stackptr != stack )           /* if any entries on stack  */
116             {
117                 POP( base_, limit );
118             }
119             else                       /* else stack empty, done   */
120             {
121                 break;
122             }
123         }
124     }
125 }
126
127 #endif
128
129 #ifdef TEST
130 #include <_PDCLIB_test.h>
131 #include <string.h>
132 #include <limits.h>
133
134 static int compare( const void * left, const void * right )
135 {
136     return *( (unsigned char *)left ) - *( (unsigned char *)right );
137 }
138
139 int main( void )
140 {
141     char presort[] = { "shreicnyjqpvozxmbt" };
142     char sorted1[] = { "bcehijmnopqrstvxyz" };
143     char sorted2[] = { "bticjqnyozpvreshxm" };
144     char s[19];
145     strcpy( s, presort );
146     qsort( s, 18, 1, compare );
147     TESTCASE( strcmp( s, sorted1 ) == 0 );
148     strcpy( s, presort );
149     qsort( s, 9, 2, compare );
150     TESTCASE( strcmp( s, sorted2 ) == 0 );
151     strcpy( s, presort );
152     qsort( s, 1, 1, compare );
153     TESTCASE( strcmp( s, presort ) == 0 );
154 #if __BSD_VISIBLE
155     puts( "qsort.c: Skipping test #4 for BSD as it goes into endless loop here." );
156 #else
157     qsort( s, 100, 0, compare );
158     TESTCASE( strcmp( s, presort ) == 0 );
159 #endif
160     return TEST_RESULTS;
161 }
162
163 #endif