forked from KolibriOS/kolibrios
361 lines
16 KiB
C
361 lines
16 KiB
C
|
/****************************************************************************
|
||
|
*
|
||
|
* Open Watcom Project
|
||
|
*
|
||
|
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original
|
||
|
* Code as defined in and that are subject to the Sybase Open Watcom
|
||
|
* Public License version 1.0 (the 'License'). You may not use this file
|
||
|
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
|
||
|
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
|
||
|
* provided with the Original Code and Modifications, and is also
|
||
|
* available at www.sybase.com/developer/opensource.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
|
||
|
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
||
|
* NON-INFRINGEMENT. Please see the License for the specific language
|
||
|
* governing rights and limitations under the License.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* Description: Implementation of qsort_s() - bounds-checking qsort().
|
||
|
* Included by qsort_s.c.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
|
||
|
/* Support OS/2 16-bit protected mode - will never get stack overflow */
|
||
|
#define MAXDEPTH (sizeof( long ) * 8)
|
||
|
|
||
|
#define SHELL 3 /* Shell constant used in shell sort */
|
||
|
|
||
|
typedef int WORD;
|
||
|
#define W sizeof( WORD )
|
||
|
|
||
|
/*
|
||
|
swap() is a macro that chooses between an in_line function call and
|
||
|
an exchange macro.
|
||
|
*/
|
||
|
#define exch( a, b, t) ( t = a, a = b, b = t )
|
||
|
#define swap( a, b ) \
|
||
|
swaptype != 0 ? BYTESWAP( a, b, size ) : \
|
||
|
( void ) exch( *( WORD* )( a ), *( WORD* )( b ), t )
|
||
|
|
||
|
/*
|
||
|
Note: The following assembly was timed against several other methods
|
||
|
of doing the same thing. The pragmas here were either fastest on all
|
||
|
machines tested, or fastest on most machines tested. (including an 8088,
|
||
|
386 16mhz, 386 33mhz, and 486 25mhz).
|
||
|
*/
|
||
|
|
||
|
#if defined( __386__ )
|
||
|
/* this is intended for 386 only... */
|
||
|
void inline_swap( char *p, char *q, size_t size );
|
||
|
#pragma aux inline_swap = \
|
||
|
0x06 /* push es */ \
|
||
|
0x1e /* push ds */ \
|
||
|
0x07 /* pop es */ \
|
||
|
0x0f 0xb6 0xd1 /* movzx edx,cl */ \
|
||
|
0xc1 0xe9 0x02 /* shr ecx,02H */ \
|
||
|
0x74 0x0b /* je L1 */ \
|
||
|
0x8b 0x07 /*L2 mov eax,[edi] */ \
|
||
|
0x87 0x06 /* xchg eax,[esi] */ \
|
||
|
0xab /* stosd */ \
|
||
|
0x83 0xc6 0x04 /* add esi,0004H */ \
|
||
|
0x49 /* dec ecx */ \
|
||
|
0x75 0xf5 /* jne L2 */ \
|
||
|
0x80 0xe2 0x03 /*L1 and dl,03H */ \
|
||
|
0x74 0x09 /* je L3 */ \
|
||
|
0x8a 0x07 /*L4 mov al,[edi] */ \
|
||
|
0x86 0x06 /* xchg al,[esi] */ \
|
||
|
0xaa /* stosb */ \
|
||
|
0x46 /* inc esi */ \
|
||
|
0x4a /* dec edx */ \
|
||
|
0x75 0xf7 /* jne L4 */ \
|
||
|
/*L3 */ \
|
||
|
0x07 /* pop es */ \
|
||
|
parm caller [esi] [edi] [ecx] \
|
||
|
value \
|
||
|
modify exact [esi edi ecx eax edx];
|
||
|
#pragma aux byteswap parm [esi] [edi] [ecx] \
|
||
|
modify exact [esi edi ecx eax edx];
|
||
|
static void _WCNEAR byteswap( char *p, char *q, size_t size ) {
|
||
|
inline_swap( p, q, size );
|
||
|
}
|
||
|
|
||
|
#elif defined( M_I86 ) && defined( __BIG_DATA__ )
|
||
|
void inline_swap( char _WCFAR *p, char _WCFAR *q, size_t size );
|
||
|
#pragma aux inline_swap = \
|
||
|
0x1e /* push ds */ \
|
||
|
0x8e 0xda /* mov ds,dx */ \
|
||
|
0xd1 0xe9 /* shr cx,1 */ \
|
||
|
0x74 0x0b /* je L1 */ \
|
||
|
0x26 0x8b 0x05 /*L2 mov ax,es:[di] */ \
|
||
|
0x87 0x04 /* xchg ax,[si] */ \
|
||
|
0xab /* stosw */ \
|
||
|
0x46 /* inc si */ \
|
||
|
0x46 /* inc si */ \
|
||
|
0x49 /* dec cx */ \
|
||
|
0x75 0xf5 /* jne L2 */ \
|
||
|
0x73 0x07 /*L1 jnc L3 */ \
|
||
|
0x8a 0x04 /* mov al,[si] */ \
|
||
|
0x26 0x86 0x05 /* xchg al,es:[di] */ \
|
||
|
0x88 0x04 /* mov [si],al */ \
|
||
|
0x1f /*L3 pop ds */ \
|
||
|
parm caller [dx si] [es di] [cx] \
|
||
|
value \
|
||
|
modify exact [si di cx ax];
|
||
|
#pragma aux byteswap parm [dx si] [es di] [cx] modify exact [si di cx ax];
|
||
|
static void _WCNEAR byteswap( char _WCFAR *p, char _WCFAR *q, size_t size ) {
|
||
|
inline_swap( p, q, size );
|
||
|
}
|
||
|
|
||
|
#elif defined( M_I86 ) && defined( __SMALL_DATA__ )
|
||
|
/* we'll ask for char __far *q to save us writing code to load es */
|
||
|
void inline_swap( char *p, char _WCFAR *q, size_t size );
|
||
|
#pragma aux inline_swap = \
|
||
|
0xd1 0xe9 /* shr cx,1 */ \
|
||
|
0x74 0x0b /* je L1 */ \
|
||
|
0x26 0x8b 0x05 /*L2 mov ax,es:[di] */ \
|
||
|
0x87 0x04 /* xchg ax,[si] */ \
|
||
|
0xab /* stosw */ \
|
||
|
0x46 /* inc si */ \
|
||
|
0x46 /* inc si */ \
|
||
|
0x49 /* dec cx */ \
|
||
|
0x75 0xf5 /* jne L2 */ \
|
||
|
0x73 0x07 /*L1 jnc L3 */ \
|
||
|
0x8a 0x04 /* mov al,[si] */ \
|
||
|
0x26 0x86 0x05 /* xchg al,es:[di] */ \
|
||
|
0x88 0x04 /* mov [si],al */ \
|
||
|
/*L3 */ \
|
||
|
parm caller [si] [es di] [cx] \
|
||
|
value \
|
||
|
modify exact [si di cx ax];
|
||
|
#pragma aux byteswap parm [si] [es di] [cx] modify exact [si di cx ax];
|
||
|
static void _WCNEAR byteswap( char *p, char _WCFAR *q, size_t size ) {
|
||
|
inline_swap( p, q, size );
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
/* this is an optimized version of a simple byteswap */
|
||
|
#define inline_swap BYTESWAP
|
||
|
static void _WCNEAR BYTESWAP( PTRATTR char *p, PTRATTR char *q, size_t size ) {
|
||
|
long dword;
|
||
|
short word;
|
||
|
char byte;
|
||
|
|
||
|
#if 1 /* this is for 32 bit machines */
|
||
|
while( size > 3 ) {
|
||
|
dword = *(PTRATTR long *)p;
|
||
|
*(PTRATTR long *)p = *(PTRATTR long *)q;
|
||
|
*(PTRATTR long *)q = dword;
|
||
|
p += 4;
|
||
|
q += 4;
|
||
|
size -= 4;
|
||
|
}
|
||
|
if( size > 1 ) {
|
||
|
word = *(PTRATTR short *)p;
|
||
|
*(PTRATTR short *)p = *(PTRATTR short *)q;
|
||
|
*(PTRATTR short *)q = word;
|
||
|
p += 2;
|
||
|
q += 2;
|
||
|
size -= 2;
|
||
|
}
|
||
|
#else /* this is for 16 bit machines */
|
||
|
while( size > 1 ) {
|
||
|
word = *(PTRATTR short *)p;
|
||
|
*(PTRATTR short *)p = *(PTRATTR short *)q;
|
||
|
*(PTRATTR short *)q = word;
|
||
|
p += 2;
|
||
|
q += 2;
|
||
|
size -= 2;
|
||
|
}
|
||
|
#endif
|
||
|
if( size ) {
|
||
|
byte = *p;
|
||
|
*p = *q;
|
||
|
*q = byte;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
FUNCTION_LINKAGE errno_t FUNCTION_NAME( PTRATTR void *in_base,
|
||
|
rsize_t n, rsize_t size,
|
||
|
int (*compar)( const void *, const void *, void * ),
|
||
|
void *context )
|
||
|
/*****************************************************************/
|
||
|
{
|
||
|
PTRATTR char *base = (PTRATTR char*) in_base;
|
||
|
PTRATTR char *p1;
|
||
|
PTRATTR char *p2;
|
||
|
PTRATTR char *pa;
|
||
|
PTRATTR char *pb;
|
||
|
PTRATTR char *pc;
|
||
|
PTRATTR char *pd;
|
||
|
PTRATTR char *pn;
|
||
|
PTRATTR char *pv;
|
||
|
PTRATTR char *mid;
|
||
|
WORD v; /* used in pivot initialization */
|
||
|
WORD t; /* used in exch() macro */
|
||
|
int comparison, swaptype, shell;
|
||
|
size_t count, r, s;
|
||
|
unsigned sp;
|
||
|
auto char *base_stack[MAXDEPTH];
|
||
|
unsigned n_stack[MAXDEPTH];
|
||
|
qcomp *cmp = (qcomp*)compar;
|
||
|
errno_t rc = -1;
|
||
|
|
||
|
/* runtime-constraints */
|
||
|
// n <= RSIZE_MAX
|
||
|
// size <= RSIZE_MAX
|
||
|
// if n > 0 then in_base, compar not NULL
|
||
|
if( __check_constraint_maxsize( n ) &&
|
||
|
__check_constraint_maxsize( size ) &&
|
||
|
((n == 0) || __check_constraint_nullptr( in_base ) &&
|
||
|
__check_constraint_nullptr( compar )) ) {
|
||
|
|
||
|
if( n == 0 ) { /* empty array - nothing to do */
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Initialization of the swaptype variable, which determines which
|
||
|
type of swapping should be performed when swap() is called.
|
||
|
0 for single-word swaps, 1 for general swapping by words, and
|
||
|
2 for swapping by bytes. W (it's a macro) = sizeof(WORD).
|
||
|
*/
|
||
|
swaptype = ( ( base - (char *)0 ) | size ) % W ? 2 : size > W ? 1 : 0;
|
||
|
sp = 0;
|
||
|
for( ;; ) {
|
||
|
while( n > 1 ) {
|
||
|
if( n < 16 ) { /* 2-shell sort on smallest arrays */
|
||
|
for( shell = (size * SHELL) ;
|
||
|
shell > 0 ;
|
||
|
shell -= ((SHELL-1) * size) ) {
|
||
|
p1 = base + shell;
|
||
|
for( ; p1 < base + n * size; p1 += shell ) {
|
||
|
for( p2 = p1;
|
||
|
p2 > base && cmp( p2 - shell, p2, context ) > 0;
|
||
|
p2 -= shell ) {
|
||
|
swap( p2, p2 - shell );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
} else { /* n >= 16 */
|
||
|
/* Small array (15 < n < 30), mid element */
|
||
|
mid = base + (n >> 1) * size;
|
||
|
if( n > 29 ) {
|
||
|
p1 = base;
|
||
|
p2 = base + ( n - 1 ) * size;
|
||
|
if( n > 42 ) { /* Big array, pseudomedian of 9 */
|
||
|
s = (n >> 3) * size;
|
||
|
p1 = MED3( p1, p1 + s, p1 + (s << 1), cmp, context );
|
||
|
mid = MED3( mid - s, mid, mid + s, cmp, context );
|
||
|
p2 = MED3( p2 - (s << 1), p2 - s, p2, cmp, context );
|
||
|
}
|
||
|
/* Mid-size (29 < n < 43), med of 3 */
|
||
|
mid = MED3( p1, mid, p2, cmp, context );
|
||
|
}
|
||
|
/*
|
||
|
The following sets up the pivot (pv) for partitioning.
|
||
|
It's better to store the pivot value out of line
|
||
|
instead of swapping it to base. However, it's
|
||
|
inconvenient in C unless the element size is fixed.
|
||
|
So, only the important special case of word-size
|
||
|
objects has utilized it.
|
||
|
*/
|
||
|
if( swaptype != 0 ) { /* Not word-size objects */
|
||
|
pv = base;
|
||
|
swap( pv, mid );
|
||
|
} else { /* Storing the pivot out of line (at v) */
|
||
|
pv = (char *)&v;
|
||
|
v = *(WORD *)mid;
|
||
|
}
|
||
|
|
||
|
pa = pb = base;
|
||
|
pc = pd = base + ( n - 1 ) * size;
|
||
|
count = n;
|
||
|
/*
|
||
|
count keeps track of how many entries we have
|
||
|
examined. Once we have looked at all the entries
|
||
|
then we know that the partitioning is complete.
|
||
|
We use count to terminate the looping, rather than
|
||
|
a pointer comparison, to handle 16bit pointer
|
||
|
limitations that may lead pb or pc to wrap.
|
||
|
i.e. pc = 0x0000;
|
||
|
pc -= 0x0004;
|
||
|
pc == 0xfffc;
|
||
|
pc is no longer less that 0x0000;
|
||
|
*/
|
||
|
for( ;; ) {
|
||
|
while( count && (comparison = cmp(pb, pv, context )) <= 0 ) {
|
||
|
if( comparison == 0 ) {
|
||
|
swap( pa, pb );
|
||
|
pa += size;
|
||
|
}
|
||
|
pb += size;
|
||
|
count--;
|
||
|
}
|
||
|
while( count && (comparison = cmp(pc, pv, context )) >= 0 ) {
|
||
|
if( comparison == 0 ) {
|
||
|
swap( pc, pd );
|
||
|
pd -= size;
|
||
|
}
|
||
|
pc -= size;
|
||
|
count--;
|
||
|
}
|
||
|
if( !count ) break;
|
||
|
swap( pb, pc );
|
||
|
pb += size;
|
||
|
count--;
|
||
|
if( !count ) break;
|
||
|
pc -= size;
|
||
|
count--;
|
||
|
}
|
||
|
pn = base + n * size;
|
||
|
s = min( pa - base, pb - pa );
|
||
|
if( s > 0 ) {
|
||
|
inline_swap( base, pb - s, s );
|
||
|
}
|
||
|
s = min( pd - pc, pn - pd - size);
|
||
|
if( s > 0 ) {
|
||
|
inline_swap( pb, pn - s, s );
|
||
|
}
|
||
|
/* Now, base to (pb-pa) needs to be sorted */
|
||
|
/* Also, pn-(pd-pc) needs to be sorted */
|
||
|
/* The middle 'chunk' contains all elements=pivot value */
|
||
|
r = pb - pa;
|
||
|
s = pd - pc;
|
||
|
if( s >= r ) { /* Stack up the larger chunk */
|
||
|
base_stack[sp] = pn - s; /* Stack up base */
|
||
|
n_stack[sp] = s / size; /* Stack up n */
|
||
|
n = r / size; /* Set up n for next 'call' */
|
||
|
/* next base is still base */
|
||
|
} else {
|
||
|
if( r <= size ) break;
|
||
|
base_stack[sp] = base; /* Stack up base */
|
||
|
n_stack[sp] = r / size; /* Stack up n */
|
||
|
base = pn - s; /* Set up base and n for */
|
||
|
n = s / size; /* next 'call' */
|
||
|
}
|
||
|
++sp;
|
||
|
}
|
||
|
}
|
||
|
if( sp == 0 ) break;
|
||
|
--sp;
|
||
|
base = base_stack[sp];
|
||
|
n = n_stack[sp];
|
||
|
}
|
||
|
rc = 0;
|
||
|
}
|
||
|
return( rc );
|
||
|
}
|