-
Notifications
You must be signed in to change notification settings - Fork 0
/
blist.c
191 lines (173 loc) · 4.18 KB
/
blist.c
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
/*
* $Id: blist.c,v 1.13 2010/04/30 22:59:27 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) + ((UINT) (inx) * data->itemSize))
#define ItemToInx(data, item) \
((UINT) ((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;
for (n = 0; 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, (size_t) last, data->itemSize, exact_match);
if (check != 0) {
rc = (int) 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 = (int) strlen(name);
int rc = -1;
int hi, lo, x0, x1, cmp;
int last = blist_count(data);
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, (size_t) 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),
(size_t) (x1 + 1 - lo),
data->itemSize,
exact_match);
if (test) {
rc = (int) 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, (size_t) 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
}