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/check.c>
7 #include <ccan/tap/tap.h>
10 /* We rig the hash so adjacent-numbered records always clash. */
11 static uint64_t clash(const void *key, size_t len, uint64_t seed, void *priv)
13 return ((uint64_t)*(unsigned int *)key)
14 << (64 - TDB_TOPLEVEL_HASH_BITS - 1);
17 int main(int argc, char *argv[])
20 struct tdb_context *tdb;
22 struct tdb_used_record rec;
23 struct tdb_data key = { (unsigned char *)&v, sizeof(v) };
24 struct tdb_data dbuf = { (unsigned char *)&v, sizeof(v) };
25 union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
27 int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
28 TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
29 TDB_NOMMAP|TDB_CONVERT,
32 hattr.base.next = &tap_log_attr;
34 plan_tests(sizeof(flags) / sizeof(flags[0])
35 * (91 + (2 * ((1 << TDB_HASH_GROUP_BITS) - 1))) + 1);
36 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
38 tdb_off_t new_off, off, subhash;
40 tdb = tdb_open("run-04-basichash.tdb", flags[i],
41 O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
47 /* Should not find it. */
48 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
49 /* Should have created correct hash. */
50 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
51 /* Should have located space in group 0, bucket 0. */
52 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
53 ok1(h.home_bucket == 0);
54 ok1(h.found_bucket == 0);
55 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
57 /* Should have lock on bucket 0 */
58 ok1(h.hlock_start == 0);
60 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
61 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
62 ok1((tdb->flags & TDB_NOLOCK)
63 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
64 /* FIXME: Check lock length */
66 /* Allocate a new record. */
67 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h, false);
68 ok1(new_off != TDB_OFF_ERR);
70 /* We should be able to add it now. */
71 ok1(add_to_hash(tdb, &h, new_off) == 0);
73 /* Make sure we fill it in for later finding. */
74 off = new_off + sizeof(struct tdb_used_record);
75 ok1(!tdb->methods->write(tdb, off, key.dptr, key.dsize));
77 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
79 /* We should be able to unlock that OK. */
80 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
83 /* Database should be consistent. */
84 ok1(tdb_check(tdb, NULL, NULL) == 0);
86 /* Now, this should give a successful lookup. */
87 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
89 /* Should have created correct hash. */
90 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
91 /* Should have located space in group 0, bucket 0. */
92 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
93 ok1(h.home_bucket == 0);
94 ok1(h.found_bucket == 0);
95 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
97 /* Should have lock on bucket 0 */
98 ok1(h.hlock_start == 0);
100 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
101 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
102 ok1((tdb->flags & TDB_NOLOCK)
103 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
104 /* FIXME: Check lock length */
106 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
109 /* Database should be consistent. */
110 ok1(tdb_check(tdb, NULL, NULL) == 0);
112 /* Test expansion. */
114 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
115 /* Should have created correct hash. */
116 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
117 /* Should have located space in group 0, bucket 1. */
118 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
119 ok1(h.home_bucket == 0);
120 ok1(h.found_bucket == 1);
121 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
123 /* Should have lock on bucket 0 */
124 ok1(h.hlock_start == 0);
126 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
127 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
128 ok1((tdb->flags & TDB_NOLOCK)
129 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
130 /* FIXME: Check lock length */
132 /* Make it expand 0'th bucket. */
133 ok1(expand_group(tdb, &h) == 0);
134 /* First one should be subhash, next should be empty. */
135 ok1(is_subhash(h.group[0]));
136 subhash = (h.group[0] & TDB_OFF_MASK);
137 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
138 ok1(h.group[j] == 0);
140 ok1(tdb_write_convert(tdb, h.group_start,
141 h.group, sizeof(h.group)) == 0);
142 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
145 /* Should be happy with expansion. */
146 ok1(tdb_check(tdb, NULL, NULL) == 0);
148 /* Should be able to find it. */
150 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
152 /* Should have created correct hash. */
153 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
154 /* Should have located space in expanded group 0, bucket 0. */
155 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
156 ok1(h.home_bucket == 0);
157 ok1(h.found_bucket == 0);
158 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
159 + TDB_SUBLEVEL_HASH_BITS);
161 /* Should have lock on bucket 0 */
162 ok1(h.hlock_start == 0);
164 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
165 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
166 ok1((tdb->flags & TDB_NOLOCK)
167 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
168 /* FIXME: Check lock length */
170 /* Simple delete should work. */
171 ok1(delete_from_hash(tdb, &h) == 0);
172 ok1(add_free_record(tdb, rec_zone_bits(&rec), new_off,
173 sizeof(struct tdb_used_record)
174 + rec_key_length(&rec)
175 + rec_data_length(&rec)
176 + rec_extra_padding(&rec)) == 0);
177 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
179 ok1(tdb_check(tdb, NULL, NULL) == 0);
181 /* Test second-level expansion: should expand 0th bucket. */
183 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
184 /* Should have created correct hash. */
185 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
186 /* Should have located space in group 0, bucket 0. */
187 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
188 ok1(h.home_bucket == 0);
189 ok1(h.found_bucket == 0);
190 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS+TDB_SUBLEVEL_HASH_BITS);
192 /* Should have lock on bucket 0 */
193 ok1(h.hlock_start == 0);
195 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
196 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
197 ok1((tdb->flags & TDB_NOLOCK)
198 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
199 /* FIXME: Check lock length */
201 ok1(expand_group(tdb, &h) == 0);
202 /* First one should be subhash, next should be empty. */
203 ok1(is_subhash(h.group[0]));
204 subhash = (h.group[0] & TDB_OFF_MASK);
205 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
206 ok1(h.group[j] == 0);
207 ok1(tdb_write_convert(tdb, h.group_start,
208 h.group, sizeof(h.group)) == 0);
209 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
212 /* Should be happy with expansion. */
213 ok1(tdb_check(tdb, NULL, NULL) == 0);
215 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL) == 0);
216 /* Should have created correct hash. */
217 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
218 /* Should have located space in group 0, bucket 0. */
219 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
220 ok1(h.home_bucket == 0);
221 ok1(h.found_bucket == 0);
222 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
223 + TDB_SUBLEVEL_HASH_BITS * 2);
225 /* We should be able to add it now. */
226 /* Allocate a new record. */
227 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h, false);
228 ok1(new_off != TDB_OFF_ERR);
229 ok1(add_to_hash(tdb, &h, new_off) == 0);
231 /* Make sure we fill it in for later finding. */
232 off = new_off + sizeof(struct tdb_used_record);
233 ok1(!tdb->methods->write(tdb, off, key.dptr, key.dsize));
235 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
237 /* We should be able to unlock that OK. */
238 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
241 /* Database should be consistent. */
242 ok1(tdb_check(tdb, NULL, NULL) == 0);
244 /* Should be able to find it. */
246 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
248 /* Should have created correct hash. */
249 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
250 /* Should have located space in expanded group 0, bucket 0. */
251 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
252 ok1(h.home_bucket == 0);
253 ok1(h.found_bucket == 0);
254 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
255 + TDB_SUBLEVEL_HASH_BITS * 2);
260 ok1(tap_log_messages == 0);
261 return exit_status();