Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 Cisco and/or its affiliates. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at: |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | /* |
| 16 | * Imported into CLIB by Eliot Dresselhaus from: |
| 17 | * |
| 18 | * This file is part of |
| 19 | * MakeIndex - A formatter and format independent index processor |
| 20 | * |
| 21 | * This file is public domain software donated by |
| 22 | * Nelson Beebe (beebe@science.utah.edu). |
| 23 | * |
| 24 | * modifications copyright (c) 2003 Cisco Systems, Inc. |
| 25 | */ |
| 26 | |
| 27 | #include <vppinfra/clib.h> |
| 28 | |
| 29 | /* |
| 30 | * qsort.c: Our own version of the system qsort routine which is faster by an |
| 31 | * average of 25%, with lows and highs of 10% and 50%. The THRESHold below is |
| 32 | * the insertion sort threshold, and has been adjusted for records of size 48 |
| 33 | * bytes. The MTHREShold is where we stop finding a better median. |
| 34 | */ |
| 35 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 36 | #define THRESH 4 /* threshold for insertion */ |
| 37 | #define MTHRESH 6 /* threshold for median */ |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 38 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 39 | typedef struct |
| 40 | { |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 41 | word qsz; /* size of each record */ |
| 42 | word thresh; /* THRESHold in chars */ |
| 43 | word mthresh; /* MTHRESHold in chars */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 44 | int (*qcmp) (const void *, const void *); /* the comparison routine */ |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 45 | } qst_t; |
| 46 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 47 | static void qst (qst_t * q, char *base, char *max); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 48 | |
| 49 | /* |
| 50 | * qqsort: First, set up some global parameters for qst to share. |
| 51 | * Then, quicksort with qst(), and then a cleanup insertion sort ourselves. |
| 52 | * Sound simple? It's not... |
| 53 | */ |
| 54 | |
| 55 | void |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 56 | qsort (void *base, uword n, uword size, |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 57 | int (*compar) (const void *, const void *)) |
| 58 | { |
| 59 | char *i; |
| 60 | char *j; |
| 61 | char *lo; |
| 62 | char *hi; |
| 63 | char *min; |
| 64 | char c; |
| 65 | char *max; |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 66 | qst_t _q, *q = &_q; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 67 | |
| 68 | if (n <= 1) |
| 69 | return; |
| 70 | |
| 71 | q->qsz = size; |
| 72 | q->qcmp = compar; |
| 73 | q->thresh = q->qsz * THRESH; |
| 74 | q->mthresh = q->qsz * MTHRESH; |
| 75 | max = base + n * q->qsz; |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 76 | if (n >= THRESH) |
| 77 | { |
| 78 | qst (q, base, max); |
| 79 | hi = base + q->thresh; |
| 80 | } |
| 81 | else |
| 82 | { |
| 83 | hi = max; |
| 84 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 85 | /* |
| 86 | * First put smallest element, which must be in the first THRESH, in the |
| 87 | * first position as a sentinel. This is done just by searching the |
| 88 | * first THRESH elements (or the first n if n < THRESH), finding the min, |
| 89 | * and swapping it into the first position. |
| 90 | */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 91 | for (j = lo = base; (lo += q->qsz) < hi;) |
| 92 | { |
| 93 | if ((*compar) (j, lo) > 0) |
| 94 | j = lo; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 95 | } |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 96 | if (j != base) |
| 97 | { /* swap j into place */ |
| 98 | for (i = base, hi = base + q->qsz; i < hi;) |
| 99 | { |
| 100 | c = *j; |
| 101 | *j++ = *i; |
| 102 | *i++ = c; |
| 103 | } |
| 104 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 105 | /* |
| 106 | * With our sentinel in place, we now run the following hyper-fast |
| 107 | * insertion sort. For each remaining element, min, from [1] to [n-1], |
| 108 | * set hi to the index of the element AFTER which this one goes. Then, do |
| 109 | * the standard insertion sort shift on a character at a time basis for |
| 110 | * each element in the frob. |
| 111 | */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 112 | for (min = base; (hi = min += q->qsz) < max;) |
| 113 | { |
| 114 | while ((*q->qcmp) (hi -= q->qsz, min) > 0); |
| 115 | if ((hi += q->qsz) != min) |
| 116 | { |
| 117 | for (lo = min + q->qsz; --lo >= min;) |
| 118 | { |
| 119 | c = *lo; |
| 120 | for (i = j = lo; (j -= q->qsz) >= hi; i = j) |
| 121 | *i = *j; |
| 122 | *i = c; |
| 123 | } |
| 124 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 125 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | |
| 129 | |
| 130 | /* |
| 131 | * qst: Do a quicksort. First, find the median element, and put that one in |
| 132 | * the first place as the discriminator. (This "median" is just the median |
| 133 | * of the first, last and middle elements). (Using this median instead of |
| 134 | * the first element is a big win). Then, the usual partitioning/swapping, |
| 135 | * followed by moving the discriminator into the right place. Then, figure |
Paul Vinciguerra | ec11b13 | 2018-09-24 05:25:00 -0700 | [diff] [blame^] | 136 | * out the sizes of the two partitions, do the smaller one recursively and the |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 137 | * larger one via a repeat of this code. Stopping when there are less than |
| 138 | * THRESH elements in a partition and cleaning up with an insertion sort (in |
| 139 | * our caller) is a huge win. All data swaps are done in-line, which is |
| 140 | * space-losing but time-saving. (And there are only three places where this |
| 141 | * is done). |
| 142 | */ |
| 143 | |
| 144 | static void |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 145 | qst (qst_t * q, char *base, char *max) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 146 | { |
| 147 | char *i; |
| 148 | char *j; |
| 149 | char *jj; |
| 150 | char *mid; |
| 151 | int ii; |
| 152 | char c; |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 153 | char *tmp; |
| 154 | int lo; |
| 155 | int hi; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 156 | int qsz = q->qsz; |
| 157 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 158 | lo = (int) (max - base); /* number of elements as chars */ |
| 159 | do |
| 160 | { |
| 161 | /* |
| 162 | * At the top here, lo is the number of characters of elements in the |
| 163 | * current partition. (Which should be max - base). Find the median |
| 164 | * of the first, last, and middle element and make that the middle |
| 165 | * element. Set j to largest of first and middle. If max is larger |
| 166 | * than that guy, then it's that guy, else compare max with loser of |
| 167 | * first and take larger. Things are set up to prefer the middle, |
| 168 | * then the first in case of ties. |
| 169 | */ |
| 170 | mid = i = base + qsz * ((unsigned) (lo / qsz) >> 1); |
| 171 | if (lo >= q->mthresh) |
| 172 | { |
| 173 | j = ((*q->qcmp) ((jj = base), i) > 0 ? jj : i); |
| 174 | if ((*q->qcmp) (j, (tmp = max - qsz)) > 0) |
| 175 | { |
| 176 | /* switch to first loser */ |
| 177 | j = (j == jj ? i : jj); |
| 178 | if ((*q->qcmp) (j, tmp) < 0) |
| 179 | j = tmp; |
| 180 | } |
| 181 | if (j != i) |
| 182 | { |
| 183 | ii = qsz; |
| 184 | do |
| 185 | { |
| 186 | c = *i; |
| 187 | *i++ = *j; |
| 188 | *j++ = c; |
| 189 | } |
| 190 | while (--ii); |
| 191 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 192 | } |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 193 | /* Semi-standard quicksort partitioning/swapping */ |
| 194 | for (i = base, j = max - qsz;;) |
| 195 | { |
| 196 | while (i < mid && (*q->qcmp) (i, mid) <= 0) |
| 197 | i += qsz; |
| 198 | while (j > mid) |
| 199 | { |
| 200 | if ((*q->qcmp) (mid, j) <= 0) |
| 201 | { |
| 202 | j -= qsz; |
| 203 | continue; |
| 204 | } |
| 205 | tmp = i + qsz; /* value of i after swap */ |
| 206 | if (i == mid) |
| 207 | { /* j <-> mid, new mid is j */ |
| 208 | mid = jj = j; |
| 209 | } |
| 210 | else |
| 211 | { /* i <-> j */ |
| 212 | jj = j; |
| 213 | j -= qsz; |
| 214 | } |
| 215 | goto swap; |
| 216 | } |
| 217 | if (i == mid) |
| 218 | { |
| 219 | break; |
| 220 | } |
| 221 | else |
| 222 | { /* i <-> mid, new mid is i */ |
| 223 | jj = mid; |
| 224 | tmp = mid = i; /* value of i after swap */ |
| 225 | j -= qsz; |
| 226 | } |
| 227 | swap: |
| 228 | ii = qsz; |
| 229 | do |
| 230 | { |
| 231 | c = *i; |
| 232 | *i++ = *jj; |
| 233 | *jj++ = c; |
| 234 | } |
| 235 | while (--ii); |
| 236 | i = tmp; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 237 | } |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 238 | /* |
| 239 | * Look at sizes of the two partitions, do the smaller one first by |
| 240 | * recursion, then do the larger one by making sure lo is its size, |
| 241 | * base and max are update correctly, and branching back. But only |
| 242 | * repeat (recursively or by branching) if the partition is of at |
| 243 | * least size THRESH. |
| 244 | */ |
| 245 | i = (j = mid) + qsz; |
| 246 | if ((lo = (int) (j - base)) <= (hi = (int) (max - i))) |
| 247 | { |
| 248 | if (lo >= q->thresh) |
| 249 | qst (q, base, j); |
| 250 | base = i; |
| 251 | lo = hi; |
| 252 | } |
| 253 | else |
| 254 | { |
| 255 | if (hi >= q->thresh) |
| 256 | qst (q, i, max); |
| 257 | max = j; |
| 258 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 259 | } |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 260 | while (lo >= q->thresh); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 261 | } |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 262 | |
| 263 | /* |
| 264 | * fd.io coding-style-patch-verification: ON |
| 265 | * |
| 266 | * Local Variables: |
| 267 | * eval: (c-set-style "gnu") |
| 268 | * End: |
| 269 | */ |