Example #1
0
/**
 * 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);
}
Example #2
0
/**
 * 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);
}
Example #3
0
/**
 * 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);
}
Example #4
0
/**
 * 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;
}
Example #5
0
// 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;
}
Example #6
0
/**
 * 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;
}
Example #7
0
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;
}
Example #8
0
/**
 * 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;
}
Example #9
0
/**
 * 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;
}
Example #10
0
/**
 * 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;
}
Example #11
0
/**
 * 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;
}
Example #12
0
/**
 * 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;
}
Example #13
0
/**
 * 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;
}
Example #14
0
/**
 * 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;
}