Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/source/alist.c
1069 views
1
/*
2
* alist.c -- resizeable arrays.
3
* Written by Jeremy Nelson
4
* Copyright 1997 EPIC Software Labs
5
*
6
* This file presumes a good deal of chicanery. Specifically, it assumes
7
* that your compiler will allocate disparate structures congruently as
8
* long as the members match as to their type and location. This is
9
* critically important for how this code works, and all hell will break
10
* loose if your compiler doesnt do this. Every compiler i know of does
11
* it, which is why im assuming it, even though im not allowed to assume it.
12
*
13
* This file is hideous. Ill kill each and every one of you who made
14
* me do this. ;-)
15
*/
16
17
#define _cs_alist_hash_
18
#define _ci_alist_hash_
19
#include "irc.h"
20
static char cvsrevision[] = "$Id: alist.c 107 2011-02-02 12:11:03Z keaston $";
21
CVS_REVISION(alist_c)
22
#include "alist.h"
23
#include "ircaux.h"
24
#include "output.h"
25
#define MAIN_SOURCE
26
#include "modval.h"
27
28
u_32int_t bin_ints = 0;
29
u_32int_t lin_ints = 0;
30
u_32int_t bin_chars = 0;
31
u_32int_t lin_chars = 0;
32
u_32int_t alist_searches = 0;
33
u_32int_t char_searches = 0;
34
35
36
#define ARRAY_ITEM(array, loc) ((Array_item *) ((array) -> list [ (loc) ]))
37
#define LARRAY_ITEM(array, loc) (((array) -> list [ (loc) ]))
38
39
/* Function decls */
40
static void check_array_size (Array *list);
41
void move_array_items (Array *list, int start, int end, int dir);
42
43
/*
44
* Returns an entry that has been displaced, if any.
45
*/
46
Array_item *BX_add_to_array (Array *array, Array_item *item)
47
{
48
int count;
49
int location = 0;
50
Array_item *ret = NULL;
51
u_32int_t mask; /* Dummy var */
52
53
if (array->hash == HASH_INSENSITIVE)
54
item->hash = ci_alist_hash(item->name, &mask);
55
else
56
item->hash = cs_alist_hash(item->name, &mask);
57
58
check_array_size(array);
59
if (array->max)
60
{
61
find_array_item(array, item->name, &count, &location);
62
if (count < 0)
63
{
64
ret = ARRAY_ITEM(array, location);
65
array->max--;
66
}
67
else
68
move_array_items(array, location, array->max, 1);
69
}
70
71
array->list[location] = item;
72
array->max++;
73
return ret;
74
}
75
76
/*
77
* Returns the entry that has been removed, if any.
78
*/
79
Array_item *BX_remove_from_array (Array *array, char *name)
80
{
81
int count, location = 0;
82
83
if (array->max)
84
{
85
find_array_item(array, name, &count, &location);
86
if (count >= 0)
87
return NULL;
88
89
return array_pop(array, location);
90
}
91
return NULL; /* Cant delete whats not there */
92
}
93
94
/* Remove the 'which'th item from the given array */
95
Array_item *BX_array_pop (Array *array, int which)
96
{
97
Array_item *ret = NULL;
98
99
if (which < 0 || which >= array->max)
100
return NULL;
101
102
ret = ARRAY_ITEM(array, which);
103
move_array_items(array, which + 1, array->max, -1);
104
array->max--;
105
return ret;
106
}
107
108
/*
109
* Returns the entry that has been removed, if any.
110
*/
111
Array_item *BX_remove_all_from_array (Array *array, char *name)
112
{
113
int count, location = 0;
114
Array_item *ret = NULL;
115
116
if (array->max)
117
{
118
find_array_item(array, name, &count, &location);
119
if (count == 0)
120
return NULL;
121
ret = ARRAY_ITEM(array, location);
122
move_array_items(array, location + 1, array->max, -1);
123
array->max--;
124
return ret;
125
}
126
return NULL; /* Cant delete whats not there */
127
}
128
129
Array_item *BX_array_lookup (Array *array, char *name, int wild, int delete)
130
{
131
int count, location;
132
133
if (delete)
134
return remove_from_array(array, name);
135
else
136
return find_array_item(array, name, &count, &location);
137
}
138
139
static void check_array_size (Array *array)
140
{
141
if (array->total_max == 0)
142
array->total_max = 6; /* Good size to start with */
143
else if (array->max == array->total_max-1)
144
array->total_max *= 2;
145
else if (array->max * 3 < array->total_max)
146
array->total_max /= 2;
147
else
148
return;
149
150
/*yell("Resizing...");*/
151
RESIZE(array->list, Array_item *, array->total_max);
152
}
153
154
/*
155
* Move ``start'' through ``end'' array elements ``dir'' places up
156
* in the array. If ``dir'' is negative, move them down in the array.
157
* Fill in the vacated spots with NULLs.
158
*/
159
void move_array_items (Array *array, int start, int end, int dir)
160
{
161
int i;
162
163
if (dir > 0)
164
{
165
for (i = end; i >= start; i--)
166
LARRAY_ITEM(array, i + dir) = ARRAY_ITEM(array, i);
167
for (i = dir; i > 0; i--)
168
LARRAY_ITEM(array, start + i - 1) = NULL;
169
}
170
else if (dir < 0)
171
{
172
for (i = start; i <= end; i++)
173
LARRAY_ITEM(array, i + dir) = ARRAY_ITEM(array, i);
174
for (i = end - dir + 1; i <= end; i++)
175
LARRAY_ITEM(array, i) = NULL;
176
}
177
}
178
179
180
/*
181
* This is just a generalization of the old function ``find_command''
182
*
183
* You give it an alist_item and a name, and it gives you the number of
184
* items that are completed by that name, and where you could find/put that
185
* item in the list. It returns the alist_item most appropriate.
186
*
187
* If ``cnt'' is less than -1, then there was one exact match and one or
188
* more ambiguous matches in addition. The exact match's location
189
* is put into ``loc'' and its entry is returned. The ambigous matches
190
* are (of course) immediately subsequent to it.
191
*
192
* If ``cnt'' is -1, then there was one exact match. Its location is
193
* put into ``loc'' and its entry is returned.
194
*
195
* If ``cnt'' is zero, then there were no matches for ``name'', but ``loc''
196
* is set to the location in the array in which you could place the
197
* specified name in the sorted list.
198
*
199
* If ``cnt'' is one, then there was one command that non-ambiguously
200
* completes ``name''
201
*
202
* If ``cnt'' is greater than one, then there was exactly ``cnt'' number
203
* of entries that completed ``name'', but they are all ambiguous.
204
* The entry that is lowest alphabetically is returned, and its
205
* location is put into ``loc''.
206
*/
207
Array_item *BX_find_array_item (Array *set, char *name, int *cnt, int *loc)
208
{
209
size_t len = strlen(name);
210
int c = 0,
211
pos = 0,
212
min,
213
max;
214
u_32int_t mask, hash;
215
216
if (set->hash == HASH_INSENSITIVE)
217
hash = ci_alist_hash(name, &mask);
218
else
219
hash = cs_alist_hash(name, &mask);
220
221
*cnt = 0;
222
if (!set->list || !set->max)
223
{
224
*loc = 0;
225
return NULL;
226
}
227
228
alist_searches++;
229
max = set->max - 1;
230
min = 0;
231
232
while (max >= min)
233
{
234
bin_ints++;
235
pos = (max - min) / 2 + min;
236
c = (hash & mask) - (ARRAY_ITEM(set, pos)->hash & mask);
237
if (c == 0)
238
break;
239
else if (c < 0)
240
max = pos - 1;
241
else
242
min = pos + 1;
243
}
244
245
/*
246
* If we can't find a symbol that qualifies, then we can just drop
247
* out here. This is good because a "pass" (lookup for a symbol that
248
* does not exist) requires only cheap integer comparisons.
249
*/
250
if (c != 0)
251
{
252
if (c > 0)
253
*loc = pos + 1;
254
else
255
*loc = pos;
256
return NULL;
257
}
258
259
/*
260
* Now we've found some symbol that has the same first four letters.
261
* Expand the min/max range to include all of the symbols that have
262
* the same first four letters...
263
*/
264
min = max = pos;
265
while ((min > 0) && (hash & mask) == (ARRAY_ITEM(set, min)->hash & mask))
266
min--, lin_ints++;
267
while ((max < set->max - 1) && (hash &mask) == (ARRAY_ITEM(set, max)->hash & mask))
268
max++, lin_ints++;
269
270
char_searches++;
271
272
/*
273
* Then do a full blown binary search on the smaller range
274
*/
275
while (max >= min)
276
{
277
bin_chars++;
278
pos = (max - min) / 2 + min;
279
c = set->func(name, ARRAY_ITEM(set, pos)->name, len);
280
if (c == 0)
281
break;
282
else if (c < 0)
283
max = pos - 1;
284
else
285
min = pos + 1;
286
}
287
288
289
if (c != 0)
290
{
291
if (c > 0)
292
*loc = pos + 1;
293
else
294
*loc = pos;
295
return NULL;
296
}
297
298
/*
299
* If we've gotten this far, then we've found at least
300
* one appropriate entry. So we set *cnt to one
301
*/
302
*cnt = 1;
303
304
/*
305
* So we know that 'pos' is a match. So we start 'min'
306
* at one less than 'pos' and walk downard until we find
307
* an entry that is NOT matched.
308
*/
309
min = pos - 1;
310
while (min >= 0 && !set->func(name, ARRAY_ITEM(set, min)->name, len))
311
(*cnt)++, min--, lin_chars++;
312
min++;
313
314
/*
315
* Repeat the same ordeal, except this time we walk upwards
316
* from 'pos' until we dont find a match.
317
*/
318
max = pos + 1;
319
while (max < set->max && !set->func(name, ARRAY_ITEM(set, max)->name, len))
320
(*cnt)++, max++, lin_chars++;
321
322
/*
323
* Alphabetically, the string that would be identical to
324
* 'name' would be the first item in a string of items that
325
* all began with 'name'. That is to say, if there is an
326
* exact match, its sitting under item 'min'. So we check
327
* for that and whack the count appropriately.
328
*/
329
if (strlen(ARRAY_ITEM(set, min)->name) == len)
330
*cnt *= -1;
331
332
/*
333
* Then we tell the caller where the lowest match is,
334
* in case they want to insert here.
335
*/
336
if (loc)
337
*loc = min;
338
339
/*
340
* Then we return the first item that matches.
341
*/
342
return ARRAY_ITEM(set, min);
343
}
344
345
#define FIXED_ITEM(list, pos, size) (*(Array_item *)((char *)list + ( pos * size )))
346
347
/*
348
* This is useful for finding items in a fixed array (eg, those lists that
349
* are a simple fixed length arrays of 1st level structs.)
350
*
351
* This code is identical to find_array_item except ``list'' is a 1st
352
* level array instead of a 2nd level array.
353
*/
354
void * BX_find_fixed_array_item (void *list, size_t size, int howmany, char *name, int *cnt, int *loc)
355
{
356
int len = strlen(name),
357
min = 0,
358
max = howmany,
359
old_pos = -1,
360
pos,
361
c;
362
363
*cnt = 0;
364
365
while (1)
366
{
367
pos = (max + min) / 2;
368
if (pos == old_pos)
369
{
370
*loc = pos;
371
return NULL;
372
}
373
old_pos = pos;
374
375
c = strncmp(name, FIXED_ITEM(list, pos, size).name, len);
376
if (c == 0)
377
break;
378
else if (c > 0)
379
min = pos;
380
else
381
max = pos;
382
}
383
384
/*
385
* If we've gotten this far, then we've found at least
386
* one appropriate entry. So we set *cnt to one
387
*/
388
*cnt = 1;
389
390
/*
391
* So we know that 'pos' is a match. So we start 'min'
392
* at one less than 'pos' and walk downard until we find
393
* an entry that is NOT matched.
394
*/
395
min = pos - 1;
396
while (min >= 0 && !strncmp(name, FIXED_ITEM(list, min, size).name, len))
397
(*cnt)++, min--;
398
min++;
399
400
/*
401
* Repeat the same ordeal, except this time we walk upwards
402
* from 'pos' until we dont find a match.
403
*/
404
max = pos + 1;
405
while ((max < howmany) && !strncmp(name, FIXED_ITEM(list, max, size).name, len))
406
(*cnt)++, max++;
407
408
/*
409
* Alphabetically, the string that would be identical to
410
* 'name' would be the first item in a string of items that
411
* all began with 'name'. That is to say, if there is an
412
* exact match, its sitting under item 'min'. So we check
413
* for that and whack the count appropriately.
414
*/
415
if (strlen(FIXED_ITEM(list, min, size).name) == len)
416
*cnt *= -1;
417
418
/*
419
* Then we tell the caller where the lowest match is,
420
* in case they want to insert here.
421
*/
422
if (loc)
423
*loc = min;
424
425
/*
426
* Then we return the first item that matches.
427
*/
428
return (void *)&FIXED_ITEM(list, min, size);
429
}
430
431
432
433