blob: 2faa5897eb2638e28422c5a79e6a099eefc0378c [file] [log] [blame]
/*
* Copyright (c) 2015 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported into CLIB by Eliot Dresselhaus from:
*
* This file is part of
* MakeIndex - A formatter and format independent index processor
*
* This file is public domain software donated by
* Nelson Beebe (beebe@science.utah.edu).
*
* modifications copyright (c) 2003 Cisco Systems, Inc.
*/
#include <vppinfra/clib.h>
/*
* qsort.c: Our own version of the system qsort routine which is faster by an
* average of 25%, with lows and highs of 10% and 50%. The THRESHold below is
* the insertion sort threshold, and has been adjusted for records of size 48
* bytes. The MTHREShold is where we stop finding a better median.
*/
#define THRESH 4 /* threshold for insertion */
#define MTHRESH 6 /* threshold for median */
typedef struct
{
word qsz; /* size of each record */
word thresh; /* THRESHold in chars */
word mthresh; /* MTHRESHold in chars */
int (*qcmp) (const void *, const void *); /* the comparison routine */
} qst_t;
static void qst (qst_t * q, char *base, char *max);
/*
* qqsort: First, set up some global parameters for qst to share.
* Then, quicksort with qst(), and then a cleanup insertion sort ourselves.
* Sound simple? It's not...
*/
void
qsort (void *base, uword n, uword size,
int (*compar) (const void *, const void *))
{
char *i;
char *j;
char *lo;
char *hi;
char *min;
char c;
char *max;
qst_t _q, *q = &_q;
if (n <= 1)
return;
q->qsz = size;
q->qcmp = compar;
q->thresh = q->qsz * THRESH;
q->mthresh = q->qsz * MTHRESH;
max = base + n * q->qsz;
if (n >= THRESH)
{
qst (q, base, max);
hi = base + q->thresh;
}
else
{
hi = max;
}
/*
* First put smallest element, which must be in the first THRESH, in the
* first position as a sentinel. This is done just by searching the
* first THRESH elements (or the first n if n < THRESH), finding the min,
* and swapping it into the first position.
*/
for (j = lo = base; (lo += q->qsz) < hi;)
{
if ((*compar) (j, lo) > 0)
j = lo;
}
if (j != base)
{ /* swap j into place */
for (i = base, hi = base + q->qsz; i < hi;)
{
c = *j;
*j++ = *i;
*i++ = c;
}
}
/*
* With our sentinel in place, we now run the following hyper-fast
* insertion sort. For each remaining element, min, from [1] to [n-1],
* set hi to the index of the element AFTER which this one goes. Then, do
* the standard insertion sort shift on a character at a time basis for
* each element in the frob.
*/
for (min = base; (hi = min += q->qsz) < max;)
{
while ((*q->qcmp) (hi -= q->qsz, min) > 0);
if ((hi += q->qsz) != min)
{
for (lo = min + q->qsz; --lo >= min;)
{
c = *lo;
for (i = j = lo; (j -= q->qsz) >= hi; i = j)
*i = *j;
*i = c;
}
}
}
}
/*
* qst: Do a quicksort. First, find the median element, and put that one in
* the first place as the discriminator. (This "median" is just the median
* of the first, last and middle elements). (Using this median instead of
* the first element is a big win). Then, the usual partitioning/swapping,
* followed by moving the discriminator into the right place. Then, figure
* out the sizes of the two partions, do the smaller one recursively and the
* larger one via a repeat of this code. Stopping when there are less than
* THRESH elements in a partition and cleaning up with an insertion sort (in
* our caller) is a huge win. All data swaps are done in-line, which is
* space-losing but time-saving. (And there are only three places where this
* is done).
*/
static void
qst (qst_t * q, char *base, char *max)
{
char *i;
char *j;
char *jj;
char *mid;
int ii;
char c;
char *tmp;
int lo;
int hi;
int qsz = q->qsz;
lo = (int) (max - base); /* number of elements as chars */
do
{
/*
* At the top here, lo is the number of characters of elements in the
* current partition. (Which should be max - base). Find the median
* of the first, last, and middle element and make that the middle
* element. Set j to largest of first and middle. If max is larger
* than that guy, then it's that guy, else compare max with loser of
* first and take larger. Things are set up to prefer the middle,
* then the first in case of ties.
*/
mid = i = base + qsz * ((unsigned) (lo / qsz) >> 1);
if (lo >= q->mthresh)
{
j = ((*q->qcmp) ((jj = base), i) > 0 ? jj : i);
if ((*q->qcmp) (j, (tmp = max - qsz)) > 0)
{
/* switch to first loser */
j = (j == jj ? i : jj);
if ((*q->qcmp) (j, tmp) < 0)
j = tmp;
}
if (j != i)
{
ii = qsz;
do
{
c = *i;
*i++ = *j;
*j++ = c;
}
while (--ii);
}
}
/* Semi-standard quicksort partitioning/swapping */
for (i = base, j = max - qsz;;)
{
while (i < mid && (*q->qcmp) (i, mid) <= 0)
i += qsz;
while (j > mid)
{
if ((*q->qcmp) (mid, j) <= 0)
{
j -= qsz;
continue;
}
tmp = i + qsz; /* value of i after swap */
if (i == mid)
{ /* j <-> mid, new mid is j */
mid = jj = j;
}
else
{ /* i <-> j */
jj = j;
j -= qsz;
}
goto swap;
}
if (i == mid)
{
break;
}
else
{ /* i <-> mid, new mid is i */
jj = mid;
tmp = mid = i; /* value of i after swap */
j -= qsz;
}
swap:
ii = qsz;
do
{
c = *i;
*i++ = *jj;
*jj++ = c;
}
while (--ii);
i = tmp;
}
/*
* Look at sizes of the two partitions, do the smaller one first by
* recursion, then do the larger one by making sure lo is its size,
* base and max are update correctly, and branching back. But only
* repeat (recursively or by branching) if the partition is of at
* least size THRESH.
*/
i = (j = mid) + qsz;
if ((lo = (int) (j - base)) <= (hi = (int) (max - i)))
{
if (lo >= q->thresh)
qst (q, base, j);
base = i;
lo = hi;
}
else
{
if (hi >= q->thresh)
qst (q, i, max);
max = j;
}
}
while (lo >= q->thresh);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/