Commit d70577b6 authored by Rusty Russell's avatar Rusty Russell

tdb2: get rid of zones

Zones were a bad idea.  They mean we can't simply add stuff to the end
of the file (which transactions relied upon), and there's no good heuristic
in how to size them.

This patch reverts us to a single free table.
parent 2ecf943a
...@@ -237,7 +237,7 @@ static bool check_hash_tree(struct tdb_context *tdb, ...@@ -237,7 +237,7 @@ static bool check_hash_tree(struct tdb_context *tdb,
goto fail; goto fail;
/* Bottom bits must match header. */ /* Bottom bits must match header. */
if ((h & ((1 << 5)-1)) != rec_hash(&rec)) { if ((h & ((1 << 11)-1)) != rec_hash(&rec)) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: Bad hash magic at" "tdb_check: Bad hash magic at"
" offset %llu (0x%llx vs 0x%llx)\n", " offset %llu (0x%llx vs 0x%llx)\n",
...@@ -267,7 +267,8 @@ static bool check_hash(struct tdb_context *tdb, ...@@ -267,7 +267,8 @@ static bool check_hash(struct tdb_context *tdb,
0, 0, used, num_used, &num_found)) 0, 0, used, num_used, &num_found))
return false; return false;
if (num_found != num_used) { /* 1 is for the free list. */
if (num_found != num_used - 1) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: Not all entries are in hash\n"); "tdb_check: Not all entries are in hash\n");
return false; return false;
...@@ -278,8 +279,7 @@ static bool check_hash(struct tdb_context *tdb, ...@@ -278,8 +279,7 @@ static bool check_hash(struct tdb_context *tdb,
static bool check_free(struct tdb_context *tdb, static bool check_free(struct tdb_context *tdb,
tdb_off_t off, tdb_off_t off,
const struct tdb_free_record *frec, const struct tdb_free_record *frec,
tdb_off_t prev, tdb_off_t prev, unsigned int bucket)
tdb_off_t zone_off, unsigned int bucket)
{ {
if (frec_magic(frec) != TDB_FREE_MAGIC) { if (frec_magic(frec) != TDB_FREE_MAGIC) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
...@@ -291,20 +291,11 @@ static bool check_free(struct tdb_context *tdb, ...@@ -291,20 +291,11 @@ static bool check_free(struct tdb_context *tdb,
+ frec->data_len+sizeof(struct tdb_used_record), + frec->data_len+sizeof(struct tdb_used_record),
false)) false))
return false; return false;
if (off < zone_off || off >= zone_off + (1ULL<<frec_zone_bits(frec))) { if (size_to_bucket(frec->data_len) != bucket) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: offset %llu outside zone %llu-%llu\n",
(long long)off,
(long long)zone_off,
(long long)zone_off + (1ULL<<frec_zone_bits(frec)));
return false;
}
if (size_to_bucket(frec_zone_bits(frec), frec->data_len) != bucket) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: offset %llu in wrong bucket %u vs %u\n", "tdb_check: offset %llu in wrong bucket %u vs %u\n",
(long long)off, (long long)off,
bucket, bucket, size_to_bucket(frec->data_len));
size_to_bucket(frec_zone_bits(frec), frec->data_len));
return false; return false;
} }
if (prev != frec->prev) { if (prev != frec->prev) {
...@@ -317,31 +308,41 @@ static bool check_free(struct tdb_context *tdb, ...@@ -317,31 +308,41 @@ static bool check_free(struct tdb_context *tdb,
return true; return true;
} }
static tdb_len_t check_free_list(struct tdb_context *tdb, static bool check_free_list(struct tdb_context *tdb,
tdb_off_t zone_off, tdb_off_t flist_off,
tdb_off_t free[], tdb_off_t free[],
size_t num_free, size_t num_free,
size_t *num_found) size_t *num_found)
{ {
struct free_zone_header zhdr; struct tdb_freelist flist;
tdb_off_t h; tdb_off_t h;
unsigned int i; unsigned int i;
if (tdb_read_convert(tdb, zone_off, &zhdr, sizeof(zhdr)) == -1) if (tdb_read_convert(tdb, flist_off, &flist, sizeof(flist)) == -1)
return TDB_OFF_ERR; return false;
if (rec_magic(&flist.hdr) != TDB_MAGIC
|| rec_key_length(&flist.hdr) != 0
|| rec_data_length(&flist.hdr) != sizeof(flist) - sizeof(flist.hdr)
|| rec_hash(&flist.hdr) != 1) {
tdb->log(tdb, TDB_DEBUG_ERROR,
tdb->log_priv,
"tdb_check: Invalid header on free list\n");
return false;
}
for (i = 0; i <= BUCKETS_FOR_ZONE(zhdr.zone_bits); i++) { for (i = 0; i < TDB_FREE_BUCKETS; i++) {
tdb_off_t off, prev = 0, *p; tdb_off_t off, prev = 0, *p;
struct tdb_free_record f; struct tdb_free_record f;
h = bucket_off(zone_off, i); h = bucket_off(flist_off, i);
for (off = tdb_read_off(tdb, h); off; off = f.next) { for (off = tdb_read_off(tdb, h); off; off = f.next) {
if (off == TDB_OFF_ERR) if (off == TDB_OFF_ERR)
return TDB_OFF_ERR; return false;
if (tdb_read_convert(tdb, off, &f, sizeof(f))) if (tdb_read_convert(tdb, off, &f, sizeof(f)))
return TDB_OFF_ERR; return false;
if (!check_free(tdb, off, &f, prev, zone_off, i)) if (!check_free(tdb, off, &f, prev, i))
return TDB_OFF_ERR; return false;
/* FIXME: Check hash bits */ /* FIXME: Check hash bits */
p = asearch(&off, free, num_free, off_cmp); p = asearch(&off, free, num_free, off_cmp);
...@@ -351,7 +352,7 @@ static tdb_len_t check_free_list(struct tdb_context *tdb, ...@@ -351,7 +352,7 @@ static tdb_len_t check_free_list(struct tdb_context *tdb,
"tdb_check: Invalid offset" "tdb_check: Invalid offset"
" %llu in free table\n", " %llu in free table\n",
(long long)off); (long long)off);
return TDB_OFF_ERR; return false;
} }
/* Mark it invalid. */ /* Mark it invalid. */
*p ^= 1; *p ^= 1;
...@@ -359,79 +360,38 @@ static tdb_len_t check_free_list(struct tdb_context *tdb, ...@@ -359,79 +360,38 @@ static tdb_len_t check_free_list(struct tdb_context *tdb,
prev = off; prev = off;
} }
} }
return 1ULL << zhdr.zone_bits; return true;
} }
static tdb_off_t check_zone(struct tdb_context *tdb, tdb_off_t zone_off, static bool check_linear(struct tdb_context *tdb,
tdb_off_t **used, size_t *num_used, tdb_off_t **used, size_t *num_used,
tdb_off_t **free, size_t *num_free, tdb_off_t **free, size_t *num_free)
unsigned int *max_zone_bits)
{ {
struct free_zone_header zhdr; tdb_off_t off;
tdb_off_t off, hdrlen, end;
tdb_len_t len; tdb_len_t len;
if (tdb_read_convert(tdb, zone_off, &zhdr, sizeof(zhdr)) == -1) for (off = sizeof(struct tdb_header); off < tdb->map_size; off += len) {
return TDB_OFF_ERR;
if (zhdr.zone_bits < INITIAL_ZONE_BITS) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"check: bad zone_bits %llu at zone %llu\n",
(long long)zhdr.zone_bits, (long long)zone_off);
return TDB_OFF_ERR;
}
/* Zone bits can only increase... */
if (zhdr.zone_bits > *max_zone_bits)
*max_zone_bits = zhdr.zone_bits;
else if (zhdr.zone_bits < *max_zone_bits) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"check: small zone_bits %llu at zone %llu\n",
(long long)zhdr.zone_bits, (long long)zone_off);
return TDB_OFF_ERR;
}
/* Zone header must be within file! */
hdrlen = sizeof(zhdr)
+ (BUCKETS_FOR_ZONE(zhdr.zone_bits) + 1) * sizeof(tdb_off_t);
if (tdb->methods->oob(tdb, zone_off + hdrlen, true))
return TDB_OFF_ERR;
end = zone_off + (1ULL << zhdr.zone_bits);
if (end > tdb->map_size)
end = tdb->map_size;
for (off = zone_off + hdrlen; off < end; off += len) {
union { union {
struct tdb_used_record u; struct tdb_used_record u;
struct tdb_free_record f; struct tdb_free_record f;
} pad, *p; } pad, *p;
p = tdb_get(tdb, off, &pad, sizeof(pad)); p = tdb_get(tdb, off, &pad, sizeof(pad));
if (!p) if (!p)
return TDB_OFF_ERR; return false;
if (frec_magic(&p->f) == TDB_FREE_MAGIC if (frec_magic(&p->f) == TDB_FREE_MAGIC
|| frec_magic(&p->f) == TDB_COALESCING_MAGIC) { || frec_magic(&p->f) == TDB_COALESCING_MAGIC) {
if (frec_zone_bits(&p->f) != zhdr.zone_bits) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: Bad free zone bits %u"
" at offset %llu\n",
frec_zone_bits(&p->f),
(long long)off);
return TDB_OFF_ERR;
}
len = sizeof(p->u) + p->f.data_len; len = sizeof(p->u) + p->f.data_len;
if (off + len > zone_off + (1ULL << zhdr.zone_bits)) { if (off + len > tdb->map_size) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: free overlength %llu" "tdb_check: free overlength %llu"
" at offset %llu\n", " at offset %llu\n",
(long long)len, (long long)off); (long long)len, (long long)off);
return TDB_OFF_ERR; return false;
} }
/* This record is free! */ /* This record is free! */
if (frec_magic(&p->f) == TDB_FREE_MAGIC if (frec_magic(&p->f) == TDB_FREE_MAGIC
&& !append(free, num_free, off)) && !append(free, num_free, off))
return TDB_OFF_ERR; return false;
} else { } else {
uint64_t klen, dlen, extra; uint64_t klen, dlen, extra;
...@@ -442,32 +402,23 @@ static tdb_off_t check_zone(struct tdb_context *tdb, tdb_off_t zone_off, ...@@ -442,32 +402,23 @@ static tdb_off_t check_zone(struct tdb_context *tdb, tdb_off_t zone_off,
" at offset %llu\n", " at offset %llu\n",
(long long)rec_magic(&p->u), (long long)rec_magic(&p->u),
(long long)off); (long long)off);
return TDB_OFF_ERR; return false;
}
if (rec_zone_bits(&p->u) != zhdr.zone_bits) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: Bad zone bits %u"
" at offset %llu\n",
rec_zone_bits(&p->u),
(long long)off);
return TDB_OFF_ERR;
} }
if (!append(used, num_used, off)) if (!append(used, num_used, off))
return TDB_OFF_ERR; return false;
klen = rec_key_length(&p->u); klen = rec_key_length(&p->u);
dlen = rec_data_length(&p->u); dlen = rec_data_length(&p->u);
extra = rec_extra_padding(&p->u); extra = rec_extra_padding(&p->u);
len = sizeof(p->u) + klen + dlen + extra; len = sizeof(p->u) + klen + dlen + extra;
if (off + len > zone_off + (1ULL << zhdr.zone_bits)) { if (off + len > tdb->map_size) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: used overlength %llu" "tdb_check: used overlength %llu"
" at offset %llu\n", " at offset %llu\n",
(long long)len, (long long)off); (long long)len, (long long)off);
return TDB_OFF_ERR; return false;
} }
if (len < sizeof(p->f)) { if (len < sizeof(p->f)) {
...@@ -475,11 +426,11 @@ static tdb_off_t check_zone(struct tdb_context *tdb, tdb_off_t zone_off, ...@@ -475,11 +426,11 @@ static tdb_off_t check_zone(struct tdb_context *tdb, tdb_off_t zone_off,
"tdb_check: too short record %llu at" "tdb_check: too short record %llu at"
" %llu\n", " %llu\n",
(long long)len, (long long)off); (long long)len, (long long)off);
return TDB_OFF_ERR; return false;
} }
} }
} }
return off - zone_off; return true;
} }
/* FIXME: call check() function. */ /* FIXME: call check() function. */
...@@ -487,10 +438,8 @@ int tdb_check(struct tdb_context *tdb, ...@@ -487,10 +438,8 @@ int tdb_check(struct tdb_context *tdb,
int (*check)(TDB_DATA key, TDB_DATA data, void *private_data), int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
void *private_data) void *private_data)
{ {
tdb_off_t *free = NULL, *used = NULL, off; tdb_off_t *free = NULL, *used = NULL;
tdb_len_t len;
size_t num_free = 0, num_used = 0, num_found = 0; size_t num_free = 0, num_used = 0, num_found = 0;
unsigned max_zone_bits = INITIAL_ZONE_BITS;
if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false) != 0) if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false) != 0)
return -1; return -1;
...@@ -504,26 +453,16 @@ int tdb_check(struct tdb_context *tdb, ...@@ -504,26 +453,16 @@ int tdb_check(struct tdb_context *tdb,
goto fail; goto fail;
/* First we do a linear scan, checking all records. */ /* First we do a linear scan, checking all records. */
for (off = sizeof(struct tdb_header); if (!check_linear(tdb, &used, &num_used, &free, &num_free))
off < tdb->map_size;
off += len) {
len = check_zone(tdb, off, &used, &num_used, &free, &num_free,
&max_zone_bits);
if (len == TDB_OFF_ERR)
goto fail; goto fail;
}
/* FIXME: Check key uniqueness? */ /* FIXME: Check key uniqueness? */
if (!check_hash(tdb, used, num_used)) if (!check_hash(tdb, used, num_used))
goto fail; goto fail;
for (off = sizeof(struct tdb_header); if (!check_free_list(tdb, tdb->flist_off, free, num_free, &num_found))
off < tdb->map_size - 1;
off += len) {
len = check_free_list(tdb, off, free, num_free, &num_found);
if (len == TDB_OFF_ERR)
goto fail; goto fail;
}
if (num_found != num_free) { if (num_found != num_free) {
tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
"tdb_check: Not all entries are in free table\n"); "tdb_check: Not all entries are in free table\n");
......
This diff is collapsed.
...@@ -257,7 +257,7 @@ static int tdb_nest_lock(struct tdb_context *tdb, tdb_off_t offset, int ltype, ...@@ -257,7 +257,7 @@ static int tdb_nest_lock(struct tdb_context *tdb, tdb_off_t offset, int ltype,
{ {
struct tdb_lock_type *new_lck; struct tdb_lock_type *new_lck;
if (offset >= TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE + tdb->map_size / 8) { if (offset > TDB_HASH_LOCK_START + TDB_HASH_LOCK_RANGE + tdb->map_size / 8) {
tdb->ecode = TDB_ERR_LOCK; tdb->ecode = TDB_ERR_LOCK;
tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv, tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
"tdb_nest_lock: invalid offset %llu ltype=%d\n", "tdb_nest_lock: invalid offset %llu ltype=%d\n",
......
...@@ -92,10 +92,8 @@ typedef uint64_t tdb_off_t; ...@@ -92,10 +92,8 @@ typedef uint64_t tdb_off_t;
/* And 8 entries in each group, ie 8 groups per sublevel. */ /* And 8 entries in each group, ie 8 groups per sublevel. */
#define TDB_HASH_GROUP_BITS 3 #define TDB_HASH_GROUP_BITS 3
/* We start with a 64k-sized zone. */ /* Extend file by least 32 times larger than needed. */
#define INITIAL_ZONE_BITS 16 #define TDB_EXTENSION_FACTOR 32
/* Try to create zones at least 32 times larger than allocations. */
#define TDB_COMFORT_FACTOR_BITS 5
/* We steal bits from the offsets to store hash info. */ /* We steal bits from the offsets to store hash info. */
#define TDB_OFF_HASH_GROUP_MASK ((1ULL << TDB_HASH_GROUP_BITS) - 1) #define TDB_OFF_HASH_GROUP_MASK ((1ULL << TDB_HASH_GROUP_BITS) - 1)
...@@ -111,14 +109,13 @@ typedef uint64_t tdb_off_t; ...@@ -111,14 +109,13 @@ typedef uint64_t tdb_off_t;
#define TDB_OFF_MASK \ #define TDB_OFF_MASK \
(((1ULL << (64 - TDB_OFF_UPPER_STEAL)) - 1) - TDB_OFF_HASH_GROUP_MASK) (((1ULL << (64 - TDB_OFF_UPPER_STEAL)) - 1) - TDB_OFF_HASH_GROUP_MASK)
/* How many buckets in a free list: see size_to_bucket(). */
#define TDB_FREE_BUCKETS (64 - TDB_OFF_UPPER_STEAL)
/* We have to be able to fit a free record here. */ /* We have to be able to fit a free record here. */
#define TDB_MIN_DATA_LEN \ #define TDB_MIN_DATA_LEN \
(sizeof(struct tdb_free_record) - sizeof(struct tdb_used_record)) (sizeof(struct tdb_free_record) - sizeof(struct tdb_used_record))
/* We ensure buckets up to size 1 << (zone_bits - TDB_COMFORT_FACTOR_BITS). */
/* FIXME: test this matches size_to_bucket! */
#define BUCKETS_FOR_ZONE(zone_bits) ((zone_bits) + 2 - TDB_COMFORT_FACTOR_BITS)
#if !HAVE_BSWAP_64 #if !HAVE_BSWAP_64
static inline uint64_t bswap_64(uint64_t x) static inline uint64_t bswap_64(uint64_t x)
{ {
...@@ -138,8 +135,7 @@ struct tdb_used_record { ...@@ -138,8 +135,7 @@ struct tdb_used_record {
magic: 16, (highest) magic: 16, (highest)
key_len_bits: 5, key_len_bits: 5,
extra_padding: 32 extra_padding: 32
hash_bits: 5, hash_bits: 11
zone_bits: 6 (lowest)
*/ */
uint64_t magic_and_meta; uint64_t magic_and_meta;
/* The bottom key_len_bits*2 are key length, rest is data length. */ /* The bottom key_len_bits*2 are key length, rest is data length. */
...@@ -166,14 +162,9 @@ static inline uint64_t rec_extra_padding(const struct tdb_used_record *r) ...@@ -166,14 +162,9 @@ static inline uint64_t rec_extra_padding(const struct tdb_used_record *r)
return (r->magic_and_meta >> 11) & 0xFFFFFFFF; return (r->magic_and_meta >> 11) & 0xFFFFFFFF;
} }
static inline unsigned int rec_zone_bits(const struct tdb_used_record *r)
{
return r->magic_and_meta & ((1 << 6) - 1);
}
static inline uint32_t rec_hash(const struct tdb_used_record *r) static inline uint32_t rec_hash(const struct tdb_used_record *r)
{ {
return (r->magic_and_meta >> 6) & ((1 << 5) - 1); return r->magic_and_meta & ((1 << 11) - 1);
} }
static inline uint16_t rec_magic(const struct tdb_used_record *r) static inline uint16_t rec_magic(const struct tdb_used_record *r)
...@@ -182,32 +173,17 @@ static inline uint16_t rec_magic(const struct tdb_used_record *r) ...@@ -182,32 +173,17 @@ static inline uint16_t rec_magic(const struct tdb_used_record *r)
} }
struct tdb_free_record { struct tdb_free_record {
uint64_t magic_and_meta; /* Bottom 6 bits are zone bits. */ uint64_t magic_and_meta;
uint64_t data_len; /* Not counting these two fields. */ uint64_t data_len; /* Not counting these two fields. */
/* This is why the minimum record size is 16 bytes. */ /* This is why the minimum record size is 16 bytes. */
uint64_t next, prev; uint64_t next, prev;
}; };
static inline unsigned int frec_zone_bits(const struct tdb_free_record *f)
{
return f->magic_and_meta & ((1 << 6) - 1);
}
static inline uint64_t frec_magic(const struct tdb_free_record *f) static inline uint64_t frec_magic(const struct tdb_free_record *f)
{ {
return f->magic_and_meta & ~((1ULL << 6) - 1); return f->magic_and_meta;
} }
/* Each zone has its set of free lists at the head.
*
* For each zone we have a series of per-size buckets, and a final bucket for
* "too big". */
struct free_zone_header {
/* How much does this zone cover? */
uint64_t zone_bits;
/* tdb_off_t buckets[free_buckets + 1] */
};
/* this is stored at the front of every database */ /* this is stored at the front of every database */
struct tdb_header { struct tdb_header {
char magic_food[64]; /* for /etc/magic */ char magic_food[64]; /* for /etc/magic */
...@@ -215,13 +191,19 @@ struct tdb_header { ...@@ -215,13 +191,19 @@ struct tdb_header {
uint64_t version; /* version of the code */ uint64_t version; /* version of the code */
uint64_t hash_test; /* result of hashing HASH_MAGIC. */ uint64_t hash_test; /* result of hashing HASH_MAGIC. */
uint64_t hash_seed; /* "random" seed written at creation time. */ uint64_t hash_seed; /* "random" seed written at creation time. */
tdb_off_t free_list; /* (First) free list. */
tdb_off_t reserved[28]; tdb_off_t reserved[27];
/* Top level hash table. */ /* Top level hash table. */
tdb_off_t hashtable[1ULL << TDB_TOPLEVEL_HASH_BITS]; tdb_off_t hashtable[1ULL << TDB_TOPLEVEL_HASH_BITS];
}; };
struct tdb_freelist {
struct tdb_used_record hdr;
tdb_off_t buckets[TDB_FREE_BUCKETS];
};
/* Information about a particular (locked) hash entry. */ /* Information about a particular (locked) hash entry. */
struct hash_info { struct hash_info {
/* Full hash value of entry. */ /* Full hash value of entry. */
...@@ -308,10 +290,8 @@ struct tdb_context { ...@@ -308,10 +290,8 @@ struct tdb_context {
/* Set if we are in a transaction. */ /* Set if we are in a transaction. */
struct tdb_transaction *transaction; struct tdb_transaction *transaction;
/* What zone of the tdb to use, for spreading load. */ /* What freelist are we using? */
uint64_t zone_off; uint64_t flist_off;
/* Cached copy of zone header. */
struct free_zone_header zhdr;
/* IO methods: changes for transactions. */ /* IO methods: changes for transactions. */
const struct tdb_methods *methods; const struct tdb_methods *methods;
...@@ -368,26 +348,25 @@ int delete_from_hash(struct tdb_context *tdb, struct hash_info *h); ...@@ -368,26 +348,25 @@ int delete_from_hash(struct tdb_context *tdb, struct hash_info *h);
bool is_subhash(tdb_off_t val); bool is_subhash(tdb_off_t val);
/* free.c: */ /* free.c: */
int tdb_zone_init(struct tdb_context *tdb); int tdb_flist_init(struct tdb_context *tdb);
/* If this fails, try tdb_expand. */ /* If this fails, try tdb_expand. */
tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen, tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen,
uint64_t hash, bool growing); uint64_t hash, bool growing);
/* Put this record in a free list. */ /* Put this record in a free list. */
int add_free_record(struct tdb_context *tdb, unsigned int zone_bits, int add_free_record(struct tdb_context *tdb,
tdb_off_t off, tdb_len_t len_with_header); tdb_off_t off, tdb_len_t len_with_header);
/* Set up header for a used record. */ /* Set up header for a used record. */
int set_header(struct tdb_context *tdb, int set_header(struct tdb_context *tdb,
struct tdb_used_record *rec, struct tdb_used_record *rec,
uint64_t keylen, uint64_t datalen, uint64_t keylen, uint64_t datalen,
uint64_t actuallen, unsigned hashlow, uint64_t actuallen, unsigned hashlow);
unsigned int zone_bits);
/* Used by tdb_check to verify. */ /* Used by tdb_check to verify. */
unsigned int size_to_bucket(unsigned int free_buckets, tdb_len_t data_len); unsigned int size_to_bucket(tdb_len_t data_len);
tdb_off_t bucket_off(tdb_off_t zone_off, tdb_off_t bucket); tdb_off_t bucket_off(tdb_off_t flist_off, unsigned bucket);
/* io.c: */ /* io.c: */
/* Initialize tdb->methods. */ /* Initialize tdb->methods. */
......
This diff is collapsed.
...@@ -80,9 +80,7 @@ static uint64_t random_number(struct tdb_context *tdb) ...@@ -80,9 +80,7 @@ static uint64_t random_number(struct tdb_context *tdb)
struct new_database { struct new_database {
struct tdb_header hdr; struct tdb_header hdr;
/* Initial free zone. */ struct tdb_freelist flist;
struct free_zone_header zhdr;
tdb_off_t free[BUCKETS_FOR_ZONE(INITIAL_ZONE_BITS) + 1];
}; };
/* initialise a new database */ /* initialise a new database */
...@@ -110,8 +108,10 @@ static int tdb_new_database(struct tdb_context *tdb, ...@@ -110,8 +108,10 @@ static int tdb_new_database(struct tdb_context *tdb,
memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable)); memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable));
/* Free is empty. */ /* Free is empty. */
newdb.zhdr.zone_bits = INITIAL_ZONE_BITS; newdb.hdr.free_list = offsetof(struct new_database, flist);
memset(newdb.free, 0, sizeof(newdb.free)); memset(&newdb.flist, 0, sizeof(newdb.flist));
set_header(NULL, &newdb.flist.hdr, 0,
sizeof(newdb.flist.buckets), sizeof(newdb.flist.buckets), 1);
/* Magic food */ /* Magic food */
memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food)); memset(newdb.hdr.magic_food, 0, sizeof(newdb.hdr.magic_food));
...@@ -176,7 +176,6 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, ...@@ -176,7 +176,6 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
tdb->log_priv = NULL; tdb->log_priv = NULL;
tdb->transaction = NULL; tdb->transaction = NULL;
tdb_hash_init(tdb); tdb_hash_init(tdb);
/* last_zone will be set below. */
tdb_io_init(tdb); tdb_io_init(tdb);
tdb_lock_init(tdb); tdb_lock_init(tdb);
...@@ -230,7 +229,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, ...@@ -230,7 +229,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
} }
tdb_convert(tdb, &hdr.hash_seed, sizeof(hdr.hash_seed)); tdb_convert(tdb, &hdr.hash_seed, sizeof(hdr.hash_seed));
tdb->hash_seed = hdr.hash_seed; tdb->hash_seed = hdr.hash_seed;
tdb_zone_init(tdb); tdb_flist_init(tdb);
return tdb; return tdb;
} }
...@@ -314,8 +313,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, ...@@ -314,8 +313,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
/* This make sure we have current map_size and mmap. */ /* This make sure we have current map_size and mmap. */
tdb->methods->oob(tdb, tdb->map_size + 1, true); tdb->methods->oob(tdb, tdb->map_size + 1, true);
/* Now we can pick a random free zone to start from. */ if (tdb_flist_init(tdb) == -1)
if (tdb_zone_init(tdb) == -1)
goto fail; goto fail;
tdb->next = tdbs; tdb->next = tdbs;
...@@ -358,8 +356,7 @@ static int update_rec_hdr(struct tdb_context *tdb, ...@@ -358,8 +356,7 @@ static int update_rec_hdr(struct tdb_context *tdb,
{ {
uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec); uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
if (set_header(tdb, rec, keylen, datalen, keylen + dataroom, h, if (set_header(tdb, rec, keylen, datalen, keylen + dataroom, h))
rec_zone_bits(rec)))
return -1; return -1;
return tdb_write_convert(tdb, off, rec, sizeof(*rec)); return tdb_write_convert(tdb, off, rec, sizeof(*rec));
...@@ -370,7 +367,6 @@ static int replace_data(struct tdb_context *tdb, ...@@ -370,7 +367,6 @@ static int replace_data(struct tdb_context *tdb,
struct hash_info *h, struct hash_info *h,
struct tdb_data key, struct tdb_data dbuf, struct tdb_data key, struct tdb_data dbuf,
tdb_off_t old_off, tdb_len_t old_room, tdb_off_t old_off, tdb_len_t old_room,
unsigned old_zone,
bool growing) bool growing)
{ {
tdb_off_t new_off; tdb_off_t new_off;
...@@ -382,7 +378,7 @@ static int replace_data(struct tdb_context *tdb, ...@@ -382,7 +378,7 @@ static int replace_data(struct tdb_context *tdb,
/* We didn't like the existing one: remove it. */ /* We didn't like the existing one: remove it. */
if (old_off) { if (old_off) {
add_free_record(tdb, old_zone, old_off, add_free_record(tdb, old_off,
sizeof(struct tdb_used_record) sizeof(struct tdb_used_record)
+ key.dsize + old_room); + key.dsize + old_room);
if (replace_in_hash(tdb, h, new_off) == -1) if (replace_in_hash(tdb, h, new_off) == -1)
...@@ -454,8 +450,7 @@ int tdb_store(struct tdb_context *tdb, ...@@ -454,8 +450,7 @@ int tdb_store(struct tdb_context *tdb,
} }
/* If we didn't use the old record, this implies we're growing. */ /* If we didn't use the old record, this implies we're growing. */
ret = replace_data(tdb, &h, key, dbuf, off, old_room, ret = replace_data(tdb, &h, key, dbuf, off, old_room, off != 0);
rec_zone_bits(&rec), off != 0);
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK); tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
return ret; return ret;
...@@ -524,8 +519,7 @@ int tdb_append(struct tdb_context *tdb, ...@@ -524,8 +519,7 @@ int tdb_append(struct tdb_context *tdb,
} }
/* If they're using tdb_append(), it implies they're growing record. */ /* If they're using tdb_append(), it implies they're growing record. */
ret = replace_data(tdb, &h, key, new_dbuf, off, ret = replace_data(tdb, &h, key, new_dbuf, off, old_room, true);
old_room, rec_zone_bits(&rec), true);
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK); tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
free(newdata); free(newdata);
...@@ -580,7 +574,7 @@ int tdb_delete(struct tdb_context *tdb, struct tdb_data key) ...@@ -580,7 +574,7 @@ int tdb_delete(struct tdb_context *tdb, struct tdb_data key)
goto unlock_err; goto unlock_err;
/* Free the deleted entry. */ /* Free the deleted entry. */
if (add_free_record(tdb, rec_zone_bits(&rec), off, if (add_free_record(tdb, off,
sizeof(struct tdb_used_record) sizeof(struct tdb_used_record)
+ rec_key_length(&rec) + rec_key_length(&rec)
+ rec_data_length(&rec) + rec_data_length(&rec)
......
...@@ -23,15 +23,10 @@ static void add(struct tdb_layout *layout, union tdb_layout_elem elem) ...@@ -23,15 +23,10 @@ static void add(struct tdb_layout *layout, union tdb_layout_elem elem)
layout->elem[layout->num_elems++] = elem; layout->elem[layout->num_elems++] = elem;
} }
void tdb_layout_add_zone(struct tdb_layout *layout, void tdb_layout_add_freelist(struct tdb_layout *layout)
unsigned int zone_bits,
bool fill_prev)
{ {
union tdb_layout_elem elem; union tdb_layout_elem elem;
if (fill_prev) elem.base.type = FREELIST;
tdb_layout_add_free(layout, 0);
elem.base.type = ZONE;
elem.zone.zone_bits = zone_bits;
add(layout, elem); add(layout, elem);
} }
...@@ -85,10 +80,9 @@ static tdb_len_t hashtable_len(struct tle_hashtable *htable) ...@@ -85,10 +80,9 @@ static tdb_len_t hashtable_len(struct tle_hashtable *htable)
+ htable->extra; + htable->extra;
} }
static tdb_len_t zone_header_len(struct tle_zone *zone) static tdb_len_t freelist_len(struct tle_freelist *flist)
{ {
return sizeof(struct free_zone_header) return sizeof(struct tdb_freelist);
+ sizeof(tdb_off_t) * (BUCKETS_FOR_ZONE(zone->zone_bits)+1);
} }
static void set_free_record(void *mem, tdb_len_t len) static void set_free_record(void *mem, tdb_len_t len)
...@@ -97,47 +91,43 @@ static void set_free_record(void *mem, tdb_len_t len) ...@@ -97,47 +91,43 @@ static void set_free_record(void *mem, tdb_len_t len)
} }
static void set_data_record(void *mem, struct tdb_context *tdb, static void set_data_record(void *mem, struct tdb_context *tdb,
struct tle_zone *last_zone,
struct tle_used *used) struct tle_used *used)
{ {
struct tdb_used_record *u = mem; struct tdb_used_record *u = mem;
set_header(tdb, u, used->key.dsize, used->data.dsize, set_header(tdb, u, used->key.dsize, used->data.dsize,
used->key.dsize + used->data.dsize + used->extra, used->key.dsize + used->data.dsize + used->extra,
tdb_hash(tdb, used->key.dptr, used->key.dsize), tdb_hash(tdb, used->key.dptr, used->key.dsize));
last_zone->zone_bits);
memcpy(u + 1, used->key.dptr, used->key.dsize); memcpy(u + 1, used->key.dptr, used->key.dsize);
memcpy((char *)(u + 1) + used->key.dsize, memcpy((char *)(u + 1) + used->key.dsize,
used->data.dptr, used->data.dsize); used->data.dptr, used->data.dsize);
} }
static void set_hashtable(void *mem, struct tdb_context *tdb, static void set_hashtable(void *mem, struct tdb_context *tdb,
struct tle_zone *last_zone,
struct tle_hashtable *htable) struct tle_hashtable *htable)
{ {
struct tdb_used_record *u = mem; struct tdb_used_record *u = mem;
tdb_len_t len = sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS; tdb_len_t len = sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS;
set_header(tdb, u, 0, len, len + htable->extra, 0, set_header(tdb, u, 0, len, len + htable->extra, 0);
last_zone->zone_bits);
memset(u + 1, 0, len); memset(u + 1, 0, len);
} }
static void set_zone(void *mem, struct tdb_context *tdb, static void set_freelist(void *mem, struct tdb_context *tdb,
struct tle_zone *zone) struct tle_freelist *freelist)
{ {
struct free_zone_header *fz = mem; struct tdb_freelist *flist = mem;
memset(fz, 0, zone_header_len(zone)); memset(flist, 0, sizeof(*flist));
fz->zone_bits = zone->zone_bits; set_header(tdb, &flist->hdr, 0,
sizeof(*flist) - sizeof(flist->hdr),
sizeof(*flist) - sizeof(flist->hdr), 1);
} }
static void add_to_freetable(struct tdb_context *tdb, static void add_to_freetable(struct tdb_context *tdb,
struct tle_zone *last_zone,
tdb_off_t eoff, tdb_off_t eoff,
tdb_off_t elen) tdb_off_t elen)
{ {
add_free_record(tdb, last_zone->zone_bits, eoff, add_free_record(tdb, eoff, sizeof(struct tdb_used_record) + elen);
sizeof(struct tdb_used_record) + elen);
} }
static tdb_off_t hbucket_off(tdb_off_t group_start, unsigned ingroup) static tdb_off_t hbucket_off(tdb_off_t group_start, unsigned ingroup)
...@@ -204,15 +194,10 @@ static void add_to_hashtable(struct tdb_context *tdb, ...@@ -204,15 +194,10 @@ static void add_to_hashtable(struct tdb_context *tdb,
struct tdb_context *tdb_layout_get(struct tdb_layout *layout) struct tdb_context *tdb_layout_get(struct tdb_layout *layout)
{ {
unsigned int i; unsigned int i;
tdb_off_t off, len; tdb_off_t off, len, flist_off = 0;
tdb_len_t zone_left;
char *mem; char *mem;
struct tdb_context *tdb; struct tdb_context *tdb;
struct tle_zone *last_zone = NULL;
assert(layout->elem[0].base.type == ZONE);
zone_left = 0;
off = sizeof(struct tdb_header); off = sizeof(struct tdb_header);
/* First pass of layout: calc lengths */ /* First pass of layout: calc lengths */
...@@ -220,15 +205,12 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout) ...@@ -220,15 +205,12 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout)
union tdb_layout_elem *e = &layout->elem[i]; union tdb_layout_elem *e = &layout->elem[i];
e->base.off = off; e->base.off = off;
switch (e->base.type) { switch (e->base.type) {
case ZONE: case FREELIST:
assert(zone_left == 0); assert(flist_off == 0);
len = zone_header_len(&e->zone); flist_off = off;
zone_left = 1ULL << e->zone.zone_bits; len = freelist_len(&e->flist);
break; break;
case FREE: case FREE:
if (e->free.len == 0)
e->free.len = zone_left
- sizeof(struct tdb_used_record);
len = free_record_len(e->free.len); len = free_record_len(e->free.len);
break; break;
case DATA: case DATA:
...@@ -241,9 +223,9 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout) ...@@ -241,9 +223,9 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout)
abort(); abort();
} }
off += len; off += len;
assert(zone_left >= len);
zone_left -= len;
} }
/* Must have a free list! */
assert(flist_off);
mem = malloc(off); mem = malloc(off);
/* Now populate our header, cribbing from a real TDB header. */ /* Now populate our header, cribbing from a real TDB header. */
...@@ -254,24 +236,22 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout) ...@@ -254,24 +236,22 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout)
free(tdb->map_ptr); free(tdb->map_ptr);
tdb->map_ptr = mem; tdb->map_ptr = mem;
tdb->map_size = off; tdb->map_size = off;
tdb->flist_off = flist_off;
for (i = 0; i < layout->num_elems; i++) { for (i = 0; i < layout->num_elems; i++) {
union tdb_layout_elem *e = &layout->elem[i]; union tdb_layout_elem *e = &layout->elem[i];
switch (e->base.type) { switch (e->base.type) {
case ZONE: case FREELIST:
set_zone(mem + e->base.off, tdb, &e->zone); set_freelist(mem + e->base.off, tdb, &e->flist);
last_zone = &e->zone;
break; break;
case FREE: case FREE:
set_free_record(mem + e->base.off, e->free.len); set_free_record(mem + e->base.off, e->free.len);
break; break;
case DATA: case DATA:
set_data_record(mem + e->base.off, tdb, last_zone, set_data_record(mem + e->base.off, tdb, &e->used);
&e->used);
break; break;
case HASHTABLE: case HASHTABLE:
set_hashtable(mem + e->base.off, tdb, last_zone, set_hashtable(mem + e->base.off, tdb, &e->hashtable);
&e->hashtable);
break; break;
} }
} }
...@@ -280,12 +260,8 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout) ...@@ -280,12 +260,8 @@ struct tdb_context *tdb_layout_get(struct tdb_layout *layout)
for (i = 0; i < layout->num_elems; i++) { for (i = 0; i < layout->num_elems; i++) {
union tdb_layout_elem *e = &layout->elem[i]; union tdb_layout_elem *e = &layout->elem[i];
switch (e->base.type) { switch (e->base.type) {
case ZONE:
last_zone = &e->zone;
break;
case FREE: case FREE:
add_to_freetable(tdb, last_zone, add_to_freetable(tdb, e->base.off, e->free.len);
e->base.off, e->free.len);
break; break;
case DATA: case DATA:
add_to_hashtable(tdb, e->base.off, e->used.key); add_to_hashtable(tdb, e->base.off, e->used.key);
......
...@@ -3,9 +3,7 @@ ...@@ -3,9 +3,7 @@
#include <ccan/tdb2/private.h> #include <ccan/tdb2/private.h>
struct tdb_layout *new_tdb_layout(const char *filename); struct tdb_layout *new_tdb_layout(const char *filename);
void tdb_layout_add_zone(struct tdb_layout *layout, void tdb_layout_add_freelist(struct tdb_layout *layout);
unsigned int zone_bits,
bool fill_prev);
void tdb_layout_add_free(struct tdb_layout *layout, tdb_len_t len); void tdb_layout_add_free(struct tdb_layout *layout, tdb_len_t len);
void tdb_layout_add_used(struct tdb_layout *layout, void tdb_layout_add_used(struct tdb_layout *layout,
TDB_DATA key, TDB_DATA data, TDB_DATA key, TDB_DATA data,
...@@ -19,7 +17,7 @@ void tdb_layout_add_hashtable(struct tdb_layout *layout, ...@@ -19,7 +17,7 @@ void tdb_layout_add_hashtable(struct tdb_layout *layout,
struct tdb_context *tdb_layout_get(struct tdb_layout *layout); struct tdb_context *tdb_layout_get(struct tdb_layout *layout);
enum layout_type { enum layout_type {
ZONE, FREE, DATA, HASHTABLE, FREELIST, FREE, DATA, HASHTABLE,
}; };
/* Shared by all union members. */ /* Shared by all union members. */
...@@ -28,9 +26,8 @@ struct tle_base { ...@@ -28,9 +26,8 @@ struct tle_base {
tdb_off_t off; tdb_off_t off;
}; };
struct tle_zone { struct tle_freelist {
struct tle_base base; struct tle_base base;
unsigned int zone_bits;
}; };
struct tle_free { struct tle_free {
...@@ -54,7 +51,7 @@ struct tle_hashtable { ...@@ -54,7 +51,7 @@ struct tle_hashtable {
union tdb_layout_elem { union tdb_layout_elem {
struct tle_base base; struct tle_base base;
struct tle_zone zone; struct tle_freelist flist;
struct tle_free free; struct tle_free free;
struct tle_used used; struct tle_used used;
struct tle_hashtable hashtable; struct tle_hashtable hashtable;
......
...@@ -13,18 +13,16 @@ int main(int argc, char *argv[]) ...@@ -13,18 +13,16 @@ int main(int argc, char *argv[])
struct tdb_used_record rec; struct tdb_used_record rec;
struct tdb_context tdb = { .log = tap_log_fn, .log_priv = NULL }; struct tdb_context tdb = { .log = tap_log_fn, .log_priv = NULL };
plan_tests(64 + 32 + 48*7 + 1); plan_tests(64 + 32 + 48*6 + 1);
/* We should be able to encode any data value. */ /* We should be able to encode any data value. */
for (i = 0; i < 64; i++) for (i = 0; i < 64; i++)
ok1(set_header(&tdb, &rec, 0, 1ULL << i, 1ULL << i, 0, 0) ok1(set_header(&tdb, &rec, 0, 1ULL << i, 1ULL << i, 0) == 0);
== 0);
/* And any key and data with < 64 bits between them. */ /* And any key and data with < 64 bits between them. */
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
tdb_len_t dlen = 1ULL >> (63 - i), klen = 1ULL << i; tdb_len_t dlen = 1ULL >> (63 - i), klen = 1ULL << i;
ok1(set_header(&tdb, &rec, klen, dlen, klen + dlen, 0, 0) ok1(set_header(&tdb, &rec, klen, dlen, klen + dlen, 0) == 0);
== 0);
} }
/* We should neatly encode all values. */ /* We should neatly encode all values. */
...@@ -33,15 +31,12 @@ int main(int argc, char *argv[]) ...@@ -33,15 +31,12 @@ int main(int argc, char *argv[])
uint64_t klen = 1ULL << (i < 16 ? i : 15); uint64_t klen = 1ULL << (i < 16 ? i : 15);
uint64_t dlen = 1ULL << i; uint64_t dlen = 1ULL << i;
uint64_t xlen = 1ULL << (i < 32 ? i : 31); uint64_t xlen = 1ULL << (i < 32 ? i : 31);
uint64_t zbits = 1ULL << (i < 6 ? i : 5); ok1(set_header(&tdb, &rec, klen, dlen, klen + dlen + xlen, h)
ok1(set_header(&tdb, &rec, klen, dlen, klen + dlen + xlen, h,
zbits)
== 0); == 0);
ok1(rec_key_length(&rec) == klen); ok1(rec_key_length(&rec) == klen);
ok1(rec_data_length(&rec) == dlen); ok1(rec_data_length(&rec) == dlen);
ok1(rec_extra_padding(&rec) == xlen); ok1(rec_extra_padding(&rec) == xlen);
ok1((uint64_t)rec_hash(&rec) == h); ok1((uint64_t)rec_hash(&rec) == h);
ok1(rec_zone_bits(&rec) == zbits);
ok1(rec_magic(&rec) == TDB_MAGIC); ok1(rec_magic(&rec) == TDB_MAGIC);
} }
ok1(tap_log_messages == 0); ok1(tap_log_messages == 0);
......
#include <ccan/tdb2/tdb.c>
#include <ccan/tdb2/free.c>
#include <ccan/tdb2/lock.c>
#include <ccan/tdb2/io.c>
#include <ccan/tdb2/hash.c>
#include <ccan/tdb2/check.c>
#include <ccan/tap/tap.h>
#include "logging.h"
#include "layout.h"
/* Calculate start of zone offset from layout directly. */
static tdb_off_t layout_zone_off(tdb_off_t off, struct tdb_layout *layout)
{
unsigned int i;
/* Every second one is a free entry, so divide by 2 to get zone */
for (i = 0; i < layout->num_elems; i++) {
if (layout->elem[i].base.type != ZONE)
continue;
if (layout->elem[i].base.off
+ (1ULL << layout->elem[i].zone.zone_bits) > off)
return layout->elem[i].base.off;
}
abort();
}
int main(int argc, char *argv[])
{
struct tdb_context *tdb;
struct tdb_layout *layout;
struct free_zone_header zhdr;
tdb_off_t off, step;
unsigned int i;
/* FIXME: Test TDB_CONVERT */
plan_tests(3 + 100);
/* No coalescing can be done due to EOF */
layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, INITIAL_ZONE_BITS, false);
tdb_layout_add_zone(layout, INITIAL_ZONE_BITS, true);
tdb_layout_add_zone(layout, INITIAL_ZONE_BITS+1, true);
tdb_layout_add_zone(layout, INITIAL_ZONE_BITS+2, true);
tdb_layout_add_zone(layout, INITIAL_ZONE_BITS+2, true);
tdb = tdb_layout_get(layout);
ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Last zone should get right zone. */
ok1(last_zone(tdb, &zhdr)
== layout->elem[layout->num_elems-1].base.off);
ok1(zhdr.zone_bits == INITIAL_ZONE_BITS+2);
off = sizeof(struct tdb_header);
step = (tdb->map_size - 1 - off) / 100;
for (i = 0; i < 100; i++, off += step) {
ok1(off_to_zone(tdb, off, &zhdr) == layout_zone_off(off, layout));
}
return exit_status();
}
...@@ -16,7 +16,7 @@ int main(int argc, char *argv[]) ...@@ -16,7 +16,7 @@ int main(int argc, char *argv[])
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
TDB_NOMMAP|TDB_CONVERT }; TDB_NOMMAP|TDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 18 + 1); plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
tdb = tdb_open("run-expand.tdb", flags[i], tdb = tdb_open("run-expand.tdb", flags[i],
...@@ -25,47 +25,15 @@ int main(int argc, char *argv[]) ...@@ -25,47 +25,15 @@ int main(int argc, char *argv[])
if (!tdb) if (!tdb)
continue; continue;
/* First expand. Should not fill zone. */ val = tdb->map_size;
val = tdb->map_size - sizeof(struct tdb_header);
ok1(tdb_expand(tdb, 1) == 0); ok1(tdb_expand(tdb, 1) == 0);
ok1(tdb->map_size < sizeof(struct tdb_header) ok1(tdb->map_size >= val + 1 * TDB_EXTENSION_FACTOR);
+ (1 << INITIAL_ZONE_BITS));
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Fill zone. */ val = tdb->map_size;
val = (1<<INITIAL_ZONE_BITS) ok1(tdb_expand(tdb, 1024) == 0);
- sizeof(struct tdb_used_record) ok1(tdb->map_size >= val + 1024 * TDB_EXTENSION_FACTOR);
- (tdb->map_size - sizeof(struct tdb_header));
ok1(tdb_expand(tdb, val) == 0);
ok1(tdb->map_size == sizeof(struct tdb_header)
+ (1 << INITIAL_ZONE_BITS));
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Second expand, adds another zone of same size. */
ok1(tdb_expand(tdb, 4 << INITIAL_ZONE_BITS) == 0);
ok1(tdb->map_size ==
(2<<INITIAL_ZONE_BITS) + sizeof(struct tdb_header));
ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Large expand now will double file. */
ok1(tdb_expand(tdb, 4 << INITIAL_ZONE_BITS) == 0);
ok1(tdb->map_size ==
(4<<INITIAL_ZONE_BITS) + sizeof(struct tdb_header));
ok1(tdb_check(tdb, NULL, NULL) == 0);
/* And again? */
ok1(tdb_expand(tdb, 4 << INITIAL_ZONE_BITS) == 0);
ok1(tdb->map_size ==
(8<<INITIAL_ZONE_BITS) + sizeof(struct tdb_header));
ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Below comfort level, won't fill zone. */
ok1(tdb_expand(tdb,
((3 << INITIAL_ZONE_BITS)
>> TDB_COMFORT_FACTOR_BITS)
- sizeof(struct tdb_used_record)) == 0);
ok1(tdb->map_size < (12<<INITIAL_ZONE_BITS)
+ sizeof(struct tdb_header));
tdb_close(tdb); tdb_close(tdb);
} }
......
...@@ -21,16 +21,15 @@ static tdb_len_t free_record_length(struct tdb_context *tdb, tdb_off_t off) ...@@ -21,16 +21,15 @@ static tdb_len_t free_record_length(struct tdb_context *tdb, tdb_off_t off)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
tdb_off_t b_off, zone_off; tdb_off_t b_off;
struct tdb_context *tdb; struct tdb_context *tdb;
struct tdb_layout *layout; struct tdb_layout *layout;
struct tdb_data data, key; struct tdb_data data, key;
tdb_len_t len; tdb_len_t len;
unsigned int zone_bits = 16;
/* FIXME: Test TDB_CONVERT */ /* FIXME: Test TDB_CONVERT */
plan_tests(45); plan_tests(38);
data.dptr = (void *)"world"; data.dptr = (void *)"world";
data.dsize = 5; data.dsize = 5;
key.dptr = (void *)"hello"; key.dptr = (void *)"hello";
...@@ -38,20 +37,18 @@ int main(int argc, char *argv[]) ...@@ -38,20 +37,18 @@ int main(int argc, char *argv[])
/* No coalescing can be done due to EOF */ /* No coalescing can be done due to EOF */
layout = new_tdb_layout(NULL); layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, zone_bits, false); tdb_layout_add_freelist(layout);
len = 1024; len = 1024;
tdb_layout_add_free(layout, len); tdb_layout_add_free(layout, len);
tdb = tdb_layout_get(layout); tdb = tdb_layout_get(layout);
zone_off = layout->elem[0].base.off;
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
ok1(free_record_length(tdb, layout->elem[1].base.off) == len); ok1(free_record_length(tdb, layout->elem[1].base.off) == len);
/* Figure out which bucket free entry is. */ /* Figure out which bucket free entry is. */
b_off = bucket_off(zone_off, size_to_bucket(zone_bits, len)); b_off = bucket_off(tdb->flist_off, size_to_bucket(len));
/* Lock and fail to coalesce. */ /* Lock and fail to coalesce. */
ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0); ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
ok1(coalesce(tdb, zone_off, zone_bits, layout->elem[1].base.off, ok1(coalesce(tdb, layout->elem[1].base.off, b_off, len) == 0);
b_off, len) == 0);
tdb_unlock_free_bucket(tdb, b_off); tdb_unlock_free_bucket(tdb, b_off);
ok1(free_record_length(tdb, layout->elem[1].base.off) == len); ok1(free_record_length(tdb, layout->elem[1].base.off) == len);
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
...@@ -59,20 +56,18 @@ int main(int argc, char *argv[]) ...@@ -59,20 +56,18 @@ int main(int argc, char *argv[])
/* No coalescing can be done due to used record */ /* No coalescing can be done due to used record */
layout = new_tdb_layout(NULL); layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, zone_bits, false); tdb_layout_add_freelist(layout);
tdb_layout_add_free(layout, 1024); tdb_layout_add_free(layout, 1024);
tdb_layout_add_used(layout, key, data, 6); tdb_layout_add_used(layout, key, data, 6);
tdb = tdb_layout_get(layout); tdb = tdb_layout_get(layout);
zone_off = layout->elem[0].base.off;
ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024); ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Figure out which bucket free entry is. */ /* Figure out which bucket free entry is. */
b_off = bucket_off(zone_off, size_to_bucket(zone_bits, 1024)); b_off = bucket_off(tdb->flist_off, size_to_bucket(1024));
/* Lock and fail to coalesce. */ /* Lock and fail to coalesce. */
ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0); ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
ok1(coalesce(tdb, zone_off, zone_bits, layout->elem[1].base.off, ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024) == 0);
b_off, 1024) == 0);
tdb_unlock_free_bucket(tdb, b_off); tdb_unlock_free_bucket(tdb, b_off);
ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024); ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
...@@ -80,21 +75,19 @@ int main(int argc, char *argv[]) ...@@ -80,21 +75,19 @@ int main(int argc, char *argv[])
/* Coalescing can be done due to two free records, then EOF */ /* Coalescing can be done due to two free records, then EOF */
layout = new_tdb_layout(NULL); layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, zone_bits, false); tdb_layout_add_freelist(layout);
tdb_layout_add_free(layout, 1024); tdb_layout_add_free(layout, 1024);
tdb_layout_add_free(layout, 2048); tdb_layout_add_free(layout, 2048);
tdb = tdb_layout_get(layout); tdb = tdb_layout_get(layout);
zone_off = layout->elem[0].base.off;
ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024); ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
ok1(free_record_length(tdb, layout->elem[2].base.off) == 2048); ok1(free_record_length(tdb, layout->elem[2].base.off) == 2048);
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Figure out which bucket (first) free entry is. */ /* Figure out which bucket (first) free entry is. */
b_off = bucket_off(zone_off, size_to_bucket(zone_bits, 1024)); b_off = bucket_off(tdb->flist_off, size_to_bucket(1024));
/* Lock and coalesce. */ /* Lock and coalesce. */
ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0); ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
ok1(coalesce(tdb, zone_off, zone_bits, layout->elem[1].base.off, ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024) == 1);
b_off, 1024) == 1);
ok1(!tdb_has_locks(tdb)); ok1(!tdb_has_locks(tdb));
ok1(free_record_length(tdb, layout->elem[1].base.off) ok1(free_record_length(tdb, layout->elem[1].base.off)
== 1024 + sizeof(struct tdb_used_record) + 2048); == 1024 + sizeof(struct tdb_used_record) + 2048);
...@@ -103,22 +96,20 @@ int main(int argc, char *argv[]) ...@@ -103,22 +96,20 @@ int main(int argc, char *argv[])
/* Coalescing can be done due to two free records, then data */ /* Coalescing can be done due to two free records, then data */
layout = new_tdb_layout(NULL); layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, zone_bits, false); tdb_layout_add_freelist(layout);
tdb_layout_add_free(layout, 1024); tdb_layout_add_free(layout, 1024);
tdb_layout_add_free(layout, 512); tdb_layout_add_free(layout, 512);
tdb_layout_add_used(layout, key, data, 6); tdb_layout_add_used(layout, key, data, 6);
tdb = tdb_layout_get(layout); tdb = tdb_layout_get(layout);
zone_off = layout->elem[0].base.off;
ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024); ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
ok1(free_record_length(tdb, layout->elem[2].base.off) == 512); ok1(free_record_length(tdb, layout->elem[2].base.off) == 512);
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Figure out which bucket free entry is. */ /* Figure out which bucket free entry is. */
b_off = bucket_off(zone_off, size_to_bucket(zone_bits, 1024)); b_off = bucket_off(tdb->flist_off, size_to_bucket(1024));
/* Lock and coalesce. */ /* Lock and coalesce. */
ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0); ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
ok1(coalesce(tdb, zone_off, zone_bits, layout->elem[1].base.off, ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024) == 1);
b_off, 1024) == 1);
ok1(!tdb_has_locks(tdb)); ok1(!tdb_has_locks(tdb));
ok1(free_record_length(tdb, layout->elem[1].base.off) ok1(free_record_length(tdb, layout->elem[1].base.off)
== 1024 + sizeof(struct tdb_used_record) + 512); == 1024 + sizeof(struct tdb_used_record) + 512);
...@@ -127,23 +118,21 @@ int main(int argc, char *argv[]) ...@@ -127,23 +118,21 @@ int main(int argc, char *argv[])
/* Coalescing can be done due to three free records, then EOF */ /* Coalescing can be done due to three free records, then EOF */
layout = new_tdb_layout(NULL); layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, zone_bits, false); tdb_layout_add_freelist(layout);
tdb_layout_add_free(layout, 1024); tdb_layout_add_free(layout, 1024);
tdb_layout_add_free(layout, 512); tdb_layout_add_free(layout, 512);
tdb_layout_add_free(layout, 256); tdb_layout_add_free(layout, 256);
tdb = tdb_layout_get(layout); tdb = tdb_layout_get(layout);
zone_off = layout->elem[0].base.off;
ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024); ok1(free_record_length(tdb, layout->elem[1].base.off) == 1024);
ok1(free_record_length(tdb, layout->elem[2].base.off) == 512); ok1(free_record_length(tdb, layout->elem[2].base.off) == 512);
ok1(free_record_length(tdb, layout->elem[3].base.off) == 256); ok1(free_record_length(tdb, layout->elem[3].base.off) == 256);
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Figure out which bucket free entry is. */ /* Figure out which bucket free entry is. */
b_off = bucket_off(zone_off, size_to_bucket(zone_bits, 1024)); b_off = bucket_off(tdb->flist_off, size_to_bucket(1024));
/* Lock and coalesce. */ /* Lock and coalesce. */
ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0); ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
ok1(coalesce(tdb, zone_off, zone_bits, layout->elem[1].base.off, ok1(coalesce(tdb, layout->elem[1].base.off, b_off, 1024) == 1);
b_off, 1024) == 1);
ok1(!tdb_has_locks(tdb)); ok1(!tdb_has_locks(tdb));
ok1(free_record_length(tdb, layout->elem[1].base.off) ok1(free_record_length(tdb, layout->elem[1].base.off)
== 1024 + sizeof(struct tdb_used_record) + 512 == 1024 + sizeof(struct tdb_used_record) + 512
...@@ -151,28 +140,6 @@ int main(int argc, char *argv[]) ...@@ -151,28 +140,6 @@ int main(int argc, char *argv[])
ok1(tdb_check(tdb, NULL, NULL) == 0); ok1(tdb_check(tdb, NULL, NULL) == 0);
tdb_close(tdb); tdb_close(tdb);
/* Coalescing across two zones isn't possible. */
layout = new_tdb_layout(NULL);
tdb_layout_add_zone(layout, zone_bits, false);
tdb_layout_add_zone(layout, zone_bits, true);
tdb = tdb_layout_get(layout);
zone_off = layout->elem[0].base.off;
len = layout->elem[1].free.len;
ok1(free_record_length(tdb, layout->elem[1].base.off) == len);
ok1(tdb_check(tdb, NULL, NULL) == 0);
/* Figure out which list free entry is. */
b_off = bucket_off(zone_off, size_to_bucket(zone_bits, len));
/* Lock and coalesce. */
ok1(tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == 0);
ok1(coalesce(tdb, zone_off, zone_bits, layout->elem[1].base.off,
b_off, len) == 0);
tdb_unlock_free_bucket(tdb, b_off);
ok1(!tdb_has_locks(tdb));
ok1(free_record_length(tdb, layout->elem[1].base.off) == len);
ok1(tdb_check(tdb, NULL, NULL) == 0);
tdb_close(tdb);
ok1(tap_log_messages == 0); ok1(tap_log_messages == 0);
return exit_status(); return exit_status();
} }
...@@ -169,7 +169,7 @@ int main(int argc, char *argv[]) ...@@ -169,7 +169,7 @@ int main(int argc, char *argv[])
/* Simple delete should work. */ /* Simple delete should work. */
ok1(delete_from_hash(tdb, &h) == 0); ok1(delete_from_hash(tdb, &h) == 0);
ok1(add_free_record(tdb, rec_zone_bits(&rec), new_off, ok1(add_free_record(tdb, new_off,
sizeof(struct tdb_used_record) sizeof(struct tdb_used_record)
+ rec_key_length(&rec) + rec_key_length(&rec)
+ rec_data_length(&rec) + rec_data_length(&rec)
......
...@@ -8,6 +8,23 @@ ...@@ -8,6 +8,23 @@
#include <err.h> #include <err.h>
#include "logging.h" #include "logging.h"
static bool empty_freelist(struct tdb_context *tdb)
{
struct tdb_freelist free;
unsigned int i;
/* Now, free list should be completely exhausted in zone 0 */
if (tdb_read_convert(tdb, tdb->flist_off, &free, sizeof(free)) != 0)
abort();
for (i = 0; i < sizeof(free.buckets)/sizeof(free.buckets[0]); i++) {
if (free.buckets[i])
return false;
}
return true;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
unsigned int i, j; unsigned int i, j;
...@@ -16,12 +33,12 @@ int main(int argc, char *argv[]) ...@@ -16,12 +33,12 @@ int main(int argc, char *argv[])
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
TDB_NOMMAP|TDB_CONVERT }; TDB_NOMMAP|TDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 5 + 1); plan_tests(sizeof(flags) / sizeof(flags[0]) * 7 + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
tdb_off_t free[BUCKETS_FOR_ZONE(INITIAL_ZONE_BITS) + 1]; TDB_DATA k;
bool all_empty; uint64_t size;
TDB_DATA k, d; bool was_empty = false;
k.dptr = (void *)&j; k.dptr = (void *)&j;
k.dsize = sizeof(j); k.dsize = sizeof(j);
...@@ -32,38 +49,23 @@ int main(int argc, char *argv[]) ...@@ -32,38 +49,23 @@ int main(int argc, char *argv[])
if (!tdb) if (!tdb)
continue; continue;
/* We don't want the hash to expand, so we use one alloc to ok1(empty_freelist(tdb));
* chew up over most of the space first. */ /* Create some free space. */
j = -1; ok1(tdb_expand(tdb, 1) == 0);
d.dsize = (1 << INITIAL_ZONE_BITS) - 500; ok1(tdb_check(tdb, NULL, NULL) == 0);
d.dptr = malloc(d.dsize); ok1(!empty_freelist(tdb));
ok1(tdb_store(tdb, k, d, TDB_INSERT) == 0);
ok1(tdb->map_size == sizeof(struct tdb_header)
+ (1 << INITIAL_ZONE_BITS));
/* Insert minimal-length records until we add a zone. */ size = tdb->map_size;
for (j = 0; /* Insert minimal-length records until we expand. */
tdb->map_size == sizeof(struct tdb_header) for (j = 0; tdb->map_size == size; j++) {
+ (1 << INITIAL_ZONE_BITS); was_empty = empty_freelist(tdb);
j++) {
if (tdb_store(tdb, k, k, TDB_INSERT) != 0) if (tdb_store(tdb, k, k, TDB_INSERT) != 0)
err(1, "Failed to store record %i", j); err(1, "Failed to store record %i", j);
} }
/* Now, free list should be completely exhausted in zone 0 */ /* Would have been empty before expansion, but no longer. */
ok1(tdb_read_convert(tdb, ok1(was_empty);
sizeof(struct tdb_header) ok1(!empty_freelist(tdb));
+ sizeof(struct free_zone_header),
&free, sizeof(free)) == 0);
all_empty = true;
for (j = 0; j < sizeof(free)/sizeof(free[0]); j++) {
if (free[j]) {
diag("Free bucket %i not empty", j);
all_empty = false;
}
}
ok1(all_empty);
tdb_close(tdb); tdb_close(tdb);
} }
......
...@@ -44,15 +44,21 @@ int main(int argc, char *argv[]) ...@@ -44,15 +44,21 @@ int main(int argc, char *argv[])
struct trav_data td; struct trav_data td;
TDB_DATA k, k2; TDB_DATA k, k2;
struct tdb_context *tdb; struct tdb_context *tdb;
union tdb_attribute seed_attr;
int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP, int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
TDB_NOMMAP|TDB_CONVERT }; TDB_NOMMAP|TDB_CONVERT };
seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
seed_attr.base.next = &tap_log_attr;
seed_attr.seed.seed = 6334326220117065685ULL;
plan_tests(sizeof(flags) / sizeof(flags[0]) plan_tests(sizeof(flags) / sizeof(flags[0])
* (NUM_RECORDS*4 + (NUM_RECORDS-1)*2 + 20) + 1); * (NUM_RECORDS*4 + (NUM_RECORDS-1)*2 + 20) + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
tdb = tdb_open("run-traverse.tdb", flags[i], tdb = tdb_open("run-traverse.tdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr); O_RDWR|O_CREAT|O_TRUNC, 0600, &seed_attr);
ok1(tdb); ok1(tdb);
if (!tdb) if (!tdb)
continue; continue;
......
...@@ -19,7 +19,7 @@ int main(int argc, char *argv[]) ...@@ -19,7 +19,7 @@ int main(int argc, char *argv[])
struct tdb_data data = { (unsigned char *)&j, sizeof(j) }; struct tdb_data data = { (unsigned char *)&j, sizeof(j) };
char *summary; char *summary;
plan_tests(sizeof(flags) / sizeof(flags[0]) * (1 + 2 * 17) + 1); plan_tests(sizeof(flags) / sizeof(flags[0]) * (1 + 2 * 4) + 1);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
tdb = tdb_open("run-summary.tdb", flags[i], tdb = tdb_open("run-summary.tdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr); O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
...@@ -42,19 +42,6 @@ int main(int argc, char *argv[]) ...@@ -42,19 +42,6 @@ int main(int argc, char *argv[])
ok1(strstr(summary, "Number of records: 500\n")); ok1(strstr(summary, "Number of records: 500\n"));
ok1(strstr(summary, "Smallest/average/largest keys: 4/4/4\n")); ok1(strstr(summary, "Smallest/average/largest keys: 4/4/4\n"));
ok1(strstr(summary, "Smallest/average/largest data: 0/2/4\n")); ok1(strstr(summary, "Smallest/average/largest data: 0/2/4\n"));
ok1(strstr(summary, "Free bucket 16:"));
ok1(strstr(summary, "Free bucket 24:"));
ok1(strstr(summary, "Free bucket 32:"));
ok1(strstr(summary, "Free bucket 40:"));
ok1(strstr(summary, "Free bucket 48:"));
ok1(strstr(summary, "Free bucket 56:"));
ok1(strstr(summary, "Free bucket 64:"));
ok1(strstr(summary, "Free bucket 72:"));
ok1(strstr(summary, "Free bucket 80:"));
ok1(strstr(summary, "Free bucket 88-136:"));
ok1(strstr(summary, "Free bucket 144-264:"));
ok1(strstr(summary, "Free bucket 272-520:"));
ok1(strstr(summary, "Free bucket 528-1032:"));
if (j == TDB_SUMMARY_HISTOGRAMS) if (j == TDB_SUMMARY_HISTOGRAMS)
ok1(strstr(summary, "|") ok1(strstr(summary, "|")
&& strstr(summary, "*")); && strstr(summary, "*"));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment