src/stdlib/SDL_qsort.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 15 Feb 2016 03:37:01 -0500
changeset 10070 734b680cba95
parent 10069 ba33b870b45c
child 10085 a8e53dc3c5a1
permissions -rw-r--r--
Another attempt to fix Windows build.

#include "../SDL_internal.h"

#include "SDL_stdinc.h"
#include "SDL_assert.h"

#if defined(HAVE_QSORT)
void
SDL_qsort(void *base, size_t nmemb, size_t size, int (*compare) (const void *, const void *))
{
    qsort(base, nmemb, size, compare);
}
#else

#ifdef REGTEST
#undef REGTEST
#endif

#ifdef TEST
#undef TEST
#endif

#ifndef _PDCLIB_memswp
#define _PDCLIB_memswp( i, j, size ) char tmp; do { tmp = *i; *i++ = *j; *j++ = tmp; } while ( --size );
#endif

#ifndef _PDCLIB_size_t
#define _PDCLIB_size_t size_t
#endif

#ifdef qsort
#undef qsort
#endif
#define qsort SDL_qsort

#define inline SDL_INLINE

/*
This code came from PDCLib, under the public domain. Specifically this:
https://bitbucket.org/pdclib/pdclib/raw/a82b02d0c7d4ed633b97f2a7639d9a10b1c92ec8/functions/stdlib/qsort.c
The _PDCLIB_memswp macro was from
https://bitbucket.org/pdclib/pdclib/src/a82b02d0c7d4ed633b97f2a7639d9a10b1c92ec8/platform/posix/internals/_PDCLIB_config.h?at=default&fileviewer=file-view-default#_PDCLIB_config.h-28

Everything below this comment until the HAVE_QSORT #endif was from PDCLib (minor changes noted inline).
--ryan.
*/

/* $Id$ */

/* 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.
*/

/* I commented this out. --ryan. #include <stdlib.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 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

void qsort( void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) )
{
    char * i;
    char * j;
    _PDCLIB_size_t thresh = T * size;
    char * base_          = (char *)base;
    char * limit          = base_ + nmemb * size;
    PREPARE_STACK;

    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 ) > 0 ) memswp( i, j, size );
            if ( compar( base_, j ) > 0 ) memswp( base_, j, size );
            if ( compar( i, base_ ) > 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_ ) < 0 );
                do
                {
                    /* move j left until *j <= pivot */
                    j -= size;
                } while ( compar( j, base_ ) > 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 ) > 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;
            }
        }
    }
}

#endif

#ifdef TEST
#include <_PDCLIB_test.h>
#include <string.h>
#include <limits.h>

static int compare( const void * left, const void * right )
{
    return *( (unsigned char *)left ) - *( (unsigned char *)right );
}

int main( void )
{
    char presort[] = { "shreicnyjqpvozxmbt" };
    char sorted1[] = { "bcehijmnopqrstvxyz" };
    char sorted2[] = { "bticjqnyozpvreshxm" };
    char s[19];
    strcpy( s, presort );
    qsort( s, 18, 1, compare );
    TESTCASE( strcmp( s, sorted1 ) == 0 );
    strcpy( s, presort );
    qsort( s, 9, 2, compare );
    TESTCASE( strcmp( s, sorted2 ) == 0 );
    strcpy( s, presort );
    qsort( s, 1, 1, compare );
    TESTCASE( strcmp( s, presort ) == 0 );
#if defined(REGTEST) && (__BSD_VISIBLE || __APPLE__)
    puts( "qsort.c: Skipping test #4 for BSD as it goes into endless loop here." );
#else
    qsort( s, 100, 0, compare );
    TESTCASE( strcmp( s, presort ) == 0 );
#endif
    return TEST_RESULTS;
}

#endif

#endif /* HAVE_QSORT */

/* vi: set ts=4 sw=4 expandtab: */