1 #include <ccan/tdb2/tdb.c>
2 #include <ccan/tdb2/open.c>
3 #include <ccan/tdb2/free.c>
4 #include <ccan/tdb2/lock.c>
5 #include <ccan/tdb2/io.c>
6 #include <ccan/tdb2/hash.c>
7 #include <ccan/tdb2/transaction.c>
8 #include <ccan/tdb2/check.c>
9 #include <ccan/tap/tap.h>
12 /* We rig the hash so adjacent-numbered records always clash. */
13 static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
15 return ((uint64_t)*(const unsigned int *)key)
16 << (64 - TDB_TOPLEVEL_HASH_BITS - 1);
19 int main(int argc, char *argv[])
22 struct tdb_context *tdb;
24 struct tdb_used_record rec;
25 struct tdb_data key = { (unsigned char *)&v, sizeof(v) };
26 struct tdb_data dbuf = { (unsigned char *)&v, sizeof(v) };
27 union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
29 int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
30 TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
31 TDB_NOMMAP|TDB_CONVERT,
34 hattr.base.next = &tap_log_attr;
36 plan_tests(sizeof(flags) / sizeof(flags[0])
37 * (91 + (2 * ((1 << TDB_HASH_GROUP_BITS) - 1))) + 1);
38 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
40 tdb_off_t new_off, off, subhash;
42 tdb = tdb_open("run-04-basichash.tdb", flags[i],
43 O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
49 /* Should not find it. */
50 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
51 /* Should have created correct hash. */
52 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
53 /* Should have located space in group 0, bucket 0. */
54 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
55 ok1(h.home_bucket == 0);
56 ok1(h.found_bucket == 0);
57 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
59 /* Should have lock on bucket 0 */
60 ok1(h.hlock_start == 0);
62 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
63 ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
64 ok1((tdb->flags & TDB_NOLOCK)
65 || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
66 /* FIXME: Check lock length */
68 /* Allocate a new record. */
69 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h,
70 TDB_USED_MAGIC, false);
71 ok1(!TDB_OFF_IS_ERR(new_off));
73 /* We should be able to add it now. */
74 ok1(add_to_hash(tdb, &h, new_off) == 0);
76 /* Make sure we fill it in for later finding. */
77 off = new_off + sizeof(struct tdb_used_record);
78 ok1(!tdb->methods->twrite(tdb, off, key.dptr, key.dsize));
80 ok1(!tdb->methods->twrite(tdb, off, dbuf.dptr, dbuf.dsize));
82 /* We should be able to unlock that OK. */
83 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
86 /* Database should be consistent. */
87 ok1(tdb_check(tdb, NULL, NULL) == 0);
89 /* Now, this should give a successful lookup. */
90 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
92 /* Should have created correct hash. */
93 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
94 /* Should have located space in group 0, bucket 0. */
95 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
96 ok1(h.home_bucket == 0);
97 ok1(h.found_bucket == 0);
98 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
100 /* Should have lock on bucket 0 */
101 ok1(h.hlock_start == 0);
103 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
104 ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
105 ok1((tdb->flags & TDB_NOLOCK)
106 || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
107 /* FIXME: Check lock length */
109 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
112 /* Database should be consistent. */
113 ok1(tdb_check(tdb, NULL, NULL) == 0);
115 /* Test expansion. */
117 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
118 /* Should have created correct hash. */
119 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
120 /* Should have located space in group 0, bucket 1. */
121 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
122 ok1(h.home_bucket == 0);
123 ok1(h.found_bucket == 1);
124 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
126 /* Should have lock on bucket 0 */
127 ok1(h.hlock_start == 0);
129 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
130 ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
131 ok1((tdb->flags & TDB_NOLOCK)
132 || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
133 /* FIXME: Check lock length */
135 /* Make it expand 0'th bucket. */
136 ok1(expand_group(tdb, &h) == 0);
137 /* First one should be subhash, next should be empty. */
138 ok1(is_subhash(h.group[0]));
139 subhash = (h.group[0] & TDB_OFF_MASK);
140 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
141 ok1(h.group[j] == 0);
143 ok1(tdb_write_convert(tdb, h.group_start,
144 h.group, sizeof(h.group)) == 0);
145 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
148 /* Should be happy with expansion. */
149 ok1(tdb_check(tdb, NULL, NULL) == 0);
151 /* Should be able to find it. */
153 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
155 /* Should have created correct hash. */
156 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
157 /* Should have located space in expanded group 0, bucket 0. */
158 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
159 ok1(h.home_bucket == 0);
160 ok1(h.found_bucket == 0);
161 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
162 + TDB_SUBLEVEL_HASH_BITS);
164 /* Should have lock on bucket 0 */
165 ok1(h.hlock_start == 0);
167 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
168 ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
169 ok1((tdb->flags & TDB_NOLOCK)
170 || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
171 /* FIXME: Check lock length */
173 /* Simple delete should work. */
174 ok1(delete_from_hash(tdb, &h) == 0);
175 ok1(add_free_record(tdb, new_off,
176 sizeof(struct tdb_used_record)
177 + rec_key_length(&rec)
178 + rec_data_length(&rec)
179 + rec_extra_padding(&rec),
180 TDB_LOCK_NOWAIT, false) == 0);
181 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
183 ok1(tdb_check(tdb, NULL, NULL) == 0);
185 /* Test second-level expansion: should expand 0th bucket. */
187 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
188 /* Should have created correct hash. */
189 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
190 /* Should have located space in group 0, bucket 0. */
191 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
192 ok1(h.home_bucket == 0);
193 ok1(h.found_bucket == 0);
194 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS+TDB_SUBLEVEL_HASH_BITS);
196 /* Should have lock on bucket 0 */
197 ok1(h.hlock_start == 0);
199 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
200 ok1((tdb->flags & TDB_NOLOCK) || tdb->file->num_lockrecs == 1);
201 ok1((tdb->flags & TDB_NOLOCK)
202 || tdb->file->lockrecs[0].off == TDB_HASH_LOCK_START);
203 /* FIXME: Check lock length */
205 ok1(expand_group(tdb, &h) == 0);
206 /* First one should be subhash, next should be empty. */
207 ok1(is_subhash(h.group[0]));
208 subhash = (h.group[0] & TDB_OFF_MASK);
209 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
210 ok1(h.group[j] == 0);
211 ok1(tdb_write_convert(tdb, h.group_start,
212 h.group, sizeof(h.group)) == 0);
213 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
216 /* Should be happy with expansion. */
217 ok1(tdb_check(tdb, NULL, NULL) == 0);
219 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
220 /* Should have created correct hash. */
221 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
222 /* Should have located space in group 0, bucket 0. */
223 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
224 ok1(h.home_bucket == 0);
225 ok1(h.found_bucket == 0);
226 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
227 + TDB_SUBLEVEL_HASH_BITS * 2);
229 /* We should be able to add it now. */
230 /* Allocate a new record. */
231 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h,
232 TDB_USED_MAGIC, false);
233 ok1(!TDB_OFF_IS_ERR(new_off));
234 ok1(add_to_hash(tdb, &h, new_off) == 0);
236 /* Make sure we fill it in for later finding. */
237 off = new_off + sizeof(struct tdb_used_record);
238 ok1(!tdb->methods->twrite(tdb, off, key.dptr, key.dsize));
240 ok1(!tdb->methods->twrite(tdb, off, dbuf.dptr, dbuf.dsize));
242 /* We should be able to unlock that OK. */
243 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
246 /* Database should be consistent. */
247 ok1(tdb_check(tdb, NULL, NULL) == 0);
249 /* Should be able to find it. */
251 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
253 /* Should have created correct hash. */
254 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
255 /* Should have located space in expanded group 0, bucket 0. */
256 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
257 ok1(h.home_bucket == 0);
258 ok1(h.found_bucket == 0);
259 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
260 + TDB_SUBLEVEL_HASH_BITS * 2);
265 ok1(tap_log_messages == 0);
266 return exit_status();