1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
/*
* $Id: blist.c,v 1.11 2008/01/13 16:15:22 tom Exp $
* Copyright 2007,2008 by Thomas E. Dickey
*
* Provide binary-search lookup of arrays of sorted structs. The beginning of
* each struct is a pointer to a string, which is the key by which the structs
* are sorted. The last entry in the array contains a struct whose key is
* null. That makes it simple to initialize the itemCount value when the
* array contents are determined by ifdef's.
*/
#include <estruct.h>
#include <blist.h>
#define ILLEGAL_NUM -1
#define ItemOf(data,inx) *(const char * const*)((const char *)(data->theList) + ((inx) * data->itemSize))
#define ItemToInx(data, item) \
(((const char *) item - (const char *) (data->theList)) \
/ data->itemSize)
typedef struct {
const char *name;
} ToFind;
#ifdef DEBUG_ME
static long total_calls;
static long total_compares;
static long total_linear;
static long total_limits;
#define COUNTER(which,n) which += n
#else
#define COUNTER(which,n)
#endif
/*
* Returns the number of items in the BLIST, saving the number in the struct
* for later use.
*/
int
blist_count(BLIST * data)
{
if (data->itemCount < 0) {
int n;
const char *item;
for (n = 0; (item = ItemOf(data, n)) != 0; ++n) {
;
}
data->itemCount = n;
}
return data->itemCount;
}
static int
exact_match(const void *keyobj, const void *arraymember)
{
COUNTER(total_compares, 1);
return strcmp(*(const char *const *) keyobj, (*(const char *const *) arraymember));
}
/*
* Exact matches can use bsearch.
*/
int
blist_match(BLIST * data, const char *name)
{
int rc = -1;
int last = blist_count(data);
void *check;
ToFind dummy;
dummy.name = name;
check = bsearch(&dummy, data->theList, last, data->itemSize, exact_match);
if (check != 0) {
rc = ItemToInx(data, check);
}
COUNTER(total_linear, rc + 1);
COUNTER(total_limits, last);
COUNTER(total_calls, 1);
return rc;
}
/*
* Given what may be part of the name (length in 'len'), look for a match.
* If the result is ambiguous, do not match unless the given name exactly
* matches one item.
*/
int
blist_pmatch(BLIST * data, const char *name, int len)
{
int actual = strlen(name);
int rc = -1;
int hi, lo, x0, x1, cmp;
int last = blist_count(data);
int exact = 0;
const char *item;
const char *test;
if (len < 0 || len > actual)
len = actual;
x1 = -1;
hi = last - 1;
lo = 0;
do {
x0 = x1;
x1 = lo + (hi - lo) / 2;
if (x0 == x1) {
if (++x1 > hi)
break;
}
item = ItemOf(data, x1);
cmp = strncmp(item, name, len);
if (cmp < 0) {
lo = (x1 == lo) ? (x1 + 1) : x1;
} else if (cmp > 0) {
hi = (x1 == hi) ? (x1 - 1) : x1;
} else {
rc = x1;
/*
* Check for an exact match...
*/
COUNTER(total_compares, 1);
if (strcmp(item, name)) {
if (x1 > lo) {
ToFind dummy;
dummy.name = name;
test = (const char *) bsearch(&dummy,
&ItemOf(data, lo),
x1 + 1 - lo,
data->itemSize,
exact_match);
if (test) {
exact = 1;
rc = ItemToInx(data, test);
break;
}
}
}
/*
* Now - if we have not found an exact match, check for ambiguity.
*/
if (rc >= 0) {
if (len > (int) strlen(item)) {
rc = -1;
} else if (x1 < last - 1) {
COUNTER(total_compares, 2);
if (strcmp(item, name)
&& !strncmp(ItemOf(data, x1 + 1), name, len)) {
rc = -1;
}
}
}
break;
}
} while (hi >= lo);
COUNTER(total_linear, rc + 2);
COUNTER(total_limits, last);
COUNTER(total_calls, 1);
return rc;
}
/*
* Reset the count because we have a new or reallocated list.
*/
void
blist_reset(BLIST * data, const void *newList)
{
data->itemCount = -1;
data->theList = newList;
}
void
blist_summary(void)
{
#ifdef DEBUG_ME
if (total_calls) {
printf("\r\nBLIST:\n");
printf(" calls %ld, average table size %.1f\n",
total_calls,
(1.0 * total_limits) / total_calls);
printf(" compares %6ld (%.1f%%)\n",
total_compares,
(100.0 * total_compares) / total_limits);
printf(" linear %6ld (%.1f%%)\n\n",
total_linear,
(100.0 * total_linear) / total_limits);
}
#endif
}
|