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