+/* Start by using a random zone to spread the load: returns the offset. */
+static uint64_t random_zone(struct tdb_context *tdb)
+{
+ struct free_zone_header zhdr;
+ tdb_off_t off = sizeof(struct tdb_header);
+ tdb_len_t half_bits;
+ uint64_t randbits = 0;
+ unsigned int i;
+
+ for (i = 0; i < 64; i += fls64(RAND_MAX))
+ randbits ^= ((uint64_t)random()) << i;
+
+ /* FIXME: Does this work? Test! */
+ half_bits = max_zone_bits(tdb) - 1;
+ do {
+ /* Pick left or right side (not outside file) */
+ if ((randbits & 1)
+ && !tdb->methods->oob(tdb, off + (1ULL << half_bits)
+ + sizeof(zhdr), true)) {
+ off += 1ULL << half_bits;
+ }
+ randbits >>= 1;
+
+ if (tdb_read_convert(tdb, off, &zhdr, sizeof(zhdr)) == -1)
+ return TDB_OFF_ERR;
+
+ if (zhdr.zone_bits == half_bits)
+ return off;
+
+ half_bits--;
+ } while (half_bits >= INITIAL_ZONE_BITS);
+
+ tdb->ecode = TDB_ERR_CORRUPT;
+ tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
+ "random_zone: zone at %llu smaller than %u bits?",
+ (long long)off, INITIAL_ZONE_BITS);
+ return TDB_OFF_ERR;
+}
+
+int tdb_zone_init(struct tdb_context *tdb)
+{
+ tdb->zone_off = random_zone(tdb);
+ if (tdb->zone_off == TDB_OFF_ERR)
+ return -1;
+ if (tdb_read_convert(tdb, tdb->zone_off,
+ &tdb->zhdr, sizeof(tdb->zhdr)) == -1)
+ return -1;
+ return 0;
+}
+
+/* Where's the header, given a zone size of 1 << zone_bits? */
+static tdb_off_t zone_off(tdb_off_t off, unsigned int zone_bits)
+{
+ off -= sizeof(struct tdb_header);
+ return (off & ~((1ULL << zone_bits) - 1)) + sizeof(struct tdb_header);
+}
+
+/* Offset of a given bucket. */
+/* FIXME: bucket can be "unsigned" everywhere, or even uint8/16. */
+tdb_off_t bucket_off(tdb_off_t zone_off, tdb_off_t bucket)
+{
+ return zone_off
+ + sizeof(struct free_zone_header)
+ + bucket * sizeof(tdb_off_t);