tdb2: get rid of zones
[ccan] / ccan / tdb2 / test / run-04-basichash.c
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>
8 #include "logging.h"
9
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)
12 {
13         return ((uint64_t)*(unsigned int *)key)
14                 << (64 - TDB_TOPLEVEL_HASH_BITS - 1);
15 }
16
17 int main(int argc, char *argv[])
18 {
19         unsigned int i, j;
20         struct tdb_context *tdb;
21         unsigned int v;
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 },
26                                                 .hash_fn = clash } };
27         int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
28                         TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
29                         TDB_NOMMAP|TDB_CONVERT,
30         };
31
32         hattr.base.next = &tap_log_attr;
33
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++) {
37                 struct hash_info h;
38                 tdb_off_t new_off, off, subhash;
39
40                 tdb = tdb_open("run-04-basichash.tdb", flags[i],
41                                O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
42                 ok1(tdb);
43                 if (!tdb)
44                         continue;
45
46                 v = 0;
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);
56
57                 /* Should have lock on bucket 0 */
58                 ok1(h.hlock_start == 0);
59                 ok1(h.hlock_range == 
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 */
65
66                 /* Allocate a new record. */
67                 new_off = alloc(tdb, key.dsize, dbuf.dsize, h.h, false);
68                 ok1(new_off != TDB_OFF_ERR);
69
70                 /* We should be able to add it now. */
71                 ok1(add_to_hash(tdb, &h, new_off) == 0);
72
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));
76                 off += key.dsize;
77                 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
78
79                 /* We should be able to unlock that OK. */
80                 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
81                                       F_WRLCK) == 0);
82
83                 /* Database should be consistent. */
84                 ok1(tdb_check(tdb, NULL, NULL) == 0);
85
86                 /* Now, this should give a successful lookup. */
87                 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
88                     == new_off);
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);
96
97                 /* Should have lock on bucket 0 */
98                 ok1(h.hlock_start == 0);
99                 ok1(h.hlock_range == 
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 */
105
106                 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
107                                       F_WRLCK) == 0);
108                 
109                 /* Database should be consistent. */
110                 ok1(tdb_check(tdb, NULL, NULL) == 0);
111
112                 /* Test expansion. */
113                 v = 1;
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);
122
123                 /* Should have lock on bucket 0 */
124                 ok1(h.hlock_start == 0);
125                 ok1(h.hlock_range == 
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 */
131
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);
139
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,
143                                       F_WRLCK) == 0);
144
145                 /* Should be happy with expansion. */
146                 ok1(tdb_check(tdb, NULL, NULL) == 0);
147
148                 /* Should be able to find it. */
149                 v = 0;
150                 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
151                     == new_off);
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);
160
161                 /* Should have lock on bucket 0 */
162                 ok1(h.hlock_start == 0);
163                 ok1(h.hlock_range == 
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 */
169
170                 /* Simple delete should work. */
171                 ok1(delete_from_hash(tdb, &h) == 0);
172                 ok1(add_free_record(tdb, 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,
178                                       F_WRLCK) == 0);
179                 ok1(tdb_check(tdb, NULL, NULL) == 0);
180
181                 /* Test second-level expansion: should expand 0th bucket. */
182                 v = 0;
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);
191
192                 /* Should have lock on bucket 0 */
193                 ok1(h.hlock_start == 0);
194                 ok1(h.hlock_range == 
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 */
200
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,
210                                       F_WRLCK) == 0);
211
212                 /* Should be happy with expansion. */
213                 ok1(tdb_check(tdb, NULL, NULL) == 0);
214
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);
224
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);
230
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));
234                 off += key.dsize;
235                 ok1(!tdb->methods->write(tdb, off, dbuf.dptr, dbuf.dsize));
236
237                 /* We should be able to unlock that OK. */
238                 ok1(tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
239                                       F_WRLCK) == 0);
240
241                 /* Database should be consistent. */
242                 ok1(tdb_check(tdb, NULL, NULL) == 0);
243
244                 /* Should be able to find it. */
245                 v = 0;
246                 ok1(find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL)
247                     == new_off);
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);
256
257                 tdb_close(tdb);
258         }
259
260         ok1(tap_log_messages == 0);
261         return exit_status();
262 }