Home | History | Annotate | Download | only in common
      1 /*
      2 *******************************************************************************
      3 *
      4 *   Copyright (C) 2003, International Business Machines
      5 *   Corporation and others.  All Rights Reserved.
      6 *
      7 *******************************************************************************
      8 *   file name:  uarrsort.c
      9 *   encoding:   US-ASCII
     10 *   tab size:   8 (not used)
     11 *   indentation:4
     12 *
     13 *   created on: 2003aug04
     14 *   created by: Markus W. Scherer
     15 *
     16 *   Internal function for sorting arrays.
     17 */
     18 
     19 #include "unicode/utypes.h"
     20 #include "cmemory.h"
     21 #include "uarrsort.h"
     22 
     23 enum {
     24     MIN_QSORT=9, /* from Knuth */
     25     STACK_ITEM_SIZE=200
     26 };
     27 
     28 /* UComparator convenience implementations ---------------------------------- */
     29 
     30 U_CAPI int32_t U_EXPORT2
     31 uprv_uint16Comparator(const void *context, const void *left, const void *right) {
     32     return (int32_t)*(const uint16_t *)left - (int32_t)*(const uint16_t *)right;
     33 }
     34 
     35 U_CAPI int32_t U_EXPORT2
     36 uprv_int32Comparator(const void *context, const void *left, const void *right) {
     37     return *(const int32_t *)left - *(const int32_t *)right;
     38 }
     39 
     40 U_CAPI int32_t U_EXPORT2
     41 uprv_uint32Comparator(const void *context, const void *left, const void *right) {
     42     uint32_t l=*(const uint32_t *)left, r=*(const uint32_t *)right;
     43 
     44     /* compare directly because (l-r) would overflow the int32_t result */
     45     if(l<r) {
     46         return -1;
     47     } else if(l==r) {
     48         return 0;
     49     } else /* l>r */ {
     50         return 1;
     51     }
     52 }
     53 
     54 /* Straight insertion sort from Knuth vol. III, pg. 81 ---------------------- */
     55 
     56 static void
     57 doInsertionSort(char *array, int32_t start, int32_t limit, int32_t itemSize,
     58                 UComparator *cmp, const void *context, void *pv) {
     59     int32_t i, j;
     60 
     61     for(j=start+1; j<limit; ++j) {
     62         /* v=array[j] */
     63         uprv_memcpy(pv, array+j*itemSize, itemSize);
     64 
     65         for(i=j; i>start; --i) {
     66             if(/* v>=array[i-1] */ cmp(context, pv, array+(i-1)*itemSize)>=0) {
     67                 break;
     68             }
     69 
     70             /* array[i]=array[i-1]; */
     71             uprv_memcpy(array+i*itemSize, array+(i-1)*itemSize, itemSize);
     72         }
     73 
     74         if(i!=j) {
     75             /* array[i]=v; */
     76             uprv_memcpy(array+i*itemSize, pv, itemSize);
     77         }
     78     }
     79 }
     80 
     81 static void
     82 insertionSort(char *array, int32_t length, int32_t itemSize,
     83               UComparator *cmp, const void *context, UErrorCode *pErrorCode) {
     84     UAlignedMemory v[STACK_ITEM_SIZE/sizeof(UAlignedMemory)+1];
     85     void *pv;
     86 
     87     /* allocate an intermediate item variable (v) */
     88     if(itemSize<=STACK_ITEM_SIZE) {
     89         pv=v;
     90     } else {
     91         pv=uprv_malloc(itemSize);
     92         if(pv==NULL) {
     93             *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
     94             return;
     95         }
     96     }
     97 
     98     doInsertionSort(array, 0, length, itemSize, cmp, context, pv);
     99 
    100     if(pv!=v) {
    101         uprv_free(pv);
    102     }
    103 }
    104 
    105 /* QuickSort ---------------------------------------------------------------- */
    106 
    107 /*
    108  * This implementation is semi-recursive:
    109  * It recurses for the smaller sub-array to shorten the recursion depth,
    110  * and loops for the larger sub-array.
    111  *
    112  * Loosely after QuickSort algorithms in
    113  * Niklaus Wirth
    114  * Algorithmen und Datenstrukturen mit Modula-2
    115  * B.G. Teubner Stuttgart
    116  * 4. Auflage 1986
    117  * ISBN 3-519-02260-5
    118  */
    119 static void
    120 subQuickSort(char *array, int32_t start, int32_t limit, int32_t itemSize,
    121              UComparator *cmp, const void *context,
    122              void *px, void *pw) {
    123     int32_t left, right;
    124 
    125     /* start and left are inclusive, limit and right are exclusive */
    126     do {
    127         if((start+MIN_QSORT)>=limit) {
    128             doInsertionSort(array, start, limit, itemSize, cmp, context, px);
    129             break;
    130         }
    131 
    132         left=start;
    133         right=limit;
    134 
    135         /* x=array[middle] */
    136         uprv_memcpy(px, array+((start+limit)/2)*itemSize, itemSize);
    137 
    138         do {
    139             while(/* array[left]<x */
    140                   cmp(context, array+left*itemSize, px)<0
    141             ) {
    142                 ++left;
    143             }
    144             while(/* x<array[right-1] */
    145                   cmp(context, px, array+(right-1)*itemSize)<0
    146             ) {
    147                 --right;
    148             }
    149 
    150             /* swap array[left] and array[right-1] via w; ++left; --right */
    151             if(left<right) {
    152                 --right;
    153 
    154                 if(left<right) {
    155                     uprv_memcpy(pw, array+left*itemSize, itemSize);
    156                     uprv_memcpy(array+left*itemSize, array+right*itemSize, itemSize);
    157                     uprv_memcpy(array+right*itemSize, pw, itemSize);
    158                 }
    159 
    160                 ++left;
    161             }
    162         } while(left<right);
    163 
    164         /* sort sub-arrays */
    165         if((right-start)<(limit-left)) {
    166             /* sort [start..right[ */
    167             if(start<(right-1)) {
    168                 subQuickSort(array, start, right, itemSize, cmp, context, px, pw);
    169             }
    170 
    171             /* sort [left..limit[ */
    172             start=left;
    173         } else {
    174             /* sort [left..limit[ */
    175             if(left<(limit-1)) {
    176                 subQuickSort(array, left, limit, itemSize, cmp, context, px, pw);
    177             }
    178 
    179             /* sort [start..right[ */
    180             limit=right;
    181         }
    182     } while(start<(limit-1));
    183 }
    184 
    185 static void
    186 quickSort(char *array, int32_t length, int32_t itemSize,
    187             UComparator *cmp, const void *context, UErrorCode *pErrorCode) {
    188     UAlignedMemory xw[(2*STACK_ITEM_SIZE)/sizeof(UAlignedMemory)+1];
    189     void *p;
    190 
    191     /* allocate two intermediate item variables (x and w) */
    192     if(itemSize<=STACK_ITEM_SIZE) {
    193         p=xw;
    194     } else {
    195         p=uprv_malloc(2*itemSize);
    196         if(p==NULL) {
    197             *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
    198             return;
    199         }
    200     }
    201 
    202     subQuickSort(array, 0, length, itemSize,
    203                  cmp, context, p, (char *)p+itemSize);
    204 
    205     if(p!=xw) {
    206         uprv_free(p);
    207     }
    208 }
    209 
    210 /* uprv_sortArray() API ----------------------------------------------------- */
    211 
    212 /*
    213  * Check arguments, select an appropriate implementation,
    214  * cast the array to char * so that array+i*itemSize works.
    215  */
    216 U_CAPI void U_EXPORT2
    217 uprv_sortArray(void *array, int32_t length, int32_t itemSize,
    218                UComparator *cmp, const void *context,
    219                UBool sortStable, UErrorCode *pErrorCode) {
    220     if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
    221         return;
    222     }
    223     if((length>0 && array==NULL) || length<0 || itemSize<=0 || cmp==NULL) {
    224         *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
    225         return;
    226     }
    227 
    228     if(length<=1) {
    229         return;
    230     } else if(length<MIN_QSORT || sortStable) {
    231         insertionSort((char *)array, length, itemSize, cmp, context, pErrorCode);
    232         /* could add heapSort or similar for stable sorting of longer arrays */
    233     } else {
    234         quickSort((char *)array, length, itemSize, cmp, context, pErrorCode);
    235     }
    236 }
    237