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) == 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) == new_off);
88 /* Should have created correct hash. */
89 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
90 /* Should have located space in group 0, bucket 0. */
91 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
92 ok1(h.home_bucket == 0);
93 ok1(h.found_bucket == 0);
94 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
96 /* Should have lock on bucket 0 */
97 ok1(h.hlock_start == 0);
99 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
100 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
101 ok1((tdb->flags & TDB_NOLOCK)
102 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
103 /* FIXME: Check lock length */
105 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
108 /* Database should be consistent. */
109 ok1(tdb_check(tdb, NULL, NULL) == 0);
111 /* Test expansion. */
113 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec) == 0);
114 /* Should have created correct hash. */
115 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
116 /* Should have located space in group 0, bucket 1. */
117 ok1(h.group_start == offsetof(struct tdb_header, hashtable));
118 ok1(h.home_bucket == 0);
119 ok1(h.found_bucket == 1);
120 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS);
122 /* Should have lock on bucket 0 */
123 ok1(h.hlock_start == 0);
125 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
126 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
127 ok1((tdb->flags & TDB_NOLOCK)
128 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
129 /* FIXME: Check lock length */
131 /* Make it expand 0'th bucket. */
132 ok1(expand_group(tdb, &h) == 0);
133 /* First one should be subhash, next should be empty. */
134 ok1(is_subhash(h.group[0]));
135 subhash = (h.group[0] & TDB_OFF_MASK);
136 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
137 ok1(h.group[j] == 0);
139 ok1(tdb_write_convert(tdb, h.group_start,
140 h.group, sizeof(h.group)) == 0);
141 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
144 /* Should be happy with expansion. */
145 ok1(tdb_check(tdb, NULL, NULL) == 0);
147 /* Should be able to find it. */
149 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec) == new_off);
150 /* Should have created correct hash. */
151 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
152 /* Should have located space in expanded group 0, bucket 0. */
153 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
154 ok1(h.home_bucket == 0);
155 ok1(h.found_bucket == 0);
156 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
157 + TDB_SUBLEVEL_HASH_BITS);
159 /* Should have lock on bucket 0 */
160 ok1(h.hlock_start == 0);
162 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
163 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
164 ok1((tdb->flags & TDB_NOLOCK)
165 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
166 /* FIXME: Check lock length */
168 /* Simple delete should work. */
169 ok1(delete_from_hash(tdb, &h) == 0);
170 ok1(add_free_record(tdb, rec_zone_bits(&rec), new_off,
171 sizeof(struct tdb_used_record)
172 + rec_key_length(&rec)
173 + rec_data_length(&rec)
174 + rec_extra_padding(&rec)) == 0);
175 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
177 ok1(tdb_check(tdb, NULL, NULL) == 0);
179 /* Test second-level expansion: should expand 0th bucket. */
181 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec) == 0);
182 /* Should have created correct hash. */
183 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
184 /* Should have located space in group 0, bucket 0. */
185 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
186 ok1(h.home_bucket == 0);
187 ok1(h.found_bucket == 0);
188 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS+TDB_SUBLEVEL_HASH_BITS);
190 /* Should have lock on bucket 0 */
191 ok1(h.hlock_start == 0);
193 1ULL << (64-(TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS)));
194 ok1((tdb->flags & TDB_NOLOCK) || tdb->num_lockrecs == 1);
195 ok1((tdb->flags & TDB_NOLOCK)
196 || tdb->lockrecs[0].off == TDB_HASH_LOCK_START);
197 /* FIXME: Check lock length */
199 ok1(expand_group(tdb, &h) == 0);
200 /* First one should be subhash, next should be empty. */
201 ok1(is_subhash(h.group[0]));
202 subhash = (h.group[0] & TDB_OFF_MASK);
203 for (j = 1; j < (1 << TDB_HASH_GROUP_BITS); j++)
204 ok1(h.group[j] == 0);
205 ok1(tdb_write_convert(tdb, h.group_start,
206 h.group, sizeof(h.group)) == 0);
207 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
210 /* Should be happy with expansion. */
211 ok1(tdb_check(tdb, NULL, NULL) == 0);
213 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec) == 0);
214 /* Should have created correct hash. */
215 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
216 /* Should have located space in group 0, bucket 0. */
217 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
218 ok1(h.home_bucket == 0);
219 ok1(h.found_bucket == 0);
220 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
221 + TDB_SUBLEVEL_HASH_BITS * 2);
223 /* We should be able to add it now. */
224 /* Allocate a new record. */
225 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h, false);
226 ok1(new_off != TDB_OFF_ERR);
227 ok1(add_to_hash(tdb, &h, new_off) == 0);
229 /* Make sure we fill it in for later finding. */
230 off = new_off + sizeof(struct tdb_used_record);
231 ok1(!tdb->methods->write(tdb, off, key.dptr, key.dsize));
233 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
235 /* We should be able to unlock that OK. */
236 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
239 /* Database should be consistent. */
240 ok1(tdb_check(tdb, NULL, NULL) == 0);
242 /* Should be able to find it. */
244 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec) == new_off);
245 /* Should have created correct hash. */
246 ok1(h.h == tdb_hash(tdb, key.dptr, key.dsize));
247 /* Should have located space in expanded group 0, bucket 0. */
248 ok1(h.group_start == subhash + sizeof(struct tdb_used_record));
249 ok1(h.home_bucket == 0);
250 ok1(h.found_bucket == 0);
251 ok1(h.hash_used == TDB_TOPLEVEL_HASH_BITS
252 + TDB_SUBLEVEL_HASH_BITS * 2);
257 ok1(tap_log_messages == 0);
258 return exit_status();