The qDecoder Project

[svn] / releases / qDecoder-9.0.3 / src / qHashtbl.c

Parent Directory Parent Directory Revision Log Revision Log


Revision 499 - Download Blame
Mon Jan 4 22:18:30 2010 UTC (8 months ago) by wolkykim
File size: 13812 byte(s)
Renaming RB-9.0.3 to qDecoder-9.0.3
    1 /*
    2  * Copyright 2008 The qDecoder Project. All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  *
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE QDECODER PROJECT ``AS IS'' AND ANY
   15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   17  * DISCLAIMED. IN NO EVENT SHALL THE QDECODER PROJECT BE LIABLE FOR ANY
   18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  */
   25 
   26 /**
   27  * @file qHashtbl.c Hash-table Data Structure API
   28  */
   29 
   30 #ifndef DISABLE_DATASTRUCTURE
   31 
   32 #include <stdio.h>
   33 #include <stdlib.h>
   34 #include <stdbool.h>
   35 #include <string.h>
   36 #include "qDecoder.h"
   37 #include "qInternal.h"
   38 
   39 static int _findEmpty(Q_HASHTBL *tbl, int startidx);
   40 static int _getIdx(Q_HASHTBL *tbl, const char *key, int hash);
   41 static bool _putData(Q_HASHTBL *tbl, int idx, int hash, const char *key, const void *value, int size, int count);
   42 static bool _removeData(Q_HASHTBL *tbl, int idx);
   43 
   44 /**
   45  * Initialize dynamic-hash table
   46  *
   47  * @param max		a number of maximum  size of Q_HASHARR
   48  *
   49  * @return		a pointer of malloced Q_HASHTBL, otherwise returns false
   50  *
   51  * @code
   52  *   Q_HASHTBL *hashtbl = qHashtblInit(1000);
   53  *   qHashtblFree(hashtbl);
   54  * @endcode
   55  */
   56 Q_HASHTBL *qHashtblInit(int max) {
   57 	if(max <= 0) return NULL;
   58 
   59 	Q_HASHTBL *tbl = (Q_HASHTBL *)malloc(sizeof(Q_HASHTBL));
   60 	if(tbl == NULL) return NULL;
   61 
   62 	memset((void *)tbl, 0, sizeof(Q_HASHTBL));
   63 
   64 	tbl->count = (int *)malloc(sizeof(int) * max);
   65 	if(tbl->count != NULL) memset((void *)(tbl->count), 0, (sizeof(int) * max));
   66 	tbl->hash = (int *)malloc(sizeof(int) * max);
   67 	if(tbl->hash != NULL) memset((void *)(tbl->hash), 0, (sizeof(int) * max));
   68 
   69 	tbl->key = (char **)malloc(sizeof(char *) * max);
   70 	if(tbl->key != NULL) memset((void *)(tbl->key), 0, (sizeof(char *) * max));
   71 	tbl->value = (void **)malloc(sizeof(char *) * max);
   72 	if(tbl->value != NULL) memset((void *)(tbl->value), 0, (sizeof(void *) * max));
   73 	tbl->size = (int *)malloc(sizeof(int) * max);
   74 	if(tbl->size != NULL) memset((void *)(tbl->size), 0, (sizeof(int) * max));
   75 
   76 	if(tbl->count == NULL || tbl->hash == NULL || tbl->key == NULL || tbl->value == NULL || tbl->size == NULL) {
   77 		qHashtblFree(tbl);
   78 		return NULL;
   79 	}
   80 
   81 	tbl->max = max;
   82 	return tbl;
   83 }
   84 
   85 /**
   86  * De-allocate hash table
   87  *
   88  * @param tbl		a pointer of Q_HASHTBL
   89  *
   90  * @return		true if successful, otherwise returns false
   91  */
   92 bool qHashtblFree(Q_HASHTBL *tbl) {
   93 	if(tbl == NULL) return false;
   94 
   95 	int idx, num;
   96 	for (idx = 0, num = 0; idx < tbl->max && num < tbl->num; idx++) {
   97 		if (tbl->count[idx] == 0) continue;
   98 		free(tbl->key[idx]);
   99 		free(tbl->value[idx]);
  100 
  101 		num++;
  102 		if(num >= tbl->num) break;
  103 	}
  104 
  105 	if(tbl->count != NULL) free(tbl->count);
  106 	if(tbl->hash != NULL) free(tbl->hash);
  107 	if(tbl->key != NULL) free(tbl->key);
  108 	if(tbl->value != NULL) free(tbl->value);
  109 	if(tbl->size != NULL) free(tbl->size);
  110 	free(tbl);
  111 
  112 	return true;
  113 }
  114 
  115 /**
  116  * Put object into hash table
  117  *
  118  * @param tbl		a pointer of Q_HASHTBL
  119  * @param key		key string
  120  * @param value		value object data
  121  * @param size		size of value
  122  *
  123  * @return		true if successful, otherwise returns false
  124  */
  125 bool qHashtblPut(Q_HASHTBL *tbl, const char *key, const void *value, int size) {
  126 	if(tbl == NULL || key == NULL || value == NULL) return false;
  127 
  128 	// get hash integer
  129 	int hash = (int)qHashFnv32(tbl->max, key, strlen(key));
  130 
  131 	// check, is slot empty
  132 	if (tbl->count[hash] == 0) { // empty slot
  133 		// put data
  134 		_putData(tbl, hash, hash, key, value, size, 1);
  135 
  136 		DEBUG("hashtbl: put(new) %s (idx=%d,hash=%d,tot=%d)", key, hash, hash, tbl->num);
  137 	} else if (tbl->count[hash] > 0) { // same key exists or hash collision
  138 		// check same key;
  139 		int idx = _getIdx(tbl, key, hash);
  140 		if (idx >= 0) { // same key
  141 			// remove and recall
  142 			qHashtblRemove(tbl, key);
  143 			return qHashtblPut(tbl, key, value, size);
  144 		} else { // no same key, just hash collision
  145 			// find empty slot
  146 			int idx = _findEmpty(tbl, hash);
  147 			if (idx < 0) return false;
  148 
  149 			// put data
  150 			_putData(tbl, idx, hash, key, value, size, -1); // -1 is used for idx != hash;
  151 
  152 			// increase counter from leading slot
  153 			tbl->count[hash]++;
  154 
  155 			DEBUG("hashtbl: put(col) %s (idx=%d,hash=%d,tot=%d)", key, idx, hash, tbl->num);
  156 		}
  157 	} else { // in case of -1. used for dup data, move it
  158 		// find empty slot
  159 		int idx = _findEmpty(tbl, hash);
  160 		if (idx < 0) return false;
  161 
  162 		// move dup data
  163 		_putData(tbl, idx, tbl->hash[hash], tbl->key[hash], tbl->value[hash], tbl->size[hash], tbl->count[hash]);
  164 		_removeData(tbl, hash);
  165 
  166 		// store data
  167 		_putData(tbl, hash, hash, key, value, size, 1);
  168 
  169 		DEBUG("hashtbl: put(swp) %s (idx=%d,hash=%d,tot=%d)", key, hash, hash, tbl->num);
  170 	}
  171 
  172 	return true;
  173 }
  174 
  175 /**
  176  * Put string into hash table
  177  *
  178  * @param tbl		a pointer of Q_HASHTBL
  179  * @param key		key string
  180  * @param value		value string
  181  *
  182  * @return		true if successful, otherwise returns false
  183  */
  184 bool qHashtblPutStr(Q_HASHTBL *tbl, const char *key, const char *value) {
  185 	int size = (value != NULL) ? (strlen(value) + 1) : 0;
  186 	return qHashtblPut(tbl, key, value, size);
  187 }
  188 
  189 /**
  190  * Put integer into hash table
  191  *
  192  * @param tbl		a pointer of Q_HASHTBL
  193  * @param key		key string
  194  * @param value		value integer
  195  *
  196  * @return		true if successful, otherwise returns false
  197  */
  198 bool qHashtblPutInt(Q_HASHTBL *tbl, const char *key, const int value) {
  199 	char data[10+1];
  200 	sprintf(data, "%d", value);
  201 	return qHashtblPut(tbl, key, data, strlen(data) + 1);
  202 }
  203 
  204 /**
  205  * Get object from hash table
  206  *
  207  * @param tbl		a pointer of Q_HASHTBL
  208  * @param key		key string
  209  * @param size		if not NULL, oject size will be stored
  210  *
  211  * @return		malloced object pointer if successful, otherwise(not found) returns NULL
  212  *
  213  * @note
  214  * returned object must be freed after done using.
  215  */
  216 void *qHashtblGet(Q_HASHTBL *tbl, const char *key, int *size) {
  217 	if(tbl == NULL || key == NULL) return NULL;
  218 
  219 	int hash = (int)qHashFnv32(tbl->max, key, strlen(key));
  220 	int idx = _getIdx(tbl, key, hash);
  221 	if (idx < 0) return NULL;
  222 
  223 	void *value = (char *)malloc(tbl->size[idx]);
  224 	memcpy(value, tbl->value[idx], tbl->size[idx]);
  225 
  226 	if(size != NULL) *size = tbl->size[idx];
  227 	return value;
  228 }
  229 
  230 /**
  231  * Get string from hash table
  232  *
  233  * @param tbl		a pointer of Q_HASHTBL
  234  * @param key		key string
  235  *
  236  * @return		string pointer if successful, otherwise(not found) returns NULL
  237  *
  238  * @note
  239  * returned object must be freed after done using.
  240  */
  241 char *qHashtblGetStr(Q_HASHTBL *tbl, const char *key) {
  242 	return qHashtblGet(tbl, key, NULL);
  243 }
  244 
  245 /**
  246  * Get integer from hash table
  247  *
  248  * @param tbl		a pointer of Q_HASHARR
  249  * @param key		key string
  250  *
  251  * @return		value integer if successful, otherwise(not found) returns 0
  252  */
  253 int qHashtblGetInt(Q_HASHTBL *tbl, const char *key) {
  254 	char *data = qHashtblGet(tbl, key, NULL);
  255 	if(data == NULL) return 0;
  256 
  257 	int value = atoi(data);
  258 	free(data);
  259 
  260 	return value;
  261 }
  262 
  263 /**
  264  * Get first key name
  265  *
  266  * @param tbl		a pointer of Q_HASHTBL
  267  * @param idx		index pointer
  268  *
  269  * @return		key name string if successful, otherwise returns NULL
  270  *
  271  * @note
  272  * Do not free returned key string.
  273  *
  274  * @code
  275  *   char *key;
  276  *   int idx;
  277  *   for(key = qHashtblGetFirstKey(tbl, &idx); key != NULL; key = qHashtblGetNextKey(tbl, &idx) {
  278  *     char *value = qHashtblGetStr(tbl, key);
  279  *     if(value != NULL) free(value);
  280  *   }
  281  * @endcode
  282  */
  283 const char *qHashtblGetFirstKey(Q_HASHTBL *tbl, int *idx) {
  284 	if(idx != NULL) *idx = -1;
  285 	return qHashtblGetNextKey(tbl, idx);
  286 }
  287 
  288 /**
  289  * Get next key name
  290  *
  291  * @param tbl		a pointer of Q_HASHTBL
  292  * @param idx		index pointer
  293  *
  294  * @return		key name string if successful, otherwise(end of table) returns NULL
  295  *
  296  * @note
  297  * Do not free returned key string.
  298  */
  299 const char *qHashtblGetNextKey(Q_HASHTBL *tbl, int *idx) {
  300 	if(tbl == NULL || idx == NULL) return NULL;
  301 
  302 	for ((*idx)++; *idx < tbl->max; (*idx)++) {
  303 		if (tbl->count[*idx] == 0) continue;
  304 		return tbl->key[*idx];
  305 	}
  306 
  307 	*idx = tbl->max;
  308 	return NULL;
  309 }
  310 
  311 /**
  312  * Remove key from hash table
  313  *
  314  * @param tbl		a pointer of Q_HASHTBL
  315  * @param key		key string
  316  *
  317  * @return		true if successful, otherwise(not found) returns false
  318  */
  319 bool qHashtblRemove(Q_HASHTBL *tbl, const char *key) {
  320 	if(tbl == NULL || key == NULL) return false;
  321 
  322 	int hash = (int)qHashFnv32(tbl->max, key, strlen(key));
  323 	int idx = _getIdx(tbl, key, hash);
  324 	if (idx < 0) return false;
  325 
  326 	if (tbl->count[idx] == 1) {
  327 		// just remove
  328 		_removeData(tbl, idx);
  329 
  330 		DEBUG("hashtbl: rem %s (idx=%d,tot=%d)", key, idx, tbl->num);
  331 	} else if (tbl->count[idx] > 1) { // leading slot and has dup
  332 		// find dup
  333 		int idx2;
  334 		for (idx2 = idx + 1; ; idx2++) {
  335 			if (idx2 >= tbl->max) idx2 = 0;
  336 			if (idx2 == idx) {
  337 				DEBUG("hashtbl: BUG remove failed %s. dup not found.", key);
  338 				return false;
  339 			}
  340 			if (tbl->count[idx2] == -1 && tbl->hash[idx2] == idx) break;
  341 		}
  342 
  343 		// move to leading slot
  344 		int backupcount = tbl->count[idx];
  345 		_removeData(tbl, idx); // remove leading slot
  346 		_putData(tbl, idx, tbl->hash[idx2], tbl->key[idx2], tbl->value[idx2], tbl->size[idx2], backupcount - 1); // copy to leading slot
  347 		_removeData(tbl, idx2); // remove dup slot
  348 
  349 		DEBUG("hashtbl: rem(lead) %s (idx=%d,tot=%d)", key, idx, tbl->num);
  350 	} else { // in case of -1. used for dup data
  351 		// decrease counter from leading slot
  352 		if(tbl->count[tbl->hash[idx]] <= 1) {
  353 			DEBUG("hashtbl: BUG remove failed %s. counter of leading slot mismatch.", key);
  354 			return false;
  355 		}
  356 		tbl->count[tbl->hash[idx]]--;
  357 
  358 		// remove dup
  359 		_removeData(tbl, idx);
  360 
  361 		DEBUG("hashtbl: rem(dup) %s (idx=%d,tot=%d)", key, idx, tbl->num);
  362 	}
  363 
  364 	return true;
  365 }
  366 
  367 /**
  368  * Print hash table for debugging purpose
  369  *
  370  * @param tbl		a pointer of Q_HASHTBL
  371  * @param out		output stream
  372  * @param showvalue	print out value if set to true
  373  *
  374  * @return		true if successful, otherwise returns false
  375  */
  376 bool qHashtblPrint(Q_HASHTBL *tbl, FILE *out, bool showvalue) {
  377 	if(tbl == NULL || out == NULL) return false;
  378 
  379 	int idx, num;
  380 	for (idx = 0, num = 0; idx < tbl->max && num < tbl->num; idx++) {
  381 		if (tbl->count[idx] == 0) continue;
  382 		fprintf(out, "%s=%s (idx=%d,hash=%d,size=%d)\n", tbl->key[idx], (showvalue)?(char*)tbl->value[idx]:"_binary_", idx, tbl->hash[idx], tbl->size[idx]);
  383 		num++;
  384 	}
  385 
  386 	return true;
  387 }
  388 
  389 /**
  390  * Get hash table internal status
  391  *
  392  * @param tbl		a pointer of Q_HASHARR
  393  * @param used		if not NULL, a number of used keys will be stored
  394  * @param max		if not NULL, the maximum usable number of keys will be stored
  395  *
  396  * @return		true if successful, otherwise returns false
  397  */
  398 bool qHashtblStatus(Q_HASHTBL *tbl, int *used, int *max) {
  399 	if(tbl == NULL) return false;
  400 
  401 	if(used != NULL) *used = tbl->num;
  402 	if(max != NULL) *max = tbl->max;
  403 
  404 	return true;
  405 }
  406 
  407 /////////////////////////////////////////////////////////////////////////
  408 // PRIVATE FUNCTIONS
  409 /////////////////////////////////////////////////////////////////////////
  410 
  411 // find empty slot : return empty slow number, otherwise returns -1.
  412 static int _findEmpty(Q_HASHTBL *tbl, int startidx) {
  413 	if (startidx >= tbl->max) startidx = 0;
  414 
  415 	int idx = startidx;
  416 	while (true) {
  417 		if (tbl->count[idx] == 0) return idx;
  418 
  419 		idx++;
  420 		if (idx >= tbl->max) idx = 0;
  421 		if(idx == startidx) break;
  422 	}
  423 
  424 	return -1;
  425 }
  426 
  427 static int _getIdx(Q_HASHTBL *tbl, const char *key, int hash) {
  428 	if (tbl->count[hash] > 0) {
  429 		int count, idx;
  430 		for (count = 0, idx = hash; count < tbl->count[hash]; ) {
  431 			// find same hash
  432 			while(true) {
  433 				if (idx >= tbl->max) idx = 0;
  434 
  435 				if (tbl->count[idx] != 0 && tbl->hash[idx] == hash) {
  436 					// found same hash
  437 					count++;
  438 					break;
  439 				}
  440 
  441 				idx++;
  442 				if(idx == hash) return -1;
  443 			}
  444 
  445 			// is same key?
  446 			if (!strcmp(tbl->key[idx], key)) return idx;
  447 
  448 			idx++;
  449 			if (idx >= tbl->max) idx = 0;
  450 			if(idx == hash) break;
  451 		}
  452 	}
  453 
  454 	return -1;
  455 }
  456 
  457 static bool _putData(Q_HASHTBL *tbl, int idx, int hash, const char *key, const void *value, int size, int count) {
  458 	// check if used
  459 	if(tbl->count[idx] != 0) return false;
  460 
  461 	// store
  462 	tbl->hash[idx] = hash;
  463 	tbl->key[idx] = strdup(key);
  464 	tbl->value[idx] = malloc(size);
  465 	if(tbl->value[idx] == NULL) {
  466 		free(tbl->key[idx]);
  467 		return false;
  468 	}
  469 	memcpy(tbl->value[idx], value, size);
  470 	tbl->size[idx] = size;
  471 	tbl->count[idx] = count;
  472 
  473 	// increase used counter
  474 	tbl->num++;
  475 
  476 	return true;
  477 }
  478 
  479 static bool _removeData(Q_HASHTBL *tbl, int idx) {
  480 	if(tbl->count[idx] == 0) return false;
  481 
  482 	free(tbl->key[idx]);
  483 	free(tbl->value[idx]);
  484 	tbl->count[idx] = 0;
  485 
  486 	// decrease used counter
  487 	tbl->num--;
  488 
  489 	return true;
  490 }
  491 
  492 #endif /* DISABLE_DATASTRUCTURE */

Home | About | Examples | Changes | Download | SVN Repository | Install | Reference