| 1 | /* Copyright (c) 2000-2002, 2007 MySQL AB |
| 2 | Use is subject to license terms |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
| 16 | |
| 17 | /* |
| 18 | qsort implementation optimized for comparison of pointers |
| 19 | Inspired by the qsort implementations by Douglas C. Schmidt, |
| 20 | and Bentley & McIlroy's "Engineering a Sort Function". |
| 21 | */ |
| 22 | |
| 23 | |
| 24 | #include "mysys_priv.h" |
| 25 | #ifndef SCO |
| 26 | #include <m_string.h> |
| 27 | #endif |
| 28 | |
| 29 | /* We need to use qsort with 2 different compare functions */ |
| 30 | #ifdef QSORT_EXTRA_CMP_ARGUMENT |
| 31 | #define CMP(A,B) ((*cmp)(cmp_argument,(A),(B))) |
| 32 | #else |
| 33 | #define CMP(A,B) ((*cmp)((A),(B))) |
| 34 | #endif |
| 35 | |
| 36 | #define SWAP(A, B, size,swap_ptrs) \ |
| 37 | do { \ |
| 38 | if (swap_ptrs) \ |
| 39 | { \ |
| 40 | reg1 char **a = (char**) (A), **b = (char**) (B); \ |
| 41 | char *tmp = *a; *a++ = *b; *b++ = tmp; \ |
| 42 | } \ |
| 43 | else \ |
| 44 | { \ |
| 45 | reg1 char *a = (A), *b = (B); \ |
| 46 | reg3 char *end= a+size; \ |
| 47 | do \ |
| 48 | { \ |
| 49 | char tmp = *a; *a++ = *b; *b++ = tmp; \ |
| 50 | } while (a < end); \ |
| 51 | } \ |
| 52 | } while (0) |
| 53 | |
| 54 | /* Put the median in the middle argument */ |
| 55 | #define MEDIAN(low, mid, high) \ |
| 56 | { \ |
| 57 | if (CMP(high,low) < 0) \ |
| 58 | SWAP(high, low, size, ptr_cmp); \ |
| 59 | if (CMP(mid, low) < 0) \ |
| 60 | SWAP(mid, low, size, ptr_cmp); \ |
| 61 | else if (CMP(high, mid) < 0) \ |
| 62 | SWAP(mid, high, size, ptr_cmp); \ |
| 63 | } |
| 64 | |
| 65 | /* The following node is used to store ranges to avoid recursive calls */ |
| 66 | |
| 67 | typedef struct st_stack |
| 68 | { |
| 69 | char *low,*high; |
| 70 | } stack_node; |
| 71 | |
| 72 | #define PUSH(LOW,HIGH) {stack_ptr->low = LOW; stack_ptr++->high = HIGH;} |
| 73 | #define POP(LOW,HIGH) {LOW = (--stack_ptr)->low; HIGH = stack_ptr->high;} |
| 74 | |
| 75 | /* The following stack size is enough for ulong ~0 elements */ |
| 76 | #define STACK_SIZE (8 * sizeof(unsigned long int)) |
| 77 | #define THRESHOLD_FOR_INSERT_SORT 10 |
| 78 | #if defined(QSORT_TYPE_IS_VOID) |
| 79 | #define SORT_RETURN return |
| 80 | #else |
| 81 | #define SORT_RETURN return 0 |
| 82 | #endif |
| 83 | |
| 84 | /**************************************************************************** |
| 85 | ** 'standard' quicksort with the following extensions: |
| 86 | ** |
| 87 | ** Can be compiled with the qsort2_cmp compare function |
| 88 | ** Store ranges on stack to avoid recursion |
| 89 | ** Use insert sort on small ranges |
| 90 | ** Optimize for sorting of pointers (used often by MySQL) |
| 91 | ** Use median comparison to find partition element |
| 92 | *****************************************************************************/ |
| 93 | |
| 94 | #ifdef QSORT_EXTRA_CMP_ARGUMENT |
| 95 | qsort_t my_qsort2(void *base_ptr, size_t count, size_t size, qsort2_cmp cmp, |
| 96 | void *cmp_argument) |
| 97 | #else |
| 98 | qsort_t my_qsort(void *base_ptr, size_t count, size_t size, qsort_cmp cmp) |
| 99 | #endif |
| 100 | { |
| 101 | char *low, *high, *pivot; |
| 102 | stack_node stack[STACK_SIZE], *stack_ptr; |
| 103 | my_bool ptr_cmp; |
| 104 | /* Handle the simple case first */ |
| 105 | /* This will also make the rest of the code simpler */ |
| 106 | if (count <= 1) |
| 107 | SORT_RETURN; |
| 108 | |
| 109 | low = (char*) base_ptr; |
| 110 | high = low+ size * (count - 1); |
| 111 | stack_ptr = stack + 1; |
| 112 | #ifdef HAVE_valgrind |
| 113 | /* The first element in the stack will be accessed for the last POP */ |
| 114 | stack[0].low=stack[0].high=0; |
| 115 | #endif |
| 116 | pivot = (char *) my_alloca((int) size); |
| 117 | ptr_cmp= size == sizeof(char*) && !((low - (char*) 0)& (sizeof(char*)-1)); |
| 118 | |
| 119 | /* The following loop sorts elements between high and low */ |
| 120 | do |
| 121 | { |
| 122 | char *low_ptr, *high_ptr, *mid; |
| 123 | |
| 124 | count=((size_t) (high - low) / size)+1; |
| 125 | /* If count is small, then an insert sort is faster than qsort */ |
| 126 | if (count < THRESHOLD_FOR_INSERT_SORT) |
| 127 | { |
| 128 | for (low_ptr = low + size; low_ptr <= high; low_ptr += size) |
| 129 | { |
| 130 | char *ptr; |
| 131 | for (ptr = low_ptr; ptr > low && CMP(ptr - size, ptr) > 0; |
| 132 | ptr -= size) |
| 133 | SWAP(ptr, ptr - size, size, ptr_cmp); |
| 134 | } |
| 135 | POP(low, high); |
| 136 | continue; |
| 137 | } |
| 138 | |
| 139 | /* Try to find a good middle element */ |
| 140 | mid= low + size * (count >> 1); |
| 141 | if (count > 40) /* Must be bigger than 24 */ |
| 142 | { |
| 143 | size_t step = size* (count / 8); |
| 144 | MEDIAN(low, low + step, low+step*2); |
| 145 | MEDIAN(mid - step, mid, mid+step); |
| 146 | MEDIAN(high - 2 * step, high-step, high); |
| 147 | /* Put best median in 'mid' */ |
| 148 | MEDIAN(low+step, mid, high-step); |
| 149 | low_ptr = low; |
| 150 | high_ptr = high; |
| 151 | } |
| 152 | else |
| 153 | { |
| 154 | MEDIAN(low, mid, high); |
| 155 | /* The low and high argument are already in sorted against 'pivot' */ |
| 156 | low_ptr = low + size; |
| 157 | high_ptr = high - size; |
| 158 | } |
| 159 | memcpy(pivot, mid, size); |
| 160 | |
| 161 | do |
| 162 | { |
| 163 | while (CMP(low_ptr, pivot) < 0) |
| 164 | low_ptr += size; |
| 165 | while (CMP(pivot, high_ptr) < 0) |
| 166 | high_ptr -= size; |
| 167 | |
| 168 | if (low_ptr < high_ptr) |
| 169 | { |
| 170 | SWAP(low_ptr, high_ptr, size, ptr_cmp); |
| 171 | low_ptr += size; |
| 172 | high_ptr -= size; |
| 173 | } |
| 174 | else |
| 175 | { |
| 176 | if (low_ptr == high_ptr) |
| 177 | { |
| 178 | low_ptr += size; |
| 179 | high_ptr -= size; |
| 180 | } |
| 181 | break; |
| 182 | } |
| 183 | } |
| 184 | while (low_ptr <= high_ptr); |
| 185 | |
| 186 | /* |
| 187 | Prepare for next iteration. |
| 188 | Skip partitions of size 1 as these doesn't have to be sorted |
| 189 | Push the larger partition and sort the smaller one first. |
| 190 | This ensures that the stack is keept small. |
| 191 | */ |
| 192 | |
| 193 | if ((int) (high_ptr - low) <= 0) |
| 194 | { |
| 195 | if ((int) (high - low_ptr) <= 0) |
| 196 | { |
| 197 | POP(low, high); /* Nothing more to sort */ |
| 198 | } |
| 199 | else |
| 200 | low = low_ptr; /* Ignore small left part. */ |
| 201 | } |
| 202 | else if ((int) (high - low_ptr) <= 0) |
| 203 | high = high_ptr; /* Ignore small right part. */ |
| 204 | else if ((high_ptr - low) > (high - low_ptr)) |
| 205 | { |
| 206 | PUSH(low, high_ptr); /* Push larger left part */ |
| 207 | low = low_ptr; |
| 208 | } |
| 209 | else |
| 210 | { |
| 211 | PUSH(low_ptr, high); /* Push larger right part */ |
| 212 | high = high_ptr; |
| 213 | } |
| 214 | } while (stack_ptr > stack); |
| 215 | my_afree(pivot); |
| 216 | SORT_RETURN; |
| 217 | } |
| 218 | |