- tdb_off_t newoff, i;
- uint64_t h, num = 1ULL << tdb->header.v.hash_bits;
- struct tdb_used_record pad, *r;
-
- /* FIXME: We should do this without holding locks throughout. */
- if (tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false) == -1)
- return;
-
- /* Someone else enlarged for us? Nothing to do. */
- if ((1ULL << tdb->header.v.hash_bits) != num)
- goto unlock;
-
- newoff = alloc(tdb, 0, num * 2, 0, false);
- if (unlikely(newoff == TDB_OFF_ERR))
- goto unlock;
- if (unlikely(newoff == 0)) {
- if (tdb_expand(tdb, 0, num * 2, false) == -1)
- goto unlock;
- newoff = alloc(tdb, 0, num * 2, 0, false);
- if (newoff == TDB_OFF_ERR || newoff == 0)
- goto unlock;
- }
-
- /* FIXME: If the space before is empty, we know this is in its ideal
- * location. We can steal a bit from the pointer to avoid rehash. */
- for (i = tdb_find_nonzero_off(tdb, tdb->header.v.hash_off, num);
- i < num;
- i += tdb_find_nonzero_off(tdb, tdb->header.v.hash_off
- + i*sizeof(tdb_off_t), num - i)) {
- tdb_off_t off;
- off = tdb_read_off(tdb, tdb->header.v.hash_off
- + i*sizeof(tdb_off_t));
- if (unlikely(off == TDB_OFF_ERR))
- goto unlock;
- if (unlikely(!off)) {
- tdb->ecode = TDB_ERR_CORRUPT;
- tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
- "find_bucket_and_lock: zero hash bucket!\n");
- goto unlock;
- }
- h = hash_record(tdb, off);
- /* FIXME: Encode extra hash bits! */
- if (tdb_write_off(tdb, newoff
- + (h & ((num * 2) - 1)) * sizeof(uint64_t),
- off) == -1)
- goto unlock;
- }
-
- /* Free up old hash. */
- r = tdb_get(tdb, tdb->header.v.hash_off, &pad, sizeof(*r));
- if (!r)
- goto unlock;
- add_free_record(tdb, tdb->header.v.hash_off,
- rec_data_length(r) + rec_extra_padding(r));
-
- /* Now we write the modified header. */
- tdb->header.v.generation++;
- tdb->header.v.hash_bits++;
- tdb->header.v.hash_off = newoff;
- tdb_write_convert(tdb, offsetof(struct tdb_header, v),
- &tdb->header.v, sizeof(tdb->header.v));
-unlock:
- tdb_allrecord_unlock(tdb, F_WRLCK);