]> git.ozlabs.org Git - ccan/blobdiff - ccan/tdb2/free.c
tdb2: add missing prototype, move tdb_firstkey/tdb_nextkey to traverse.c
[ccan] / ccan / tdb2 / free.c
index 9af6f5d04ebf3afb6673fcaf11694e290e52d6df..361171902680ece2441e698a3b960df6ddb781de 100644 (file)
@@ -164,13 +164,10 @@ tdb_off_t bucket_off(tdb_off_t zone_off, tdb_off_t bucket)
 /* Returns free_buckets + 1, or list number to search. */
 static tdb_off_t find_free_head(struct tdb_context *tdb, tdb_off_t bucket)
 {
-       tdb_off_t b;
-
        /* Speculatively search for a non-zero bucket. */
-       b = tdb_find_nonzero_off(tdb, bucket_off(tdb->zone_off, bucket),
-                                BUCKETS_FOR_ZONE(tdb->zhdr.zone_bits) + 1
-                                - bucket);
-       return bucket + b;
+       return tdb_find_nonzero_off(tdb, bucket_off(tdb->zone_off, 0),
+                                   bucket,
+                                   BUCKETS_FOR_ZONE(tdb->zhdr.zone_bits) + 1);
 }
 
 /* Remove from free bucket. */
@@ -510,10 +507,10 @@ int set_header(struct tdb_context *tdb,
 {
        uint64_t keybits = (fls64(keylen) + 1) / 2;
 
-       /* Use top bits of hash, so it's independent of hash table size. */
+       /* Use bottom bits of hash, so it's independent of hash table size. */
        rec->magic_and_meta
                = zone_bits
-               | ((hash >> 59) << 6)
+               | ((hash & ((1 << 5)-1)) << 6)
                | ((actuallen - (keylen + datalen)) << 11)
                | (keybits << 43)
                | (TDB_MAGIC << 48);
@@ -533,53 +530,6 @@ int set_header(struct tdb_context *tdb,
        return 0;
 }
 
-static tdb_len_t adjust_size(size_t keylen, size_t datalen, bool growing)
-{
-       tdb_len_t size = keylen + datalen;
-
-       if (size < MIN_DATA_LEN)
-               size = MIN_DATA_LEN;
-
-       /* Overallocate if this is coming from an enlarging store. */
-       if (growing)
-               size += datalen / 2;
-
-       /* Round to next uint64_t boundary. */
-       return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL);
-}
-
-/* If this fails, try tdb_expand. */
-tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen,
-               uint64_t hash, bool growing)
-{
-       tdb_off_t off;
-       tdb_len_t size, actual;
-       struct tdb_used_record rec;
-
-       /* We don't want header to change during this! */
-       assert(tdb->header_uptodate);
-
-       size = adjust_size(keylen, datalen, growing);
-
-       off = get_free(tdb, size, &actual);
-       if (unlikely(off == TDB_OFF_ERR || off == 0))
-               return off;
-
-       /* Some supergiant values can't be encoded. */
-       /* FIXME: Check before, and limit actual in get_free. */
-       if (set_header(tdb, &rec, keylen, datalen, actual, hash,
-                      tdb->zhdr.zone_bits) != 0) {
-               add_free_record(tdb, tdb->zhdr.zone_bits, off,
-                               sizeof(rec) + actual);
-               return TDB_OFF_ERR;
-       }
-
-       if (tdb_write_convert(tdb, off, &rec, sizeof(rec)) != 0)
-               return TDB_OFF_ERR;
-       
-       return off;
-}
-
 static bool zones_happy(struct tdb_context *tdb)
 {
        /* FIXME: look at distribution of zones. */
@@ -594,8 +544,7 @@ static tdb_len_t overhead(unsigned int zone_bits)
 }
 
 /* Expand the database (by adding a zone). */
-int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen,
-              bool growing)
+static int tdb_expand(struct tdb_context *tdb, tdb_len_t size)
 {
        uint64_t old_size;
        tdb_off_t off;
@@ -606,8 +555,7 @@ int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen,
        bool enlarge_zone;
 
        /* We need room for the record header too. */
-       wanted = sizeof(struct tdb_used_record)
-               + (adjust_size(klen, dlen, growing)<<TDB_COMFORT_FACTOR_BITS);
+       wanted = sizeof(struct tdb_used_record) + size;
 
        /* Only one person can expand file at a time. */
        if (tdb_lock_expand(tdb, F_WRLCK) != 0)
@@ -667,6 +615,10 @@ int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen,
        if (add_free_record(tdb, zone_bits, off, tdb->map_size-1-off) == -1)
                goto fail;
 
+       /* Try allocating from this zone now. */
+       tdb->zone_off = old_size - 1;
+       tdb->zhdr = zhdr;
+
 success:
        tdb_unlock_expand(tdb, F_WRLCK);
        return 0;
@@ -675,3 +627,57 @@ fail:
        tdb_unlock_expand(tdb, F_WRLCK);
        return -1;
 }
+
+static tdb_len_t adjust_size(size_t keylen, size_t datalen, bool growing)
+{
+       tdb_len_t size = keylen + datalen;
+
+       if (size < MIN_DATA_LEN)
+               size = MIN_DATA_LEN;
+
+       /* Overallocate if this is coming from an enlarging store. */
+       if (growing)
+               size += datalen / 2;
+
+       /* Round to next uint64_t boundary. */
+       return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL);
+}
+
+/* This won't fail: it will expand the database if it has to. */
+tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen,
+               uint64_t hash, bool growing)
+{
+       tdb_off_t off;
+       tdb_len_t size, actual;
+       struct tdb_used_record rec;
+
+       /* We can't hold pointers during this: we could unmap! */
+       assert(!tdb->direct_access);
+
+       size = adjust_size(keylen, datalen, growing);
+
+again:
+       off = get_free(tdb, size, &actual);
+       if (unlikely(off == TDB_OFF_ERR))
+               return off;
+
+       if (unlikely(off == 0)) {
+               if (tdb_expand(tdb, size) == -1)
+                       return TDB_OFF_ERR;
+               goto again;
+       }
+
+       /* Some supergiant values can't be encoded. */
+       /* FIXME: Check before, and limit actual in get_free. */
+       if (set_header(tdb, &rec, keylen, datalen, actual, hash,
+                      tdb->zhdr.zone_bits) != 0) {
+               add_free_record(tdb, tdb->zhdr.zone_bits, off,
+                               sizeof(rec) + actual);
+               return TDB_OFF_ERR;
+       }
+
+       if (tdb_write_convert(tdb, off, &rec, sizeof(rec)) != 0)
+               return TDB_OFF_ERR;
+       
+       return off;
+}