hash.c

Go to the documentation of this file.
00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <assert.h>
00005 
00006 #include "str.h"
00007 #include "cser.h"
00008 
00009 #define __HASH__IMPLEMENTATION__
00010 #include "hash.h"
00011 
00012 /**
00013  * \file hash.c Implementa??o de um hash gen?rico. Este hash usa lista 
00014  * encadeada e permite edi??o do valor de uma chave. Para lidar com varia??es 
00015  * nos tipos de dados sem perder efici?ncia, o tipo da chave (pode ser string 
00016  * ou inteiro) e o tipo do valor (pode ser inteiro ou void *) s?o definidos 
00017  * atrav?s de ifdefs. A cada tipo de hash mudam os nomes dos tipos abstratos de
00018  * dados e das fun??es para que diferentes tipos de hash possam ser usados 
00019  * dentro de um mesmo arquivo fonte, mas os algoritmos s?o os mesmos para todos
00020  * os tipos(exceto que se chave ? um string deve-se chamar str_hash_code() para
00021  * calcular o hash code, se ela ? inteiro ela ? seu pr?prio hash code). As 
00022  * fun??es desse arquivo t?m nomes como HASH_CREATE que o preprocessador do C 
00023  * substitui por hashKVCreate onde K ? o tipo da chave que pode ser Int(inteiro)
00024  * ou Str(String) e V ? o tipo do valor que pode ser Int(inteiro) ou Void 
00025  * (void *). Nessa documenta??o e de hash.h K ? Str e V ? Int.
00026  *
00027  * Complexidade das opera??es:
00028  * 
00029  * C?lculo do hashcode: A complexidade do c?lculo do hashcode ? O(n) onde n ? o tamanho do da chave 
00030  * (se ela for string). Mas como as chaves t?m tamanho m?dio de uns 10 
00031  * caracteres e poucas devem ter 20 caracteres, a complexidade do c?lculo do 
00032  * hashcode pode ser considerada O(1).
00033  * 
00034  * Inser??o na lista encadeada de uma posi??o do hash: O(n) onde n ? o tamanho da lista.
00035  *
00036  * Inser??o no hash: Para inserir uma chave no hash ? necess?rio calcular o
00037  * hashcode dessa chave e inser?-la na lista encadeada de uma posi??o do hash.
00038  * O c?lculo do hashcode ? O(1), enquanto a inser??o na lista ? O(n) onde n ?
00039  * o tamanho da lista. Como o tamanho da lista ? 1 se o n?mero de chaves n?o for 
00040  * muito maior que o n?mero de posi??es da tabela hash a complexidade esperada 
00041  * de inser??o no hash ? O(1).
00042  * 
00043  *      \sa hash.h
00044  */ 
00045 
00046 
00047 /*----------------------------- hash operations -----------------------------*/
00048 
00049 #ifdef KEY_INT
00050 #define hashCode(x) (x)
00051 #define keyEqual(a,b) (a) == (b)
00052 #define keyCopy(d,s) (d) = (s)
00053 #define destroyKey(k)
00054 #elif defined(KEY_STRING)
00055 #define hashCode(x) str_hash_code(x) ///< Calcula o hash code da chave x
00056 #define keyEqual(a,b) (!strcmp(a,b)) ///< Verify if two keys are equal
00057 #define keyCopy(d,s) (d) = strdup(s) ///< Faz d ser uma copia da chave s
00058 #define destroyKey(k) free(k) ///< Destroy a key
00059 #elif defined(KEY_DSI)
00060 #define hashCode(x) (abs(x.work*1000000+x.task))
00061 #define keyEqual(a,b) ( ((a).work==(b).work) && ((a).task==(b).task) )
00062 #define keyCopy(d,s) (d) = (s)
00063 #define destroyKey(k)
00064 #else
00065 #error "Not implemented"
00066 #endif
00067 
00068 /// Define o que colocar no campo valor caso n?o haja valor definido.
00069 #ifdef VAL_INT
00070 #define NO_VALUE -1
00071 #endif
00072 #ifdef VAL_VOID
00073 #define NO_VALUE NULL
00074 #endif
00075 #ifdef VAL_FLOAT
00076 #define NO_VALUE 0
00077 #endif
00078 
00079 #ifdef KEY_INT
00080 #define KEY_NULL -1
00081 #elif defined(KEY_STRING)
00082 #define KEY_NULL NULL
00083 #elif defined(KEY_DSI)
00084 #define KEY_NULL {-1, -1}
00085 #endif
00086 
00087 
00088 
00089 //-----------------------------------------------------------------------------
00090 /** Cria um hash.
00091         \param sz Tamanho do hash.
00092         \return Ponteiro para o hash.
00093 */
00094 //-----------------------------------------------------------------------------
00095 HASH_TYPE *HASH_CREATE(int sz) {
00096         HASH_TYPE *hash  = (HASH_TYPE *)malloc(sizeof(HASH_TYPE));
00097         hash->tam      = sz;
00098         hash->colisoes = 0;
00099         hash->chaves   = 0;
00100         if (sz > 0) {
00101                 hash->entradas = (LISTA_TYPE **)calloc(sz, sizeof(LISTA_TYPE *)); //Create and zeroes all entries
00102         } else {
00103                 hash->entradas = NULL;
00104         }
00105 #ifdef VAL_VOID
00106         hash->serValVoid = NULL;
00107         hash->deserValVoid = NULL;
00108 #endif
00109         
00110         return hash;
00111 }
00112 
00113 //-----------------------------------------------------------------------------
00114 /** Insere uma chave no hash (caso ela n?o exista).
00115         \param h Hash onde a chave ser? inserida.
00116         \param key Chave a ser inserida (se a chave for um string, ele ser? copiado).
00117         \return Handler para edi??o da posi??o.
00118 */
00119 //-----------------------------------------------------------------------------
00120 POS_HANDLER_TYPE HASH_ADD(HASH_TYPE *h, KEY_TYPE key) {
00121         if (h->tam < 3) {
00122                 HASH_REHASH(h, 3); // 101 is a prime number
00123         } else {
00124                 float collisions = (float)h->colisoes;
00125                 float size = (float)h->tam;
00126                 
00127                 if ( (collisions/size) > MAX_COLISIONS_FACTOR) {
00128                         float newSize = size * REHASH_MULTIPLIER;                       
00129                         HASH_REHASH(h, (int)newSize);
00130                 }
00131         }
00132                 
00133         int pos = hashCode(key) % (h->tam);
00134         LISTA_TYPE **ptr_lista = &(h->entradas[pos]);
00135         LISTA_TYPE *ptr_item = NULL;
00136 
00137         for (; (*ptr_lista)!=NULL; ptr_lista = &((*ptr_lista)->next) ) {
00138                 if (keyEqual( (*ptr_lista)->key, key )) {
00139                         // ja tinha essa chave no hash
00140                         return *ptr_lista;
00141                 }       
00142         }
00143 
00144         // Nova entrada no hash
00145         h->chaves++;
00146 
00147         // Se a chave n?o existia no hash e tem outra chave nessa posi??o,
00148         // houve uma colis?o.
00149         if (h->entradas[pos] != NULL) {
00150                 h->colisoes++;
00151         }
00152 
00153         ptr_item = (LISTA_TYPE *) malloc(sizeof(LISTA_TYPE));
00154         keyCopy(ptr_item->key, key);
00155         ptr_item->val  = NO_VALUE;
00156         ptr_item->next = NULL;
00157 
00158         *ptr_lista = ptr_item;
00159         return ptr_item;
00160 }
00161 
00162 //-----------------------------------------------------------------------------
00163 /// Retorna um Handler da posi??o da chave key no hash h. 
00164 //-----------------------------------------------------------------------------
00165 POS_HANDLER_TYPE HASH_GET(HASH_TYPE *h, KEY_TYPE key) {
00166         if (h->tam > 0) {
00167                 int pos = hashCode(key) % (h->tam);
00168                 LISTA_TYPE *ptr_lista = h->entradas[pos];
00169 
00170                 for (; ptr_lista!=NULL; ptr_lista = ptr_lista->next) {
00171                         if (keyEqual( ptr_lista->key, key )) {
00172                                 // essa existe chave no hash
00173                                 return ptr_lista;
00174                         }
00175                 }
00176         }
00177 
00178         return NULL;    
00179 }
00180 
00181 //-----------------------------------------------------------------------------
00182 /// Removes a key from the hash. 
00183 ///             \param h Hash where the key will be removed.
00184 ///             \param removedKey Key to be removed.
00185 ///             \return 0 on success, -1 on error.
00186 //-----------------------------------------------------------------------------
00187 int HASH_REMOVE(HASH_TYPE *h, KEY_TYPE removedKey) {
00188 #ifndef VAL_INT
00189         if (h == NULL) return -1;
00190 #endif
00191         if (h->tam > 0) {
00192                 int pos = hashCode(removedKey) % (h->tam);
00193                 LISTA_TYPE *ptr_lista = h->entradas[pos];
00194                 LISTA_TYPE **ptr_ant = &(h->entradas[pos]);
00195 
00196                 /// \todo Test removal on the begining, middle and end of a >3 node linked list
00197                 while (ptr_lista != NULL) {
00198                         if (keyEqual( ptr_lista->key, removedKey )) {
00199                                 *ptr_ant = ptr_lista->next;
00200                                 free(ptr_lista);
00201                                 h->chaves--;
00202                                 return 0;
00203                         }
00204                         
00205                         ptr_ant   = &(ptr_lista->next);
00206                         ptr_lista = ptr_lista->next;
00207                 }       
00208                 return -1;
00209         }
00210         return -1;
00211 }
00212 
00213 //-----------------------------------------------------------------------------
00214 /// Destroy a hash.
00215 //-----------------------------------------------------------------------------
00216 void HASH_DESTROY(HASH_TYPE *h) {
00217         int i;
00218 
00219         for (i=0; i<h->tam; i++) {
00220                 if (h->entradas[i] != NULL) {
00221                         LISTA_DESTROY(h->entradas[i]);
00222                 }
00223         }
00224 
00225         free(h->entradas);
00226         free(h);
00227 }
00228 
00229 //-----------------------------------------------------------------------------
00230 /// Limpa um hash, apagando todos os pares chave->valor.
00231 //-----------------------------------------------------------------------------
00232 void HASH_CLEAN(HASH_TYPE *h) {
00233         int i;
00234 
00235         for (i=0; i<h->tam; i++) {
00236                 if (h->entradas[i] != NULL) {
00237                         LISTA_DESTROY(h->entradas[i]);
00238                         h->entradas[i] = NULL;
00239                 }
00240         }
00241 }
00242 
00243 //-----------------------------------------------------------------------------
00244 /// Grow the hash to newSize
00245 //-----------------------------------------------------------------------------
00246 void HASH_REHASH(HASH_TYPE *h, int newSize) {
00247         LISTA_TYPE **newHash = (LISTA_TYPE **)calloc(newSize, sizeof(LISTA_TYPE *));
00248         ITERATOR_TYPE *oldHashIt = CREATE_ITERATOR(h, 1);
00249         POS_HANDLER_TYPE oldPos = ITERATOR_NEXT(oldHashIt, h);
00250         int collisions = 0;
00251         int newPos = -1;
00252 
00253         while (oldPos != NULL) {
00254                 newPos = hashCode(posGetKey(oldPos)) % (newSize);
00255                 LISTA_TYPE **ptr_lista = &(newHash[newPos]);
00256                 LISTA_TYPE *ptr_item = NULL;
00257 
00258                 while ((*ptr_lista)!=NULL) {
00259                         ptr_lista = &((*ptr_lista)->next);
00260                 }
00261 
00262                 // Se a chave n?o existia no hash e tem outra chave nessa posi??o,
00263                 // houve uma colis?o.
00264                 if (newHash[newPos] != NULL) {
00265                         collisions++;
00266                 }
00267 
00268                 ptr_item = (LISTA_TYPE *) malloc(sizeof(LISTA_TYPE));
00269                 keyCopy(ptr_item->key, posGetKey(oldPos));
00270                 ptr_item->val  = posGetValue(oldPos);
00271                 ptr_item->next = NULL;
00272 
00273                 *ptr_lista = ptr_item;
00274                 
00275                 
00276                 oldPos = ITERATOR_NEXT(oldHashIt, h);
00277         }
00278 
00279         if (h->entradas != NULL) {
00280                 free(h->entradas);
00281         }
00282         h->tam      = newSize;
00283         h->colisoes = collisions;
00284         h->entradas = newHash;
00285 }
00286 
00287 int HASH_SERIALIZE(FILE *outputFile, HASH_TYPE *hash){
00288         WRITE_NUM(outputFile, "numKeys", hash->chaves);
00289         
00290 #ifdef DEBUG    
00291         char header[] = "-#Hash-";
00292         WRITE_BYTES(outputFile, header, strlen(header));
00293 #endif
00294 
00295         ITERATOR_TYPE *it = CREATE_ITERATOR(hash, 0);
00296         POS_HANDLER_TYPE pos = NULL;
00297         for (pos = ITERATOR_NEXT(it, hash); pos != NULL; pos = ITERATOR_NEXT(it, hash)) {
00298                 WRITE_KEY(outputFile, hash, pos);
00299                 WRITE_VALUE(outputFile, hash, pos);
00300         }
00301         ITERATOR_DESTROY(it, hash);
00302 
00303 #ifdef DEBUG    
00304         char footer[] = "-#HashEnd-";
00305         WRITE_BYTES(outputFile, footer, strlen(footer));
00306 #endif
00307 
00308         return 0;
00309 }
00310 
00311 int HASH_DESERIALIZE(FILE *inputFile, HASH_TYPE *hash){
00312         int i = 0, chaves = -1;
00313         READ_BEGIN(inputFile);
00314         
00315         READ_NUM("numKeys", chaves);
00316         assert(chaves != -1);
00317 
00318         
00319 #ifdef DEBUG
00320         char header[] = "-#Hash-";
00321         char *buf = malloc(strlen(header)+1); // +1 to the \0
00322         READ_BYTES(inputFile, buf, strlen(header));
00323         buf[strlen(header)] = '\0';
00324         
00325         if (strcmp(header, buf)) {
00326                 // If header != buf, use assert to indicate the error
00327                 assert(0 == strcmp(header, buf));
00328         }
00329         free(buf);
00330 #endif
00331 
00332         POS_HANDLER_TYPE tmpPos = malloc(sizeof(LISTA_TYPE));
00333         POS_HANDLER_TYPE hashPos = NULL;
00334         for (i=0; i<chaves; i++) {
00335                 READ_KEY(inputFile, hash, tmpPos);
00336                 READ_VALUE(inputFile, hash, tmpPos);
00337 
00338                 hashPos = HASH_ADD(hash, posGetKey(tmpPos));
00339                 posSetValue(hashPos, posGetValue(tmpPos));
00340         }
00341         free(tmpPos);
00342         READ_END
00343 
00344 #ifdef DEBUG    
00345         char footer[] = "-#HashEnd-";
00346         // Needed to redeclare buf here because READ_BEGIN()...READ_END have a
00347         // block ( {} ).
00348         char *buf = (char *)malloc(strlen(footer)+1); // +1 to the \0
00349         READ_BYTES(inputFile, buf, strlen(footer));
00350         buf[strlen(footer)] = '\0';
00351 
00352         if (strcmp(footer, buf)) {
00353                 // If footer != buf, use assert to indicate the error
00354                 assert(0 == strcmp(footer, buf));
00355         }
00356         free(buf);
00357 #endif
00358         
00359         return 0;
00360 }
00361 
00362 
00363 int WRITE_KEY(FILE *outputFile, HASH_TYPE *hash, POS_HANDLER_TYPE pos) {
00364 
00365 #ifdef DEBUG    
00366         char header[] = "-#Tuple-";
00367         WRITE_BYTES(outputFile, header, strlen(header));
00368 #endif
00369 
00370 #ifdef KEY_INT
00371         WRITE_NUM(outputFile, "key", posGetKey(pos));
00372 #elif defined(KEY_STRING)
00373         WRITE_STR(outputFile, "key", posGetKey(pos));
00374 #elif defined(KEY_DSI)
00375         WRITE_NUM(outputFile, "work", posGetKey(pos).work);
00376         WRITE_NUM(outputFile, "task", posGetKey(pos).task);     
00377 #else 
00378 #error "Not implemented"
00379 #endif
00380         
00381         return 0;
00382 }
00383 
00384 
00385 int READ_KEY(FILE *inputFile, HASH_TYPE *hash, POS_HANDLER_TYPE pos){
00386         KEY_TYPE key = KEY_NULL;
00387                 
00388 #ifdef DEBUG
00389         char header[] = "-#Tuple-";
00390         char *buf = malloc(strlen(header)+1); // +1 to the \0
00391         READ_BYTES(inputFile, buf, strlen(header));
00392         buf[strlen(header)] = '\0';
00393         
00394         if (strcmp(header, buf)) {
00395                 // If header != buf, use assert to indicate the error
00396                 assert(0 == strcmp(header, buf));
00397         }
00398         free(buf);
00399 #endif
00400 
00401         READ_BEGIN(inputFile);
00402 #ifdef KEY_INT
00403         READ_NUM("key", key);
00404 #elif defined(KEY_STRING)
00405         READ_STR("key", key);
00406 #elif defined(KEY_DSI)
00407         READ_NUM("work", key.work);
00408         READ_NUM("task", key.task);
00409 #else
00410 #error "Not implemented"
00411 #endif
00412         READ_END
00413         pos->key=key;
00414 
00415         return 0;
00416 }
00417 
00418 int WRITE_VALUE(FILE *outputFile, HASH_TYPE *hash, POS_HANDLER_TYPE pos) {
00419 #ifdef DEBUG    
00420         char header[] = "-#Value-";
00421         WRITE_BYTES(outputFile, header, strlen(header));
00422 #endif
00423 
00424         
00425 #ifdef VAL_INT
00426         WRITE_NUM(outputFile, "value", posGetValue(pos));
00427 #endif
00428 #ifdef VAL_VOID
00429         /// \todo Test values with \0 inside
00430         hash->serValVoid(outputFile, posGetValue(pos));
00431 #endif
00432 #ifdef VAL_FLOAT
00433         WRITE_FLOAT(outputFile, "value", posGetValue(pos));
00434 #endif
00435 
00436 #ifdef DEBUG    
00437         char footer[] = "-#TupleEnd-";
00438         WRITE_BYTES(outputFile, footer, strlen(footer));
00439 #endif
00440 
00441         return 0;
00442 }
00443 
00444 int READ_VALUE(FILE *inputFile, HASH_TYPE *hash, POS_HANDLER_TYPE pos) {
00445 #ifdef DEBUG    
00446         char header[] = "-#Value-";
00447         char *buf = malloc(strlen(header)+1); // +1 to the \0
00448         READ_BYTES(inputFile, buf, strlen(header));
00449         buf[strlen(header)] = '\0';
00450         
00451         if (strcmp(header, buf)) {
00452                 // If header != buf, use assert to indicate the error
00453                 assert(0 == strcmp(header, buf));
00454         }
00455         free(buf);
00456 #endif
00457 
00458         // Set value to NULL, to catch cases where cser read nothing
00459         posSetValue(pos, NO_VALUE);
00460 
00461 #ifdef VAL_VOID
00462         /// \todo Test values with \0 inside
00463         posSetValue(pos, hash->deserValVoid(inputFile));
00464 #else
00465 
00466         READ_BEGIN(inputFile);  
00467 #ifdef VAL_INT
00468         READ_NUM("value", posGetValue(pos));
00469 #endif
00470 #ifdef VAL_FLOAT
00471         READ_FLOAT("value", posGetValue(pos));
00472 #endif
00473         READ_END
00474 
00475 #endif // ifdef VAL_VOID                
00476         
00477 
00478 #ifdef DEBUG    
00479         char footer[] = "-#TupleEnd-";
00480         buf = malloc(strlen(footer)+1); // +1 to the \0
00481         READ_BYTES(inputFile, buf, strlen(footer));
00482         buf[strlen(footer)] = '\0';
00483         
00484         if (strcmp(footer, buf)) {
00485                 // If footer != buf, use assert to indicate the error
00486                 assert(0 == strcmp(footer, buf));
00487         }
00488         free(buf);
00489 #endif
00490 
00491         return 0;
00492 }
00493 
00494 #ifdef VAL_VOID
00495 void SET_SERIALIZER_VAL_VOID(HASH_TYPE *hash, hashSerializeValVoid *serializer, hashDeserializeValVoid *deserializer) {
00496         hash->serValVoid = serializer;
00497         hash->deserValVoid = deserializer;
00498 }
00499 #endif
00500 
00501 
00502 /*---------------- Opera??es sobre a lista encadeada do hash ----------------*/
00503 
00504 //-----------------------------------------------------------------------------
00505 /// Destr?i a lista encadeada de um hash.
00506 //-----------------------------------------------------------------------------
00507 void LISTA_DESTROY(LISTA_TYPE *l) {
00508         if (l->next != NULL) {
00509                 LISTA_DESTROY(l->next);
00510         }
00511         destroyKey(l->key);
00512 
00513         free(l);
00514 }
00515 
00516 
00517 /*------------------- Opera??es sobre um iterador do hash -------------------*/
00518 
00519 //-----------------------------------------------------------------------------
00520 /** Cria um iterador de hash.
00521         \param hash Hash sobre o qual o iterador vai percorrer.
00522         \param is_destructor se for =1 o iterador vai limpando o hash a medida que passa por ele.
00523         \return Ponteiro para o iterador.
00524         \sa hashStrIntIteratorNext()
00525 */
00526 //-----------------------------------------------------------------------------
00527 ITERATOR_TYPE *CREATE_ITERATOR(HASH_TYPE *hash, int is_destructor) {
00528         ITERATOR_TYPE *hdi = (ITERATOR_TYPE *) malloc(sizeof(ITERATOR_TYPE));
00529 
00530         hdi->is_destructor = is_destructor;
00531         hdi->entr_atual    = -1;
00532         hdi->elem_atual    = NULL;
00533         
00534         return hdi;
00535 }
00536 
00537 
00538 //-----------------------------------------------------------------------------
00539 /** Retorna a posi??o do pr?ximo elemento no hash. Se o iterador foi criado com 
00540  * o argumento is_destructor =1 destr?i toda lista encadeada do hash que 
00541     terminar de percorrer.
00542         \param hdi Iterador.
00543         \param hash Hash sobre o qual o iterador vai percorrer.
00544         \return Ponteiro para a posi??o do pr?ximo elemento do hash.
00545 */
00546 //-----------------------------------------------------------------------------
00547 POS_HANDLER_TYPE ITERATOR_NEXT(ITERATOR_TYPE *hdi, HASH_TYPE *hash) {
00548         if (hdi->elem_atual != NULL) {
00549                 hdi->elem_atual = hdi->elem_atual->next;
00550         }
00551         if (hdi->is_destructor && hdi->elem_atual==NULL && hdi->entr_atual >=0) {
00552                 LISTA_DESTROY(hash->entradas[hdi->entr_atual]);
00553                 hash->entradas[hdi->entr_atual] = NULL;
00554         }
00555         while (hdi->elem_atual == NULL) {
00556                 hdi->entr_atual++;
00557                 // Se o hash acabou retorna NULL
00558                 if (hdi->entr_atual >= hash->tam) return NULL;
00559                 hdi->elem_atual = hash->entradas[hdi->entr_atual];                      
00560         }
00561 
00562 #ifdef DEBUG
00563         if (hdi->elem_atual == NULL) {
00564                 if (hdi->entr_atual < hash->tam) {
00565                         printf("%s(%d): Deu pau!!! Vai retornar NULL sem o hash ter acabado\n", __FILE__, __LINE__);
00566                         exit(1);
00567                 }
00568         }
00569 #endif
00570         return hdi->elem_atual;
00571 }
00572 
00573 //-----------------------------------------------------------------------------
00574 /** Destroi um iterador de hash. Se o iterador foi criado com o argumento 
00575  * is_destructor =1 e tiver acabado de percorrer uma lista encadeada do hash, 
00576  * acaba com ela.
00577  */ 
00578 //-----------------------------------------------------------------------------
00579 void ITERATOR_DESTROY(ITERATOR_TYPE *hdi, HASH_TYPE *hash) {
00580         if (hdi->is_destructor && hdi->entr_atual<hash->tam && hdi->entr_atual >=0 && hdi->elem_atual==NULL) {
00581                 LISTA_DESTROY(hash->entradas[hdi->entr_atual]);
00582                 hash->entradas[hdi->entr_atual] = NULL;
00583         }
00584         free(hdi);
00585 }
00586 

Generated on Tue Jan 17 19:18:38 2006 for Void by  doxygen 1.4.6