1 #include <ccan/tdb2/tdb.c>
2 #include <ccan/tdb2/free.c>
3 #include <ccan/tdb2/lock.c>
4 #include <ccan/tdb2/io.c>
5 #include <ccan/tdb2/hash.c>
6 #include <ccan/tdb2/transaction.c>
7 #include <ccan/tdb2/check.c>
8 #include <ccan/tap/tap.h>
11 /* We rig the hash so adjacent-numbered records always clash. */
12 static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
14 return ((uint64_t)*(unsigned int *)key)
15 << (64 - TDB_TOPLEVEL_HASH_BITS - 1);
18 int main(int argc, char *argv[])
21 struct tdb_context *tdb;
23 struct tdb_used_record rec;
24 struct tdb_data key = { (unsigned char *)&v, sizeof(v) };
25 struct tdb_data dbuf = { (unsigned char *)&v, sizeof(v) };
26 union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
28 int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
29 TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
30 TDB_NOMMAP|TDB_CONVERT,
33 hattr.base.next = &tap_log_attr;
35 plan_tests(sizeof(flags) / sizeof(flags[0])
36 * (91 + (2 * ((1 << TDB_HASH_GROUP_BITS) - 1))) + 1);
37 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
39 tdb_off_t new_off, off, subhash;
41 tdb = tdb_open("run-04-basichash.tdb", flags[i],
42 O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
48 /* Should not find it. */
49 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
50 /* Should have created correct hash. */
51 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
52 /* Should have located space in group 0, bucket 0. */
53 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
54 ok1(h.home_bucket == 0);
55 ok1(h.found_bucket == 0);
56 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
58 /* Should have lock on bucket 0 */
59 ok1(h.hlock_start == 0);
61 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
62 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
63 ok1((tdb->flags & TDB_NOLOCK)
64 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
65 /* FIXME: Check lock length */
67 /* Allocate a new record. */
68 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h, false);
69 ok1(new_off != TDB_OFF_ERR);
71 /* We should be able to add it now. */
72 ok1(add_to_hash(tdb, &h, new_off) == 0);
74 /* Make sure we fill it in for later finding. */
75 off = new_off + sizeof(struct tdb_used_record);
76 ok1(!tdb->methods->write(tdb, off, key.dptr, key.dsize));
78 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
80 /* We should be able to unlock that OK. */
81 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
84 /* Database should be consistent. */
85 ok1(tdb_check(tdb, NULL, NULL) == 0);
87 /* Now, this should give a successful lookup. */
88 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
90 /* Should have created correct hash. */
91 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
92 /* Should have located space in group 0, bucket 0. */
93 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
94 ok1(h.home_bucket == 0);
95 ok1(h.found_bucket == 0);
96 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
98 /* Should have lock on bucket 0 */
99 ok1(h.hlock_start == 0);
101 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
102 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
103 ok1((tdb->flags & TDB_NOLOCK)
104 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
105 /* FIXME: Check lock length */
107 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
110 /* Database should be consistent. */
111 ok1(tdb_check(tdb, NULL, NULL) == 0);
113 /* Test expansion. */
115 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
116 /* Should have created correct hash. */
117 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
118 /* Should have located space in group 0, bucket 1. */
119 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
120 ok1(h.home_bucket == 0);
121 ok1(h.found_bucket == 1);
122 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
124 /* Should have lock on bucket 0 */
125 ok1(h.hlock_start == 0);
127 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
128 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
129 ok1((tdb->flags & TDB_NOLOCK)
130 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
131 /* FIXME: Check lock length */
133 /* Make it expand 0'th bucket. */
134 ok1(expand_group(tdb, &h) == 0);
135 /* First one should be subhash, next should be empty. */
136 ok1(is_subhash(h.group[0]));
137 subhash = (h.group[0] & TDB_OFF_MASK);
138 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
139 ok1(h.group[j] == 0);
141 ok1(tdb_write_convert(tdb, h.group_start,
142 h.group, sizeof(h.group)) == 0);
143 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
146 /* Should be happy with expansion. */
147 ok1(tdb_check(tdb, NULL, NULL) == 0);
149 /* Should be able to find it. */
151 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
153 /* Should have created correct hash. */
154 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
155 /* Should have located space in expanded group 0, bucket 0. */
156 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
157 ok1(h.home_bucket == 0);
158 ok1(h.found_bucket == 0);
159 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
160 + TDB_SUBLEVEL_HASH_BITS);
162 /* Should have lock on bucket 0 */
163 ok1(h.hlock_start == 0);
165 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
166 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
167 ok1((tdb->flags & TDB_NOLOCK)
168 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
169 /* FIXME: Check lock length */
171 /* Simple delete should work. */
172 ok1(delete_from_hash(tdb, &h) == 0);
173 ok1(add_free_record(tdb, new_off,
174 sizeof(struct tdb_used_record)
175 + rec_key_length(&rec)
176 + rec_data_length(&rec)
177 + rec_extra_padding(&rec)) == 0);
178 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
180 ok1(tdb_check(tdb, NULL, NULL) == 0);
182 /* Test second-level expansion: should expand 0th bucket. */
184 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
185 /* Should have created correct hash. */
186 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
187 /* Should have located space in group 0, bucket 0. */
188 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
189 ok1(h.home_bucket == 0);
190 ok1(h.found_bucket == 0);
191 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS+TDB_SUBLEVEL_HASH_BITS);
193 /* Should have lock on bucket 0 */
194 ok1(h.hlock_start == 0);
196 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
197 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
198 ok1((tdb->flags & TDB_NOLOCK)
199 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
200 /* FIXME: Check lock length */
202 ok1(expand_group(tdb, &h) == 0);
203 /* First one should be subhash, next should be empty. */
204 ok1(is_subhash(h.group[0]));
205 subhash = (h.group[0] & TDB_OFF_MASK);
206 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
207 ok1(h.group[j] == 0);
208 ok1(tdb_write_convert(tdb, h.group_start,
209 h.group, sizeof(h.group)) == 0);
210 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
213 /* Should be happy with expansion. */
214 ok1(tdb_check(tdb, NULL, NULL) == 0);
216 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
217 /* Should have created correct hash. */
218 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
219 /* Should have located space in group 0, bucket 0. */
220 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
221 ok1(h.home_bucket == 0);
222 ok1(h.found_bucket == 0);
223 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
224 + TDB_SUBLEVEL_HASH_BITS * 2);
226 /* We should be able to add it now. */
227 /* Allocate a new record. */
228 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h, false);
229 ok1(new_off != TDB_OFF_ERR);
230 ok1(add_to_hash(tdb, &h, new_off) == 0);
232 /* Make sure we fill it in for later finding. */
233 off = new_off + sizeof(struct tdb_used_record);
234 ok1(!tdb->methods->write(tdb, off, key.dptr, key.dsize));
236 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
238 /* We should be able to unlock that OK. */
239 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
242 /* Database should be consistent. */
243 ok1(tdb_check(tdb, NULL, NULL) == 0);
245 /* Should be able to find it. */
247 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
249 /* Should have created correct hash. */
250 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
251 /* Should have located space in expanded group 0, bucket 0. */
252 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
253 ok1(h.home_bucket == 0);
254 ok1(h.found_bucket == 0);
255 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
256 + TDB_SUBLEVEL_HASH_BITS * 2);
261 ok1(tap_log_messages == 0);
262 return exit_status();