static bool _put_data(qhasharr_t *tbl, int idx, unsigned int hash, const char *key, size_t key_size, const void *value, size_t val_size, int count) { size_t tmp_size = 0; qhasharr_slot_t *_tbl_slots = NULL; qhasharr_init(tbl, &_tbl_slots); // check if used if (_tbl_slots[idx].count != 0) { errno = EFAULT; return false; } unsigned char keymd5[16]; qhashmd5(key, key_size, keymd5); // store key _tbl_slots[idx].count = count; _tbl_slots[idx].hash = hash; tmp_size = (key_size <= _Q_HASHARR_KEYSIZE) ? key_size : _Q_HASHARR_KEYSIZE; memcpy(_tbl_slots[idx].data.pair.key, key, tmp_size); memcpy((char *)_tbl_slots[idx].data.pair.keymd5, (char *)keymd5, 16); _tbl_slots[idx].data.pair.keylen = key_size; _tbl_slots[idx].link = -1; // store value int newidx; size_t savesize; for (newidx = idx, savesize = 0; savesize < val_size;) { if (savesize > 0) // find next empty slot { int tmpidx = _find_empty(tbl, newidx + 1); if (tmpidx < 0) { _remove_data(tbl, idx); errno = ENOBUFS; return false; } // clear & set memset((void *)(&_tbl_slots[tmpidx]), '\0', sizeof(qhasharr_slot_t)); _tbl_slots[tmpidx].count = -2; // extended data block _tbl_slots[tmpidx].hash = newidx; // prev link _tbl_slots[tmpidx].link = -1; // end block mark _tbl_slots[tmpidx].size = 0; _tbl_slots[newidx].link = tmpidx; // link chain newidx = tmpidx; } // copy data size_t copysize = val_size - savesize; if (_tbl_slots[newidx].count == -2) { // extended value //if (copysize > sizeof(struct _Q_HASHARR_SLOT_EXT)) { // copysize = sizeof(struct _Q_HASHARR_SLOT_EXT); //} if (copysize > sizeof(union _slot_data)) { copysize = sizeof(union _slot_data); } memcpy(_tbl_slots[newidx].data.ext.value, (char*)value + savesize, copysize); } else { // first slot if (copysize > _Q_HASHARR_VALUESIZE) { copysize = _Q_HASHARR_VALUESIZE; } memcpy(_tbl_slots[newidx].data.pair.value, (char*)value + savesize, copysize); // increase stored key counter tbl->num++; } _tbl_slots[newidx].size = copysize; savesize += copysize; // increase used slot counter tbl->usedslots++; } return true; }
static bool _put_data(qhasharr_t *tbl, int idx, unsigned int hash, const char *key, const void *value, size_t size, int count) { // check if used if (tbl->slots[idx].count != 0) { DEBUG("hasharr: BUG found."); errno = EFAULT; return false; } size_t keylen = strlen(key); unsigned char keymd5[16]; qhashmd5(key, keylen, keymd5); // store key tbl->slots[idx].count = count; tbl->slots[idx].hash = hash; strncpy(tbl->slots[idx].data.pair.key, key, _Q_HASHARR_KEYSIZE); memcpy((char *)tbl->slots[idx].data.pair.keymd5, (char *)keymd5, 16); tbl->slots[idx].data.pair.keylen = keylen; tbl->slots[idx].link = -1; // store value int newidx; size_t savesize; for (newidx = idx, savesize = 0; savesize < size;) { if (savesize > 0) { // find next empty slot int tmpidx = _find_empty(tbl, newidx + 1); if (tmpidx < 0) { DEBUG("hasharr: Can't expand slot for key %s.", key); _remove_data(tbl, idx); errno = ENOBUFS; return false; } // clear & set memset((void *)(&tbl->slots[tmpidx]), '\0', sizeof(qhasharr_slot_t)); tbl->slots[tmpidx].count = -2; // extended data block tbl->slots[tmpidx].hash = newidx; // prev link tbl->slots[tmpidx].link = -1; // end block mark tbl->slots[tmpidx].size = 0; tbl->slots[newidx].link = tmpidx; // link chain DEBUG("hasharr: slot %d is linked to slot %d for key %s.", tmpidx, newidx, key); newidx = tmpidx; } // copy data size_t copysize = size - savesize; if (tbl->slots[newidx].count == -2) { // extended value if (copysize > sizeof(struct _Q_HASHARR_SLOT_EXT)) { copysize = sizeof(struct _Q_HASHARR_SLOT_EXT); } memcpy(tbl->slots[newidx].data.ext.value, value + savesize, copysize); } else { // first slot if (copysize > _Q_HASHARR_VALUESIZE) { copysize = _Q_HASHARR_VALUESIZE; } memcpy(tbl->slots[newidx].data.pair.value, value + savesize, copysize); // increase stored key counter tbl->num++; } tbl->slots[newidx].size = copysize; savesize += copysize; // increase used slot counter 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; }