|
| 1 | +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ |
| 2 | +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ |
| 3 | +╞══════════════════════════════════════════════════════════════════════════════╡ |
| 4 | +│ Copyright (c) 1991, 1993 │ |
| 5 | +│ The Regents of the University of California. All rights reserved. │ |
| 6 | +│ │ |
| 7 | +│ Redistribution and use in source and binary forms, with or without │ |
| 8 | +│ modification, are permitted provided that the following conditions │ |
| 9 | +│ are met: │ |
| 10 | +│ 1. Redistributions of source code must retain the above copyright │ |
| 11 | +│ notice, this list of conditions and the following disclaimer. │ |
| 12 | +│ 2. Redistributions in binary form must reproduce the above copyright │ |
| 13 | +│ notice, this list of conditions and the following disclaimer in the │ |
| 14 | +│ documentation and/or other materials provided with the distribution. │ |
| 15 | +│ 3. Neither the name of the University nor the names of its contributors │ |
| 16 | +│ may be used to endorse or promote products derived from this software │ |
| 17 | +│ without specific prior written permission. │ |
| 18 | +│ │ |
| 19 | +│ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND │ |
| 20 | +│ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE │ |
| 21 | +│ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE │ |
| 22 | +│ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE │ |
| 23 | +│ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL │ |
| 24 | +│ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS │ |
| 25 | +│ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) │ |
| 26 | +│ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT │ |
| 27 | +│ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY │ |
| 28 | +│ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF │ |
| 29 | +│ SUCH DAMAGE. │ |
| 30 | +╚─────────────────────────────────────────────────────────────────────────────*/ |
| 31 | +#include "libc/mem/alg.h" |
| 32 | +#include "libc/mem/mem.h" |
| 33 | +#include "libc/sysv/errfuns.h" |
| 34 | +// clang-format off |
| 35 | + |
| 36 | +/* |
| 37 | + * Swap two areas of size number of bytes. Although qsort(3) permits random |
| 38 | + * blocks of memory to be sorted, sorting pointers is almost certainly the |
| 39 | + * common case (and, were it not, could easily be made so). Regardless, it |
| 40 | + * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer |
| 41 | + * arithmetic gets lost in the time required for comparison function calls. |
| 42 | + */ |
| 43 | +#define SWAP(a, b, count, size, tmp) { \ |
| 44 | + count = size; \ |
| 45 | + do { \ |
| 46 | + tmp = *a; \ |
| 47 | + *a++ = *b; \ |
| 48 | + *b++ = tmp; \ |
| 49 | + } while (--count); \ |
| 50 | +} |
| 51 | + |
| 52 | +/* Copy one block of size size to another. */ |
| 53 | +#define COPY(a, b, count, size, tmp1, tmp2) { \ |
| 54 | + count = size; \ |
| 55 | + tmp1 = a; \ |
| 56 | + tmp2 = b; \ |
| 57 | + do { \ |
| 58 | + *tmp1++ = *tmp2++; \ |
| 59 | + } while (--count); \ |
| 60 | +} |
| 61 | + |
| 62 | +/* |
| 63 | + * Build the list into a heap, where a heap is defined such that for |
| 64 | + * the records K1 ... KN, Kj/2 >= Kj for 1 <= j/2 <= j <= N. |
| 65 | + * |
| 66 | + * There are two cases. If j == nmemb, select largest of Ki and Kj. If |
| 67 | + * j < nmemb, select largest of Ki, Kj and Kj+1. |
| 68 | + */ |
| 69 | +#define CREATE(initval, nmemb, par_i, child_i, par, child, size, count, tmp) { \ |
| 70 | + for (par_i = initval; (child_i = par_i * 2) <= nmemb; \ |
| 71 | + par_i = child_i) { \ |
| 72 | + child = base + child_i * size; \ |
| 73 | + if (child_i < nmemb && compar(child, child + size, z) < 0) { \ |
| 74 | + child += size; \ |
| 75 | + ++child_i; \ |
| 76 | + } \ |
| 77 | + par = base + par_i * size; \ |
| 78 | + if (compar(child, par, z) <= 0) \ |
| 79 | + break; \ |
| 80 | + SWAP(par, child, count, size, tmp); \ |
| 81 | + } \ |
| 82 | +} |
| 83 | + |
| 84 | +/* |
| 85 | + * Select the top of the heap and 'heapify'. Since by far the most expensive |
| 86 | + * action is the call to the compar function, a considerable optimization |
| 87 | + * in the average case can be achieved due to the fact that k, the displaced |
| 88 | + * element, is usually quite small, so it would be preferable to first |
| 89 | + * heapify, always maintaining the invariant that the larger child is copied |
| 90 | + * over its parent's record. |
| 91 | + * |
| 92 | + * Then, starting from the *bottom* of the heap, finding k's correct place, |
| 93 | + * again maintaining the invariant. As a result of the invariant no element |
| 94 | + * is 'lost' when k is assigned its correct place in the heap. |
| 95 | + * |
| 96 | + * The time savings from this optimization are on the order of 15-20% for the |
| 97 | + * average case. See Knuth, Vol. 3, page 158, problem 18. |
| 98 | + * |
| 99 | + * XXX Don't break the #define SELECT line, below. Reiser cpp gets upset. |
| 100 | + */ |
| 101 | +#define SELECT(par_i, child_i, nmemb, par, child, size, k, count, tmp1, tmp2) { \ |
| 102 | + for (par_i = 1; (child_i = par_i * 2) <= nmemb; par_i = child_i) { \ |
| 103 | + child = base + child_i * size; \ |
| 104 | + if (child_i < nmemb && compar(child, child + size, z) < 0) { \ |
| 105 | + child += size; \ |
| 106 | + ++child_i; \ |
| 107 | + } \ |
| 108 | + par = base + par_i * size; \ |
| 109 | + COPY(par, child, count, size, tmp1, tmp2); \ |
| 110 | + } \ |
| 111 | + for (;;) { \ |
| 112 | + child_i = par_i; \ |
| 113 | + par_i = child_i / 2; \ |
| 114 | + child = base + child_i * size; \ |
| 115 | + par = base + par_i * size; \ |
| 116 | + if (child_i == 1 || compar(k, par, z) < 0) { \ |
| 117 | + COPY(child, k, count, size, tmp1, tmp2); \ |
| 118 | + break; \ |
| 119 | + } \ |
| 120 | + COPY(child, par, count, size, tmp1, tmp2); \ |
| 121 | + } \ |
| 122 | +} |
| 123 | + |
| 124 | +/** |
| 125 | + * Sorts array w/ optional callback argument. |
| 126 | + * |
| 127 | + * @param vbase is base of array |
| 128 | + * @param nmemb is item count |
| 129 | + * @param size is item width |
| 130 | + * @param compar is a callback returning <0, 0, or >0 |
| 131 | + * @param z will optionally be passed as the third argument to cmp |
| 132 | + * @see heapsort() |
| 133 | + */ |
| 134 | +int |
| 135 | +heapsort_r(void *vbase, size_t nmemb, size_t size, |
| 136 | + int (*compar)(const void *, const void *, void *), void *z) |
| 137 | +{ |
| 138 | + size_t cnt, i, j, l; |
| 139 | + char tmp, *tmp1, *tmp2; |
| 140 | + char *base, *k, *p, *t; |
| 141 | + |
| 142 | + if (nmemb <= 1) |
| 143 | + return (0); |
| 144 | + |
| 145 | + if (!size) |
| 146 | + return (einval()); |
| 147 | + |
| 148 | + if ((k = malloc(size)) == NULL) |
| 149 | + return (-1); |
| 150 | + |
| 151 | + /* |
| 152 | + * Items are numbered from 1 to nmemb, so offset from size bytes |
| 153 | + * below the starting address. |
| 154 | + */ |
| 155 | + base = (char *)vbase - size; |
| 156 | + |
| 157 | + for (l = nmemb / 2 + 1; --l;) |
| 158 | + CREATE(l, nmemb, i, j, t, p, size, cnt, tmp); |
| 159 | + |
| 160 | + /* |
| 161 | + * For each element of the heap, save the largest element into its |
| 162 | + * final slot, save the displaced element (k), then recreate the |
| 163 | + * heap. |
| 164 | + */ |
| 165 | + while (nmemb > 1) { |
| 166 | + COPY(k, base + nmemb * size, cnt, size, tmp1, tmp2); |
| 167 | + COPY(base + nmemb * size, base + size, cnt, size, tmp1, tmp2); |
| 168 | + --nmemb; |
| 169 | + SELECT(i, j, nmemb, t, p, size, k, cnt, tmp1, tmp2); |
| 170 | + } |
| 171 | + free(k); |
| 172 | + return (0); |
| 173 | +} |
| 174 | + |
| 175 | +/** |
| 176 | + * Sorts array. |
| 177 | + * |
| 178 | + * Runs in O (N lg N), both average and worst. While heapsort is faster |
| 179 | + * than the worst case of quicksort, the BSD quicksort does median |
| 180 | + * selection so that the chance of finding a data set that will trigger |
| 181 | + * the worst case is nonexistent. Heapsort's only advantage over |
| 182 | + * quicksort is that it requires little additional memory. |
| 183 | + * |
| 184 | + * @param vbase is base of array |
| 185 | + * @param nmemb is item count |
| 186 | + * @param size is item width |
| 187 | + * @param compar is a callback returning <0, 0, or >0 |
| 188 | + * @see Knuth, Vol. 3, page 145. |
| 189 | + * @see heapsort_r() |
| 190 | + * @see mergesort() |
| 191 | + * @see qsort() |
| 192 | + */ |
| 193 | +int |
| 194 | +heapsort(void *vbase, size_t nmemb, size_t size, |
| 195 | + int (*compar)(const void *, const void *)) |
| 196 | +{ |
| 197 | + return heapsort_r(vbase, nmemb, size, (void *)compar, 0); |
| 198 | +} |
0 commit comments