1 #include "tdb2-source.h"
2 #include <ccan/tap/tap.h>
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
)
8 return ((uint64_t)*(const unsigned int *)key
)
9 << (64 - TDB_TOPLEVEL_HASH_BITS
- 1);
12 int main(int argc
, char *argv
[])
15 struct tdb_context
*tdb
;
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
},
22 int flags
[] = { TDB_INTERNAL
, TDB_DEFAULT
, TDB_NOMMAP
,
23 TDB_INTERNAL
|TDB_CONVERT
, TDB_CONVERT
,
24 TDB_NOMMAP
|TDB_CONVERT
,
27 hattr
.base
.next
= &tap_log_attr
;
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
++) {
33 tdb_off_t new_off
, off
, subhash
;
35 tdb
= tdb_open("run-04-basichash.tdb", flags
[i
],
36 O_RDWR
|O_CREAT
|O_TRUNC
, 0600, &hattr
);
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
);
52 /* Should have lock on bucket 0 */
53 ok1(h
.hlock_start
== 0);
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 */
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
));
66 /* We should be able to add it now. */
67 ok1(add_to_hash(tdb
, &h
, new_off
) == 0);
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
));
73 ok1(!tdb
->tdb2
.io
->twrite(tdb
, off
, dbuf
.dptr
, dbuf
.dsize
));
75 /* We should be able to unlock that OK. */
76 ok1(tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
,
79 /* Database should be consistent. */
80 ok1(tdb_check(tdb
, NULL
, NULL
) == 0);
82 /* Now, this should give a successful lookup. */
83 ok1(find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
)
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
);
93 /* Should have lock on bucket 0 */
94 ok1(h
.hlock_start
== 0);
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 */
102 ok1(tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
,
105 /* Database should be consistent. */
106 ok1(tdb_check(tdb
, NULL
, NULL
) == 0);
108 /* Test expansion. */
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
);
119 /* Should have lock on bucket 0 */
120 ok1(h
.hlock_start
== 0);
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 */
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);
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
,
141 /* Should be happy with expansion. */
142 ok1(tdb_check(tdb
, NULL
, NULL
) == 0);
144 /* Should be able to find it. */
146 ok1(find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
)
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
);
157 /* Should have lock on bucket 0 */
158 ok1(h
.hlock_start
== 0);
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 */
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
,
176 ok1(tdb_check(tdb
, NULL
, NULL
) == 0);
178 /* Test second-level expansion: should expand 0th bucket. */
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
);
189 /* Should have lock on bucket 0 */
190 ok1(h
.hlock_start
== 0);
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 */
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
,
209 /* Should be happy with expansion. */
210 ok1(tdb_check(tdb
, NULL
, NULL
) == 0);
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);
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);
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
));
233 ok1(!tdb
->tdb2
.io
->twrite(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
, NULL
)
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);
258 ok1(tap_log_messages
== 0);
259 return exit_status();