2005-04-17 02:20:36 +04:00
/*
* A fast , small , non - recursive O ( nlog n ) sort for the Linux kernel
*
* Jan 23 2005 Matt Mackall < mpm @ selenic . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
2005-09-10 11:26:59 +04:00
# include <linux/sort.h>
2005-10-31 02:03:48 +03:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
2005-09-10 11:26:59 +04:00
static void u32_swap ( void * a , void * b , int size )
2005-04-17 02:20:36 +04:00
{
u32 t = * ( u32 * ) a ;
* ( u32 * ) a = * ( u32 * ) b ;
* ( u32 * ) b = t ;
}
2005-09-10 11:26:59 +04:00
static void generic_swap ( void * a , void * b , int size )
2005-04-17 02:20:36 +04:00
{
char t ;
do {
t = * ( char * ) a ;
* ( char * ) a + + = * ( char * ) b ;
* ( char * ) b + + = t ;
} while ( - - size > 0 ) ;
}
2007-02-10 12:45:59 +03:00
/**
2005-04-17 02:20:36 +04:00
* sort - sort an array of elements
* @ base : pointer to data to sort
* @ num : number of elements
* @ size : size of each element
* @ cmp : pointer to comparison function
* @ swap : pointer to swap function or NULL
*
* This function does a heapsort on the given array . You may provide a
* swap function optimized to your element type .
*
* Sorting time is O ( n log n ) both on average and worst - case . While
* qsort is about 20 % faster on average , it suffers from exploitable
* O ( n * n ) worst - case behavior and extra memory requirements that make
* it less suitable for kernel use .
*/
void sort ( void * base , size_t num , size_t size ,
int ( * cmp ) ( const void * , const void * ) ,
void ( * swap ) ( void * , void * , int size ) )
{
/* pre-scale counters for performance */
2006-10-03 12:13:49 +04:00
int i = ( num / 2 - 1 ) * size , n = num * size , c , r ;
2005-04-17 02:20:36 +04:00
if ( ! swap )
swap = ( size = = 4 ? u32_swap : generic_swap ) ;
/* heapify */
for ( ; i > = 0 ; i - = size ) {
2006-10-03 12:13:49 +04:00
for ( r = i ; r * 2 + size < n ; r = c ) {
c = r * 2 + size ;
2005-04-17 02:20:36 +04:00
if ( c < n - size & & cmp ( base + c , base + c + size ) < 0 )
c + = size ;
if ( cmp ( base + r , base + c ) > = 0 )
break ;
swap ( base + r , base + c , size ) ;
}
}
/* sort */
2007-10-17 10:27:06 +04:00
for ( i = n - size ; i > 0 ; i - = size ) {
2005-04-17 02:20:36 +04:00
swap ( base , base + i , size ) ;
2006-10-03 12:13:49 +04:00
for ( r = 0 ; r * 2 + size < i ; r = c ) {
c = r * 2 + size ;
2005-04-17 02:20:36 +04:00
if ( c < i - size & & cmp ( base + c , base + c + size ) < 0 )
c + = size ;
if ( cmp ( base + r , base + c ) > = 0 )
break ;
swap ( base + r , base + c , size ) ;
}
}
}
EXPORT_SYMBOL ( sort ) ;
#if 0
/* a simple boot-time regression test */
int cmpint ( const void * a , const void * b )
{
return * ( int * ) a - * ( int * ) b ;
}
static int sort_test ( void )
{
2005-05-06 03:16:19 +04:00
int * a , i , r = 1 ;
2005-04-17 02:20:36 +04:00
a = kmalloc ( 1000 * sizeof ( int ) , GFP_KERNEL ) ;
BUG_ON ( ! a ) ;
printk ( " testing sort() \n " ) ;
for ( i = 0 ; i < 1000 ; i + + ) {
r = ( r * 725861 ) % 6599 ;
a [ i ] = r ;
}
sort ( a , 1000 , sizeof ( int ) , cmpint , NULL ) ;
for ( i = 0 ; i < 999 ; i + + )
if ( a [ i ] > a [ i + 1 ] ) {
printk ( " sort() failed! \n " ) ;
break ;
}
kfree ( a ) ;
return 0 ;
}
module_init ( sort_test ) ;
# endif