8 #include <rpm/rpmstring.h>
9 #include "rpmio/rpmhook.h"
13 #define RPMHOOK_TABLE_INITSIZE 256
14 #define RPMHOOK_BUCKET_INITSIZE 5
16 typedef struct rpmhookItem_s {
19 struct rpmhookItem_s *next;
22 typedef struct rpmhookBucket_s {
28 typedef struct rpmhookTable_s {
31 struct rpmhookBucket_s bucket[1];
35 rpmhookArgs rpmhookArgsNew(int argc)
37 rpmhookArgs args = (rpmhookArgs) xcalloc(1,
38 sizeof(*args) + sizeof(args->argv) * (argc-1));
43 rpmhookArgs rpmhookArgsFree(rpmhookArgs args)
50 static rpmhookTable rpmhookTableNew(int size)
52 rpmhookTable table = (rpmhookTable) xcalloc(1,
53 sizeof(*table) + sizeof(table->bucket) * (size-1));
59 static rpmhookTable rpmhookTableFree(rpmhookTable table)
61 rpmhookItem item, nextItem;
63 for (i = 0; i != table->size; i++) {
64 if (table->bucket[i].name == NULL)
66 free(table->bucket[i].name);
67 item = table->bucket[i].item;
69 nextItem = item->next;
79 static void rpmhookTableRehash(rpmhookTable *table);
81 static int rpmhookTableFindBucket(rpmhookTable *table, const char *name)
83 /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */
84 unsigned long perturb;
85 unsigned long hash = 0;
86 unsigned char *bp = (unsigned char *)name;
87 unsigned char *be = bp + strlen(name);
91 if (((*table)->used/2)*3 > (*table)->size)
92 rpmhookTableRehash(table);
94 hash ^= (unsigned long)*bp++;
95 hash *= (unsigned long)0x01000193;
98 ret = hash % (*table)->size;
99 bucket = &(*table)->bucket[ret];
100 while (bucket->name &&
101 (bucket->hash != hash || !rstreq(bucket->name, name))) {
102 /* Collision resolution based on Python's perturb scheme. */
103 ret = ((ret << 2) + ret + perturb + 1) % (*table)->size;
105 bucket = &(*table)->bucket[ret];
112 static void rpmhookTableRehash(rpmhookTable *table)
114 rpmhookTable newtable = rpmhookTableNew((*table)->size*2);
117 for (; i != (*table)->size; i++) {
118 if ((*table)->bucket[i].name == NULL)
120 n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
121 newtable->bucket[n].name = (*table)->bucket[i].name;
122 newtable->bucket[n].item = (*table)->bucket[i].item;
124 newtable->used = (*table)->used;
129 static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
130 rpmhookFunc func, void *data)
132 int n = rpmhookTableFindBucket(table, name);
133 rpmhookBucket bucket = &(*table)->bucket[n];
134 rpmhookItem *item = &bucket->item;
136 bucket->name = xstrdup(name);
139 while (*item) item = &(*item)->next;
140 *item = xcalloc(1, sizeof(**item));
141 (*item)->func = func;
142 (*item)->data = data;
145 static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
146 rpmhookFunc func, void *data,
147 int matchfunc, int matchdata)
149 int n = rpmhookTableFindBucket(table, name);
150 rpmhookBucket bucket = &(*table)->bucket[n];
151 rpmhookItem item = bucket->item;
152 rpmhookItem lastItem = NULL;
153 rpmhookItem nextItem;
155 nextItem = item->next;
156 if ((!matchfunc || item->func == func) &&
157 (!matchdata || item->data == data)) {
160 lastItem->next = nextItem;
162 bucket->item = nextItem;
175 static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
177 rpmhookArgs args = rpmhookArgsNew(strlen(argt));
181 for (i = 0; i != args->argc; i++) {
184 args->argv[i].s = va_arg(ap, char *);
187 args->argv[i].i = va_arg(ap, int);
190 args->argv[i].f = (float)va_arg(ap, double);
193 args->argv[i].p = va_arg(ap, void *);
196 fprintf(stderr, "error: unsupported type '%c' as "
197 "a hook argument\n", argt[i]);
204 static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
207 int n = rpmhookTableFindBucket(table, name);
208 rpmhookItem item = (*table)->bucket[n].item;
212 if (item->func(args, item->data) != 0)
218 static rpmhookTable globalTable = NULL;
220 void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
222 if (globalTable == NULL)
223 globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
224 rpmhookTableAddItem(&globalTable, name, func, data);
227 void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
229 if (globalTable != NULL)
230 rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
233 void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
235 if (globalTable != NULL)
236 rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
239 void rpmhookUnregisterAll(const char *name)
241 if (globalTable != NULL)
242 rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
245 void rpmhookCall(const char *name, const char *argt, ...)
247 if (globalTable != NULL) {
251 args = rpmhookArgsParse(argt, ap);
252 rpmhookTableCallArgs(&globalTable, name, args);
253 (void) rpmhookArgsFree(args);
258 void rpmhookCallArgs(const char *name, rpmhookArgs args)
260 if (globalTable != NULL)
261 rpmhookTableCallArgs(&globalTable, name, args);