/** * Get an object from table * * @param tbl qhasharr_t container pointer. * @param key key string * @param size if not NULL, oject size will be stored * * @return malloced object pointer if successful, otherwise(not found) * returns NULL * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invalid argument. * - ENOMEM : Memory allocation failed. * * @note * returned object must be freed after done using. */ void *qhasharr_get(qhasharr_t *tbl, const char *key, size_t key_size, size_t *val_size) { if (NULL == tbl || NULL == key) { errno = EINVAL; return NULL; } qhasharr_slot_t *_tbl_slots = NULL; qhasharr_init(tbl, &_tbl_slots); // get hash integer if (tbl->maxslots == 0) { return NULL; } unsigned int hash = qhashmurmur3_32(key, key_size) % tbl->maxslots; int idx = _get_idx(tbl, key, key_size, hash); if (idx < 0) { errno = ENOENT; return NULL; } return _get_data(tbl, idx, val_size); }
/** * qhasharr->get(): Get an object from this table * * @param tbl qhasharr_t container pointer. * @param key key string * @param size if not NULL, oject size will be stored * * @return malloced object pointer if successful, otherwise(not found) * returns NULL * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invalid argument. * - ENOMEM : Memory allocation failed. * * @note * returned object must be freed after done using. */ static void *get(qhasharr_t *tbl, const char *key, size_t *size) { if (key == NULL) { errno = EINVAL; return NULL; } // get hash integer unsigned int hash = qhashmurmur3_32(key, strlen(key)) % tbl->maxslots; int idx = _get_idx(tbl, key, hash); if (idx < 0) { errno = ENOENT; return NULL; } return _get_data(tbl, idx, size); }
/** * qhasharr->remove_by_obj(): Remove an object from this table by key object * * @param tbl qhasharr_t container pointer. * @param name key data * @param namesize size of key * * @return true if successful, otherwise(not found) returns false * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invald argument. * - EFAULT : Unexpected error. Data structure is not constant. */ bool qhasharr_remove_by_obj(qhasharr_t *tbl, const void *name, size_t namesize) { if (tbl == NULL || name == NULL || namesize == 0) { errno = EINVAL; return false; } qhasharr_data_t *tbldata = tbl->data; // get hash integer uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots; int idx = get_idx(tbl, name, namesize, hash); if (idx < 0) { errno = ENOENT; return false; } return qhasharr_remove_by_idx(tbl, idx); }
/** * qhashtbl->remove(): Remove an object from this table. * * @param tbl qhashtbl_t container pointer. * @param name key name * * @return true if successful, otherwise(not found) returns false * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invalid argument. */ bool qhashtbl_remove(qhashtbl_t *tbl, const char *name) { if (name == NULL) { errno = EINVAL; return false; } qhashtbl_lock(tbl); uint32_t hash = qhashmurmur3_32(name, strlen(name)); int idx = hash % tbl->range; // find key bool found = false; qhashtbl_obj_t *prev = NULL; qhashtbl_obj_t *obj; for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) { if (obj->hash == hash && !strcmp(obj->name, name)) { // adjust link if (prev == NULL) tbl->slots[idx] = obj->next; else prev->next = obj->next; // remove free(obj->name); free(obj->data); free(obj); found = true; tbl->num--; break; } prev = obj; } qhashtbl_unlock(tbl); if (found == false) errno = ENOENT; return found; }
// lock must be obtained from caller static bool insertObject(listtable* tbl, listtableObj* obj) { // update hash obj->hash = qhashmurmur3_32(obj->name, strlen(obj->name)); listtableObj* prev = obj->prev; listtableObj* next = obj->next; if (prev == NULL) tbl->first = obj; else prev->next = obj; if (next == NULL) tbl->last = obj; else next->prev = obj; // increase counter tbl->num++; return true; }
/** * qhashtbl->get(): Get an object from this table. * * @param tbl qhashtbl_t container pointer. * @param name key name. * @param size if not NULL, oject size will be stored. * @param newmem whether or not to allocate memory for the data. * * @return a pointer of data if the key is found, otherwise returns NULL. * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invalid argument. * - ENOMEM : Memory allocation failure. * * @code * qhashtbl_t *tbl = qhashtbl(0, 0); * (...codes...) * * // with newmem flag unset * size_t size; * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, false); * * // with newmem flag set * size_t size; * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, true); * free(data); * @endcode * * @note * If newmem flag is set, returned data will be malloced and should be * deallocated by user. Otherwise returned pointer will point internal buffer * directly and should not be de-allocated by user. In thread-safe mode, * newmem flag must be set to true always. */ void *qhashtbl_get(qhashtbl_t *tbl, const char *name, size_t *size, bool newmem) { if (name == NULL) { errno = EINVAL; return NULL; } uint32_t hash = qhashmurmur3_32(name, strlen(name)); int idx = hash % tbl->range; qhashtbl_lock(tbl); // find key qhashtbl_obj_t *obj; for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) { if (obj->hash == hash && !strcmp(obj->name, name)) { break; } } void *data = NULL; if (obj != NULL) { if (newmem == false) { data = obj->data; } else { data = malloc(obj->size); if (data == NULL) { errno = ENOMEM; return NULL; } memcpy(data, obj->data, obj->size); } if (size != NULL && data != NULL) *size = obj->size; } qhashtbl_unlock(tbl); if (data == NULL) errno = ENOENT; return data; }
bool qhasharr_exist(qhasharr_t *tbl, const char *key, size_t key_size) { if ( NULL == tbl || NULL == key) { errno = EINVAL; return false; } if (tbl->maxslots == 0) { return false; } // get hash integer unsigned int hash = qhashmurmur3_32(key, key_size) % tbl->maxslots; if (_get_idx(tbl, key, key_size, hash) >= 0) //same key { return true; } return false; }
/** * qhasharr->getts_by_obj(): Get an timestamp of object by key object from * this table * * @param tbl qhasharr_t container pointer. * @param name key data * @param namesize size of key * @param ts if not NULL, timestamp of object will be stored * * @return malloced object pointer if successful, otherwise(not found) * returns NULL * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invalid argument. * * @note * timestamp must be pre-allocated. */ bool qhasharr_getts_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, time_t *ts) { if (tbl == NULL || name == NULL || namesize == 0) { errno = EINVAL; return false; } qhasharr_data_t *tbldata = tbl->data; // get hash integer uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots; int idx = get_idx(tbl, name, namesize, hash); if (idx < 0) { errno = ENOENT; return false; } if (ts) { qhasharr_slot_t *tblslots = get_slots(tbl); *ts = tblslots[idx].timestamp; } return true; }
/** * qhashtbl->put(): Put an object into this table. * * @param tbl qhashtbl_t container pointer. * @param name key name * @param data data object * @param size size of data object * * @return true if successful, otherwise returns false * @retval errno will be set in error condition. * - EINVAL : Invalid argument. * - ENOMEM : Memory allocation failure. */ bool qhashtbl_put(qhashtbl_t *tbl, const char *name, const void *data, size_t size) { if (name == NULL || data == NULL) { errno = EINVAL; return false; } // get hash integer uint32_t hash = qhashmurmur3_32(name, strlen(name)); int idx = hash % tbl->range; qhashtbl_lock(tbl); // find existence key qhashtbl_obj_t *obj; for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) { if (obj->hash == hash && !strcmp(obj->name, name)) { break; } } // duplicate object char *dupname = strdup(name); void *dupdata = malloc(size); if (dupname == NULL || dupdata == NULL) { free(dupname); free(dupdata); qhashtbl_unlock(tbl); errno = ENOMEM; return false; } memcpy(dupdata, data, size); // put into table if (obj == NULL) { // insert obj = (qhashtbl_obj_t *) calloc(1, sizeof(qhashtbl_obj_t)); if (obj == NULL) { free(dupname); free(dupdata); qhashtbl_unlock(tbl); errno = ENOMEM; return false; } if (tbl->slots[idx] != NULL) { // insert at the beginning obj->next = tbl->slots[idx]; } tbl->slots[idx] = obj; // increase counter tbl->num++; } else { // replace free(obj->name); free(obj->data); } // set data obj->hash = hash; obj->name = dupname; obj->data = dupdata; obj->size = size; qhashtbl_unlock(tbl); return true; }
/** * Remove an object from this table. * * @param tbl qhasharr_t container pointer. * @param key key string * * @return true if successful, otherwise(not found) returns false * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invald argument. * - EFAULT : Unexpected error. Data structure is not constant. */ bool qhasharr_remove(qhasharr_t *tbl, const char *key, size_t key_size) { if (NULL == tbl || NULL == key) { errno = EINVAL; return false; } qhasharr_slot_t *_tbl_slots = NULL; qhasharr_init(tbl, &_tbl_slots); if (tbl->maxslots == 0) { return false; } // get hash integer unsigned int hash = qhashmurmur3_32(key, key_size) % tbl->maxslots; int idx = _get_idx(tbl, key, key_size, hash); if (idx < 0) { errno = ENOENT; return false; } if (_tbl_slots[idx].count == 1) { // just remove _remove_data(tbl, idx); } else if (_tbl_slots[idx].count > 1) // leading slot and has dup { // find dup int idx2; for (idx2 = idx + 1; ; idx2++) { if (idx2 >= tbl->maxslots) idx2 = 0; if (idx2 == idx) { errno = EFAULT; return false; } if (_tbl_slots[idx2].count == -1 && _tbl_slots[idx2].hash == hash) { break; } } // move to leading slot int backupcount = _tbl_slots[idx].count; _remove_data(tbl, idx); // remove leading data _copy_slot(tbl, idx, idx2); // copy slot _remove_slot(tbl, idx2); // remove moved slot _tbl_slots[idx].count = backupcount - 1; // adjust collision counter if (_tbl_slots[idx].link != -1) { _tbl_slots[_tbl_slots[idx].link].hash = idx; } } else if (_tbl_slots[idx].count == -1) // in case of -1. used for collision resolution { // decrease counter from leading slot if (_tbl_slots[ _tbl_slots[idx].hash ].count <= 1) { errno = EFAULT; return false; } _tbl_slots[ _tbl_slots[idx].hash ].count--; // remove data _remove_data(tbl, idx); } else { errno = ENOENT; return false; } return true; }
/** * Put an object into table. * * @param tbl qhasharr_t container pointer. * @param key key string * @param value value object data * @param size size of value * * @return true if successful, otherwise returns false * @retval errno will be set in error condition. * - ENOBUFS : Table doesn't have enough space to store the object. * - EINVAL : Invalid argument. * - EFAULT : Unexpected error. Data structure is not constant. */ bool qhasharr_put(qhasharr_t *tbl, const char *key, size_t key_size, const void *value, size_t val_size) { if (NULL == tbl || NULL == key || NULL == value) { errno = EINVAL; return false; } qhasharr_slot_t *_tbl_slots = NULL; qhasharr_init(tbl, &_tbl_slots); if (tbl->maxslots == 0) { return false; } // check full if (tbl->usedslots >= tbl->maxslots) { errno = ENOBUFS; return false; } // get hash integer unsigned int hash = qhashmurmur3_32(key, key_size) % tbl->maxslots; // check, is slot empty if (_tbl_slots[hash].count == 0) // empty slot { // put data if (_put_data(tbl, hash, hash, key, key_size, value, val_size, 1) == false) { return false; } } else if (_tbl_slots[hash].count > 0) // same key or hash collision { // check same key; int idx = _get_idx(tbl, key, key_size, hash); if (idx >= 0) // same key { // remove and recall if (!qhasharr_remove(tbl, key, key_size)) return false; return qhasharr_put(tbl, key, key_size, value, val_size); } else // no same key, just hash collision { // find empty slot int idx = _find_empty(tbl, hash); if (idx < 0) { errno = ENOBUFS; return false; } // put data. -1 is used for collision resolution (idx != hash); if (_put_data(tbl, idx, hash, key, key_size, value, val_size, -1) == false) { return false; } // increase counter from leading slot _tbl_slots[hash].count++; // key, idx, hash, tbl->usedslots); } } else { // in case of -1 or -2, move it. -1 used for collision resolution, // -2 used for oversized value data. // find empty slot int idx = _find_empty(tbl, hash + 1); if (idx < 0) { errno = ENOBUFS; return false; } // move dup slot to empty _copy_slot(tbl, idx, hash); _remove_slot(tbl, hash); // in case of -2, adjust link of mother if (_tbl_slots[idx].count == -2) { _tbl_slots[ _tbl_slots[idx].hash ].link = idx; if (_tbl_slots[idx].link != -1) { _tbl_slots[ _tbl_slots[idx].link ].hash = idx; } } else if (_tbl_slots[idx].count == -1) { if (_tbl_slots[idx].link != -1) { _tbl_slots[ _tbl_slots[idx].link ].hash = idx; } } // store data if (_put_data(tbl, hash, hash, key, key_size, value, val_size, 1) == false) { return false; } } return true; }
/** * qhasharr->remove(): Remove an object from this table. * * @param tbl qhasharr_t container pointer. * @param key key string * * @return true if successful, otherwise(not found) returns false * @retval errno will be set in error condition. * - ENOENT : No such key found. * - EINVAL : Invald argument. * - EFAULT : Unexpected error. Data structure is not constant. */ static bool remove_(qhasharr_t *tbl, const char *key) { if (key == NULL) { errno = EINVAL; return false; } // get hash integer unsigned int hash = qhashmurmur3_32(key, strlen(key)) % tbl->maxslots; int idx = _get_idx(tbl, key, hash); if (idx < 0) { DEBUG("not found %s", key); errno = ENOENT; return false; } if (tbl->slots[idx].count == 1) { // just remove _remove_data(tbl, idx); DEBUG("hasharr: rem %s (idx=%d,tot=%d)", key, idx, tbl->usedslots); } else if (tbl->slots[idx].count > 1) { // leading slot and has dup // find dup int idx2; for (idx2 = idx + 1; ; idx2++) { if (idx2 >= tbl->maxslots) idx2 = 0; if (idx2 == idx) { DEBUG("hasharr: [BUG] failed to remove dup key %s.", key); errno = EFAULT; return false; } if (tbl->slots[idx2].count == -1 && tbl->slots[idx2].hash == hash) { break; } } // move to leading slot int backupcount = tbl->slots[idx].count; _remove_data(tbl, idx); // remove leading data _copy_slot(tbl, idx, idx2); // copy slot _remove_slot(tbl, idx2); // remove moved slot tbl->slots[idx].count = backupcount - 1; // adjust collision counter if (tbl->slots[idx].link != -1) { tbl->slots[tbl->slots[idx].link].hash = idx; } DEBUG("hasharr: rem(lead) %s (idx=%d,tot=%d)", key, idx, tbl->usedslots); } else { // in case of -1. used for collision resolution // decrease counter from leading slot if (tbl->slots[ tbl->slots[idx].hash ].count <= 1) { DEBUG("hasharr: [BUG] failed to remove %s. " "counter of leading slot mismatch.", key); errno = EFAULT; return false; } tbl->slots[ tbl->slots[idx].hash ].count--; // remove data _remove_data(tbl, idx); DEBUG("hasharr: rem(dup) %s (idx=%d,tot=%d)", key, idx, tbl->usedslots); } return true; }
/** * qhasharr->put(): Put an object into this table. * * @param tbl qhasharr_t container pointer. * @param key key string * @param value value object data * @param size size of value * * @return true if successful, otherwise returns false * @retval errno will be set in error condition. * - ENOBUFS : Table doesn't have enough space to store the object. * - EINVAL : Invalid argument. * - EFAULT : Unexpected error. Data structure is not constant. */ static bool put(qhasharr_t *tbl, const char *key, const void *value, size_t size) { if (key == NULL || value == NULL) { errno = EINVAL; return false; } // check full if (tbl->usedslots >= tbl->maxslots) { DEBUG("hasharr: put %s - FULL", key); errno = ENOBUFS; return false; } // get hash integer unsigned int hash = qhashmurmur3_32(key, strlen(key)) % tbl->maxslots; // check, is slot empty if (tbl->slots[hash].count == 0) { // empty slot // put data if (_put_data(tbl, hash, hash, key, value, size, 1) == false) { DEBUG("hasharr: FAILED put(new) %s", key); return false; } DEBUG("hasharr: put(new) %s (idx=%d,hash=%u,tot=%d)", key, hash, hash, tbl->usedslots); } else if (tbl->slots[hash].count > 0) { // same key or hash collision // check same key; int idx = _get_idx(tbl, key, hash); if (idx >= 0) { // same key // remove and recall remove_(tbl, key); return put(tbl, key, value, size); } else { // no same key, just hash collision // find empty slot int idx = _find_empty(tbl, hash); if (idx < 0) { errno = ENOBUFS; return false; } // put data. -1 is used for collision resolution (idx != hash); if (_put_data(tbl, idx, hash, key, value, size, -1) == false) { DEBUG("hasharr: FAILED put(col) %s", key); return false; } // increase counter from leading slot tbl->slots[hash].count++; DEBUG("hasharr: put(col) %s (idx=%d,hash=%u,tot=%d)", key, idx, hash, tbl->usedslots); } } else { // in case of -1 or -2, move it. -1 used for collision resolution, // -2 used for oversized value data. // find empty slot int idx = _find_empty(tbl, hash + 1); if (idx < 0) { errno = ENOBUFS; return false; } // move dup slot to empty _copy_slot(tbl, idx, hash); _remove_slot(tbl, hash); // in case of -2, adjust link of mother if (tbl->slots[idx].count == -2) { tbl->slots[ tbl->slots[idx].hash ].link = idx; if (tbl->slots[idx].link != -1) { tbl->slots[ tbl->slots[idx].link ].hash = idx; } } // store data if (_put_data(tbl, hash, hash, key, value, size, 1) == false) { DEBUG("hasharr: FAILED put(swp) %s", key); return false; } DEBUG("hasharr: put(swp) %s (idx=%u,hash=%u,tot=%d)", key, hash, hash, tbl->usedslots); } return true; }
/** * qhasharr->put_by_obj(): ut an object into this table by key object. * * @param tbl qhasharr_t container pointer. * @param name key data * @param namesize size of key * @param data data * @param datasize size of data * * @return true if successful, otherwise returns false * @retval errno will be set in error condition. * - ENOBUFS : Table doesn't have enough space to store the object. * - EINVAL : Invalid argument. * - EFAULT : Unexpected error. Data structure is not constant. */ bool qhasharr_put_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize) { if (tbl == NULL || name == NULL || namesize == 0 || data == NULL || datasize == 0) { errno = EINVAL; return false; } qhasharr_data_t *tbldata = tbl->data; qhasharr_slot_t *tblslots = get_slots(tbl); // check full if (tbldata->usedslots >= tbldata->maxslots) { errno = ENOBUFS; return false; } // get hash integer uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots; // check, is slot empty if (tblslots[hash].count == 0) { // empty slot // put data if (put_data(tbl, hash, hash, name, namesize, data, datasize, 1) == false) { return false; } } else if (tblslots[hash].count > 0) { // same key or hash collision // check same key; int idx = get_idx(tbl, name, namesize, hash); if (idx >= 0) { // same key // remove and recall qhasharr_remove_by_idx(tbl, idx); return qhasharr_put_by_obj(tbl, name, namesize, data, datasize); } else { // no same key but hash collision // find empty slot int idx = find_avail(tbl, hash); if (idx < 0) { errno = ENOBUFS; return false; } // put data. -1 is used for collision resolution (idx != hash); if (put_data(tbl, idx, hash, name, namesize, data, datasize, COLLISION_MARK) == false) { return false; } // increase counter from leading slot tblslots[hash].count++; } } else { // collision key or extended block // find empty slot int idx = find_avail(tbl, hash + 1); if (idx < 0) { errno = ENOBUFS; return false; } // move the slot copy_slot(tbl, idx, hash); remove_slot(tbl, hash); // adjust the link chain if (tblslots[idx].link != -1) { tblslots[tblslots[idx].link].hash = idx; } if (tblslots[idx].count == EXTBLOCK_MARK) { tblslots[tblslots[idx].hash].link = idx; } // store data if (put_data(tbl, hash, hash, name, namesize, data, datasize, 1) == false) { return false; } } return true; }