rpmio/rpmhook.c

Go to the documentation of this file.
00001 /*@-bounds@*/
00002 #include "system.h"
00003 
00004 #include <stdlib.h>
00005 #include <stdio.h>
00006 #include <string.h>
00007 #include <stdarg.h>
00008 
00009 #include <rpmhook.h>
00010 
00011 #define RPMHOOK_TABLE_INITSIZE  256
00012 #define RPMHOOK_BUCKET_INITSIZE 5
00013 
00014 typedef struct rpmhookItem_s {
00015     rpmhookFunc func;
00016     void *data;
00017     struct rpmhookItem_s *next;
00018 } * rpmhookItem;
00019 
00020 typedef struct rpmhookBucket_s {
00021     unsigned long hash;
00022 /*@relnull@*/
00023     char *name;
00024     rpmhookItem item;
00025 } * rpmhookBucket;
00026 
00027 typedef struct rpmhookTable_s {
00028     int size;
00029     int used;
00030     struct rpmhookBucket_s bucket[1];
00031 } * rpmhookTable;
00032 
00033 
00034 rpmhookArgs rpmhookArgsNew(int argc)
00035 {
00036     rpmhookArgs args = (rpmhookArgs) xcalloc(1,
00037                         sizeof(*args) + sizeof(args->argv) * (argc-1));
00038     args->argc = argc;
00039     return args;
00040 }
00041 
00042 rpmhookArgs rpmhookArgsFree(rpmhookArgs args)
00043 {
00044     if (args != NULL)
00045         free(args);
00046     return NULL;
00047 }
00048 
00049 /*@only@*/
00050 static rpmhookTable rpmhookTableNew(int size)
00051         /*@*/
00052 {
00053     rpmhookTable table = (rpmhookTable) xcalloc(1,
00054                 sizeof(*table) + sizeof(table->bucket) * (size-1));
00055     table->size = size;
00056     return table;
00057 }
00058 
00059 #if 0
00060 static rpmhookTable rpmhookTableFree(rpmhookTable table)
00061         /*@*/
00062 {
00063     rpmhookItem item, nextItem;
00064     int i;
00065     for (i = 0; i != table->size; i++) {
00066         if (table->bucket[i].name == NULL)
00067             continue;
00068         free(table->bucket[i].name);
00069         item = table->bucket[i].item;
00070         while (item) {
00071             nextItem = item->next;
00072             free(item);
00073             item = nextItem;
00074         }
00075     }
00076     free(table);
00077     return NULL;
00078 }
00079 #endif
00080 
00081 static void rpmhookTableRehash(rpmhookTable *table)
00082         /*@modifies *table @*/;
00083 
00084 static int rpmhookTableFindBucket(rpmhookTable *table, const char *name)
00085         /*@modifies *table @*/
00086 {
00087     /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */
00088     unsigned long perturb;
00089     unsigned long hash = 0;
00090     unsigned char *bp = (unsigned char *)name;
00091     unsigned char *be = bp + strlen(name);
00092     rpmhookBucket bucket;
00093     int ret;
00094 
00095     if (((*table)->used/2)*3 > (*table)->size)
00096         rpmhookTableRehash(table);
00097     while (bp < be) {
00098         hash ^= (unsigned long)*bp++;
00099         hash *= (unsigned long)0x01000193;
00100     }
00101     perturb = hash;
00102     ret = hash % (*table)->size;
00103     bucket = &(*table)->bucket[ret];
00104     while (bucket->name &&
00105             (bucket->hash != hash || strcmp(bucket->name, name) != 0)) {
00106         /* Collision resolution based on Python's perturb scheme. */
00107 /*@-shiftimplementation@*/
00108         ret = ((ret << 2) + ret + perturb + 1) % (*table)->size;
00109 /*@=shiftimplementation@*/
00110         perturb >>= 5;
00111         bucket = &(*table)->bucket[ret];
00112     }
00113     if (!bucket->name)
00114         bucket->hash = hash;
00115     return ret;
00116 }
00117 
00118 static void rpmhookTableRehash(rpmhookTable *table)
00119         /*@modifies *table @*/
00120 {
00121     rpmhookTable newtable = rpmhookTableNew((*table)->size*2);
00122     int n, i = 0;
00123 
00124 /*@-branchstate@*/
00125     for (; i != (*table)->size; i++) {
00126         if ((*table)->bucket[i].name == NULL)
00127             continue;
00128         n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
00129         newtable->bucket[n].name = (*table)->bucket[i].name;
00130         newtable->bucket[n].item = (*table)->bucket[i].item;
00131     }
00132 /*@=branchstate@*/
00133     newtable->used = (*table)->used;
00134 /*@-unqualifiedtrans@*/
00135     free(*table);
00136 /*@=unqualifiedtrans@*/
00137     *table = newtable;
00138 }
00139 
00140 static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
00141                                 rpmhookFunc func, void *data)
00142         /*@modifies *table @*/
00143 {
00144     int n = rpmhookTableFindBucket(table, name);
00145     rpmhookBucket bucket = &(*table)->bucket[n];
00146     rpmhookItem *item = &bucket->item;
00147     if (!bucket->name) {
00148         bucket->name = strdup(name);
00149         (*table)->used++;
00150     }
00151     while (*item) item = &(*item)->next;
00152     *item = xcalloc(1, sizeof(**item));
00153     (*item)->func = func;
00154 /*@-temptrans@*/
00155     (*item)->data = data;
00156 /*@=temptrans@*/
00157 }
00158 
00159 static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
00160                 /*@null@*/ rpmhookFunc func, /*@null@*/ void *data,
00161                 int matchfunc, int matchdata)
00162         /*@modifies *table @*/
00163 {
00164     int n = rpmhookTableFindBucket(table, name);
00165     rpmhookBucket bucket = &(*table)->bucket[n];
00166     rpmhookItem item = bucket->item;
00167     rpmhookItem lastItem = NULL;
00168     rpmhookItem nextItem;
00169     while (item) {
00170         nextItem = item->next;
00171 /*@-branchstate@*/
00172         if ((!matchfunc || item->func == func) &&
00173             (!matchdata || item->data == data)) {
00174             free(item);
00175             if (lastItem)
00176                 lastItem->next = nextItem;
00177             else
00178                 bucket->item = nextItem;
00179         } else {
00180             lastItem = item;
00181         }
00182 /*@=branchstate@*/
00183 /*@-usereleased@*/
00184         item = nextItem;
00185     }
00186     if (!bucket->item) {
00187         free(bucket->name);
00188         bucket->name = NULL;
00189         (*table)->used--;
00190     }
00191 /*@=usereleased@*/
00192 }
00193 
00194 static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
00195         /*@*/
00196 {
00197     rpmhookArgs args = rpmhookArgsNew(strlen(argt));
00198     int i;
00199 
00200 /*@-temptrans@*/
00201     args->argt = argt;
00202 /*@=temptrans@*/
00203     for (i = 0; i != args->argc; i++) {
00204         switch (argt[i]) {
00205             case 's':
00206                 args->argv[i].s = va_arg(ap, char *);
00207                 /*@switchbreak@*/ break;
00208             case 'i':
00209                 args->argv[i].i = va_arg(ap, int);
00210                 /*@switchbreak@*/ break;
00211             case 'f':
00212                 args->argv[i].f = (float)va_arg(ap, double);
00213                 /*@switchbreak@*/ break;
00214             case 'p':
00215                 args->argv[i].p = va_arg(ap, void *);
00216                 /*@switchbreak@*/ break;
00217             default:
00218 /*@-modfilesys @*/
00219                 fprintf(stderr, "error: unsupported type '%c' as "
00220                                 "a hook argument\n", argt[i]);
00221 /*@=modfilesys @*/
00222                 /*@switchbreak@*/ break;
00223         }
00224     }
00225     return args;
00226 }
00227 
00228 static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
00229                         rpmhookArgs args)
00230         /*@modifies *table @*/
00231 {
00232     int n = rpmhookTableFindBucket(table, name);
00233     rpmhookItem item = (*table)->bucket[n].item;
00234     while (item) {
00235         if (item->func(args, item->data) != 0)
00236             break;
00237         item = item->next;
00238     }
00239 }
00240 
00241 /*@unchecked@*/ /*@only@*/ /*@null@*/
00242 static rpmhookTable globalTable = NULL;
00243 
00244 void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
00245         /*@globals globalTable @*/
00246         /*@modifies globalTable @*/
00247 {
00248     if (globalTable == NULL)
00249         globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
00250     rpmhookTableAddItem(&globalTable, name, func, data);
00251 }
00252 
00253 void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
00254 {
00255     if (globalTable != NULL)
00256         rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
00257 }
00258 
00259 void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
00260 {
00261     if (globalTable != NULL)
00262         rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
00263 }
00264 
00265 void rpmhookUnregisterAll(const char *name)
00266 {
00267     if (globalTable != NULL)
00268         rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
00269 }
00270 
00271 void rpmhookCall(const char *name, const char *argt, ...)
00272 {
00273     if (globalTable != NULL) {
00274         rpmhookArgs args;
00275         va_list ap;
00276         va_start(ap, argt);
00277         args = rpmhookArgsParse(argt, ap);
00278 /*@-noeffect@*/
00279         rpmhookTableCallArgs(&globalTable, name, args);
00280 /*@=noeffect@*/
00281         (void) rpmhookArgsFree(args);
00282         va_end(ap);
00283     }
00284 }
00285 
00286 void rpmhookCallArgs(const char *name, rpmhookArgs args)
00287 {
00288 /*@-noeffect@*/
00289     if (globalTable != NULL)
00290         rpmhookTableCallArgs(&globalTable, name, args);
00291 /*@=noeffect@*/
00292 }
00293 /*@=bounds@*/

Generated on Tue Feb 19 22:26:36 2008 for rpm by  doxygen 1.5.1