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