Drizzled Public API Documentation

mf_qsort.cc
00001 /* Copyright (C) 2000 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 /*
00017   qsort implementation optimized for comparison of pointers
00018   Inspired by the qsort implementations by Douglas C. Schmidt,
00019   and Bentley & McIlroy's "Engineering a Sort Function".
00020 */
00021 
00022 
00023 #include <config.h>
00024 
00025 #include <drizzled/internal/my_sys.h>
00026 #include <drizzled/internal/m_string.h>
00027 
00028 namespace drizzled {
00029 namespace internal {
00030 
00031 /* We need to use qsort with 2 different compare functions */
00032 #ifdef QSORT_EXTRA_CMP_ARGUMENT
00033 #define CMP(A,B) ((*cmp)(cmp_argument,(A),(B)))
00034 #else
00035 #define CMP(A,B) ((*cmp)((A),(B)))
00036 #endif
00037 
00038 #define SWAP(A, B, size,swap_ptrs)      \
00039 do {              \
00040    if (swap_ptrs)         \
00041    {              \
00042      char **a = (char**) (A), **b = (char**) (B);  \
00043      char *tmp = *a; *a++ = *b; *b++ = tmp;   \
00044    }              \
00045    else             \
00046    {              \
00047      char *a = (A), *b = (B);     \
00048      char *end= a+size;       \
00049      do             \
00050      {              \
00051        char tmp = *a; *a++ = *b; *b++ = tmp;    \
00052      } while (a < end);         \
00053    }              \
00054 } while (0)
00055 
00056 /* Put the median in the middle argument */
00057 #define MEDIAN(low, mid, high)        \
00058 {             \
00059     if (CMP(high,low) < 0)        \
00060       SWAP(high, low, size, ptr_cmp);     \
00061     if (CMP(mid, low) < 0)        \
00062       SWAP(mid, low, size, ptr_cmp);      \
00063     else if (CMP(high, mid) < 0)      \
00064       SWAP(mid, high, size, ptr_cmp);     \
00065 }
00066 
00067 /* The following node is used to store ranges to avoid recursive calls */
00068 
00069 typedef struct st_stack
00070 {
00071   char *low,*high;
00072 } stack_node;
00073 
00074 #define PUSH(LOW,HIGH)  {stack_ptr->low = LOW; stack_ptr++->high = HIGH;}
00075 #define POP(LOW,HIGH)   {LOW = (--stack_ptr)->low; HIGH = stack_ptr->high;}
00076 
00077 /* The following stack size is enough for UINT32_MAX elements */
00078 #define STACK_SIZE  (8 * sizeof(unsigned long int))
00079 #define THRESHOLD_FOR_INSERT_SORT 10
00080 
00081 /****************************************************************************
00082 ** 'standard' quicksort with the following extensions:
00083 **
00084 ** Can be compiled with the qsort2_cmp compare function
00085 ** Store ranges on stack to avoid recursion
00086 ** Use insert sort on small ranges
00087 ** Optimize for sorting of pointers (used often by MySQL)
00088 ** Use median comparison to find partition element
00089 *****************************************************************************/
00090 
00091 #ifdef QSORT_EXTRA_CMP_ARGUMENT
00092 void my_qsort2(void *base_ptr, size_t count, size_t size,
00093                        qsort2_cmp cmp, void *cmp_argument)
00094 #else
00095 void my_qsort(void *base_ptr, size_t count, size_t size,
00096                       qsort_cmp cmp)
00097 #endif
00098 {
00099   char *low, *high, *pivot;
00100   stack_node stack[STACK_SIZE], *stack_ptr;
00101   bool ptr_cmp;
00102   /* Handle the simple case first */
00103   /* This will also make the rest of the code simpler */
00104   if (count <= 1)
00105     return;
00106 
00107   low  = (char*) base_ptr;
00108   high = low+ size * (count - 1);
00109   stack_ptr = stack + 1;
00110 #ifdef HAVE_VALGRIND
00111   /* The first element in the stack will be accessed for the last POP */
00112   stack[0].low=stack[0].high=0;
00113 #endif
00114   pivot = (char *) malloc(size);
00115   ptr_cmp= size == sizeof(char*) && !((low - (char*) 0)& (sizeof(char*)-1));
00116 
00117   /* The following loop sorts elements between high and low */
00118   do
00119   {
00120     char *low_ptr, *high_ptr, *mid;
00121 
00122     count=((size_t) (high - low) / size)+1;
00123     /* If count is small, then an insert sort is faster than qsort */
00124     if (count < THRESHOLD_FOR_INSERT_SORT)
00125     {
00126       for (low_ptr = low + size; low_ptr <= high; low_ptr += size)
00127       {
00128         char *ptr;
00129         for (ptr = low_ptr; ptr > low && CMP(ptr - size, ptr) > 0;
00130              ptr -= size)
00131           SWAP(ptr, ptr - size, size, ptr_cmp);
00132       }
00133       POP(low, high);
00134       continue;
00135     }
00136 
00137     /* Try to find a good middle element */
00138     mid= low + size * (count >> 1);
00139     if (count > 40)       /* Must be bigger than 24 */
00140     {
00141       size_t step = size* (count / 8);
00142       MEDIAN(low, low + step, low+step*2);
00143       MEDIAN(mid - step, mid, mid+step);
00144       MEDIAN(high - 2 * step, high-step, high);
00145       /* Put best median in 'mid' */
00146       MEDIAN(low+step, mid, high-step);
00147       low_ptr  = low;
00148       high_ptr = high;
00149     }
00150     else
00151     {
00152       MEDIAN(low, mid, high);
00153       /* The low and high argument are already in sorted against 'pivot' */
00154       low_ptr  = low + size;
00155       high_ptr = high - size;
00156     }
00157     memcpy(pivot, mid, size);
00158 
00159     do
00160     {
00161       while (CMP(low_ptr, pivot) < 0)
00162         low_ptr += size;
00163       while (CMP(pivot, high_ptr) < 0)
00164         high_ptr -= size;
00165 
00166       if (low_ptr < high_ptr)
00167       {
00168         SWAP(low_ptr, high_ptr, size, ptr_cmp);
00169         low_ptr += size;
00170         high_ptr -= size;
00171       }
00172       else
00173       {
00174         if (low_ptr == high_ptr)
00175         {
00176           low_ptr += size;
00177           high_ptr -= size;
00178         }
00179         break;
00180       }
00181     }
00182     while (low_ptr <= high_ptr);
00183 
00184     /*
00185       Prepare for next iteration.
00186        Skip partitions of size 1 as these doesn't have to be sorted
00187        Push the larger partition and sort the smaller one first.
00188        This ensures that the stack is keept small.
00189     */
00190 
00191     if ((int) (high_ptr - low) <= 0)
00192     {
00193       if ((int) (high - low_ptr) <= 0)
00194       {
00195         POP(low, high);     /* Nothing more to sort */
00196       }
00197       else
00198         low = low_ptr;      /* Ignore small left part. */
00199     }
00200     else if ((int) (high - low_ptr) <= 0)
00201       high = high_ptr;      /* Ignore small right part. */
00202     else if ((high_ptr - low) > (high - low_ptr))
00203     {
00204       PUSH(low, high_ptr);    /* Push larger left part */
00205       low = low_ptr;
00206     }
00207     else
00208     {
00209       PUSH(low_ptr, high);    /* Push larger right part */
00210       high = high_ptr;
00211     }
00212   } while (stack_ptr > stack);
00213   free(pivot);
00214   return;
00215 }
00216 
00217 } /* namespace internal */
00218 } /* namespace drizzled */