f0a2b897e38032b17b8d00b3efe2cfdded708ebd
[ccan] / ccan / tdb2 / test / layout.c
1 /* TDB tools to create various canned database layouts. */
2 #include "layout.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6
7 struct tdb_layout *new_tdb_layout(void)
8 {
9         struct tdb_layout *layout = malloc(sizeof(*layout));
10         layout->num_elems = 0;
11         layout->elem = NULL;
12         layout->ftable = layout->htable = -1;
13         return layout;
14 }
15
16 static void add(struct tdb_layout *layout, union tdb_layout_elem elem)
17 {
18         layout->elem = realloc(layout->elem,
19                                sizeof(layout->elem[0])
20                                * (layout->num_elems+1));
21         layout->elem[layout->num_elems++] = elem;
22 }
23
24 void tdb_layout_add_free(struct tdb_layout *layout, tdb_len_t len)
25 {
26         union tdb_layout_elem elem;
27         elem.base.type = FREE;
28         elem.free.len = len;
29         add(layout, elem);
30 }
31
32 static struct tdb_data dup_key(struct tdb_data key)
33 {
34         struct tdb_data ret;
35         ret.dsize = key.dsize;
36         ret.dptr = malloc(ret.dsize);
37         memcpy(ret.dptr, key.dptr, ret.dsize);
38         return ret;
39 }
40
41 void tdb_layout_add_used(struct tdb_layout *layout,
42                          TDB_DATA key, TDB_DATA data,
43                          tdb_len_t extra)
44 {
45         union tdb_layout_elem elem;
46         elem.base.type = DATA;
47         elem.used.key = dup_key(key);
48         elem.used.data = dup_key(data);
49         elem.used.extra = extra;
50         add(layout, elem);
51 }
52
53 void tdb_layout_add_hashtable(struct tdb_layout *layout,
54                               unsigned int hash_bits,
55                               tdb_len_t extra)
56 {
57         union tdb_layout_elem elem;
58         elem.base.type = HASHTABLE;
59         elem.hashtable.hash_bits = hash_bits;
60         elem.hashtable.extra = extra;
61         assert(layout->htable == -1U);
62         layout->htable = layout->num_elems;
63         add(layout, elem);
64 }
65
66 void tdb_layout_add_freetable(struct tdb_layout *layout,
67                               unsigned int num_zones,
68                               unsigned int zone_bits,
69                               unsigned int num_buckets,
70                               tdb_len_t extra)
71 {
72         union tdb_layout_elem elem;
73         elem.base.type = FREETABLE;
74         elem.freetable.num_zones = num_zones;
75         elem.freetable.zone_bits = zone_bits;
76         elem.freetable.num_buckets = num_buckets;
77         elem.freetable.extra = extra;
78         assert(layout->ftable == -1U);
79         layout->ftable = layout->num_elems;
80         add(layout, elem);
81 }
82
83 static tdb_len_t free_record_len(tdb_len_t len)
84 {
85         return sizeof(struct tdb_used_record) + len;
86 }
87
88 static tdb_len_t data_record_len(struct tle_used *used)
89 {
90         tdb_len_t len;
91         len = sizeof(struct tdb_used_record)
92                 + used->key.dsize + used->data.dsize + used->extra;
93         assert(len >= sizeof(struct tdb_free_record));
94         return len;
95 }
96
97 static tdb_len_t hashtable_len(struct tle_hashtable *htable)
98 {
99         return sizeof(struct tdb_used_record)
100                 + (sizeof(tdb_off_t) << htable->hash_bits);
101 }
102
103 static tdb_len_t freetable_len(struct tle_freetable *ftable)
104 {
105         return sizeof(struct tdb_used_record)
106                 + (sizeof(tdb_off_t) * ftable->num_zones
107                    * (ftable->num_buckets + 1));
108 }
109
110 static void set_free_record(void *mem, tdb_len_t len)
111 {
112         /* We do all the work in add_to_freetable */
113 }
114
115 static void set_data_record(void *mem, struct tdb_context *tdb,
116                             struct tle_used *used)
117 {
118         struct tdb_used_record *u = mem;
119
120         set_header(tdb, u, used->key.dsize, used->data.dsize,
121                    used->key.dsize + used->data.dsize + used->extra,
122                    tdb_hash(tdb, used->key.dptr, used->key.dsize));
123         memcpy(u + 1, used->key.dptr, used->key.dsize);
124         memcpy((char *)(u + 1) + used->key.dsize,
125                used->data.dptr, used->data.dsize);
126 }
127
128 static void set_hashtable(void *mem, struct tdb_context *tdb,
129                           struct tle_hashtable *htable)
130 {
131         struct tdb_used_record *u = mem;
132         tdb_len_t len = sizeof(tdb_off_t) << htable->hash_bits;
133
134         set_header(tdb, u, 0, len, len + htable->extra, 0);
135         memset(u + 1, 0, len);
136 }
137
138 static void set_freetable(void *mem, struct tdb_context *tdb,
139                           struct tle_freetable *ftable)
140 {
141         struct tdb_used_record *u = mem;
142         tdb_len_t len = sizeof(tdb_off_t) * ftable->num_zones
143                 * (ftable->num_buckets + 1);
144         set_header(tdb, u, 0, len, len + ftable->extra, 0);
145         memset(u + 1, 0, len);
146 }
147
148 static void add_to_freetable(struct tdb_context *tdb,
149                              tdb_off_t eoff,
150                              tdb_off_t elen)
151 {
152         add_free_record(tdb, eoff, sizeof(struct tdb_used_record) + elen);
153 }
154
155 static tdb_off_t hash_off(struct tdb_context *tdb, uint64_t list)
156 {
157         return tdb->header.v.hash_off
158                 + ((list & ((1ULL << tdb->header.v.hash_bits) - 1))
159                    * sizeof(tdb_off_t));
160 }
161
162 static void add_to_hashtable(struct tdb_context *tdb,
163                              tdb_off_t eoff,
164                              struct tdb_data key)
165 {
166         uint64_t hash = tdb_hash(tdb, key.dptr, key.dsize);
167         tdb_off_t hoff;
168
169         while (tdb_read_off(tdb, hoff = hash_off(tdb, hash)) != 0)
170                 hash++;
171
172         tdb_write_off(tdb, hoff, eoff);
173 }
174
175 /* FIXME: Support TDB_CONVERT */
176 struct tdb_context *tdb_layout_get(struct tdb_layout *layout)
177 {
178         unsigned int i;
179         tdb_off_t len;
180         struct tdb_header *hdr;
181         char *mem;
182         struct tdb_context *tdb;
183
184         assert(layout->ftable != -1U);
185         assert(layout->htable != -1U);
186
187         len = sizeof(struct tdb_header);
188
189         /* First pass of layout: calc lengths */
190         for (i = 0; i < layout->num_elems; i++) {
191                 union tdb_layout_elem *e = &layout->elem[i];
192                 e->base.off = len;
193                 switch (e->base.type) {
194                 case FREE:
195                         len += free_record_len(e->free.len);
196                         break;
197                 case DATA:
198                         len += data_record_len(&e->used);
199                         break;
200                 case HASHTABLE:
201                         len += hashtable_len(&e->hashtable);
202                         break;
203                 case FREETABLE:
204                         len += freetable_len(&e->freetable);
205                         break;
206                 }
207         }
208
209         mem = malloc(len);
210         /* Now populate our header, cribbing from a real TDB header. */
211         tdb = tdb_open(NULL, TDB_INTERNAL, O_RDWR, 0, NULL);
212         hdr = (void *)mem;
213         *hdr = tdb->header;
214         hdr->v.generation++;
215         hdr->v.num_zones = layout->elem[layout->ftable].freetable.num_zones;
216         hdr->v.zone_bits = layout->elem[layout->ftable].freetable.zone_bits;
217         hdr->v.free_buckets
218                 = layout->elem[layout->ftable].freetable.num_buckets;
219         hdr->v.free_off = layout->elem[layout->ftable].base.off
220                 + sizeof(struct tdb_used_record);
221         hdr->v.hash_bits = layout->elem[layout->htable].hashtable.hash_bits;
222         hdr->v.hash_off = layout->elem[layout->htable].base.off
223                 + sizeof(struct tdb_used_record);
224
225         /* Mug the tdb we have to make it use this. */
226         free(tdb->map_ptr);
227         tdb->map_ptr = mem;
228         tdb->map_size = len;
229         header_changed(tdb);
230
231         for (i = 0; i < layout->num_elems; i++) {
232                 union tdb_layout_elem *e = &layout->elem[i];
233                 switch (e->base.type) {
234                 case FREE:
235                         set_free_record(mem + e->base.off, e->free.len);
236                         break;
237                 case DATA:
238                         set_data_record(mem + e->base.off, tdb, &e->used);
239                         break;
240                 case HASHTABLE:
241                         set_hashtable(mem + e->base.off, tdb, &e->hashtable);
242                         break;
243                 case FREETABLE:
244                         set_freetable(mem + e->base.off, tdb, &e->freetable);
245                         break;
246                 }
247         }
248
249         /* Now fill the free and hash tables. */
250         for (i = 0; i < layout->num_elems; i++) {
251                 union tdb_layout_elem *e = &layout->elem[i];
252                 switch (e->base.type) {
253                 case FREE:
254                         add_to_freetable(tdb, e->base.off, e->free.len);
255                         break;
256                 case DATA:
257                         add_to_hashtable(tdb, e->base.off, e->used.key);
258                         break;
259                 default:
260                         break;
261                 }
262         }
263
264         return tdb;
265 }