/** * 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; }
/** * 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; }
/** * 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->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; }