1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- |
---|
2 | * |
---|
3 | * Copyright (C) 2000 Ximian, Inc. |
---|
4 | * |
---|
5 | * Authors: Michael Zucchi <notzed@ximian.com> |
---|
6 | * |
---|
7 | * This program is free software; you can redistribute it and/or |
---|
8 | * modify it under the terms of version 2 of the GNU General Public |
---|
9 | * License as published by the Free Software Foundation. |
---|
10 | * |
---|
11 | * This program is distributed in the hope that it will be useful, |
---|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
14 | * General Public License for more details. |
---|
15 | * |
---|
16 | * You should have received a copy of the GNU General Public |
---|
17 | * License along with this program; if not, write to the |
---|
18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
19 | * Boston, MA 02111-1307, USA. |
---|
20 | */ |
---|
21 | |
---|
22 | /* a disk based array storage class that stores the tails of data lists |
---|
23 | in common blocks */ |
---|
24 | |
---|
25 | |
---|
26 | #include <sys/types.h> |
---|
27 | #include <sys/stat.h> |
---|
28 | #include <fcntl.h> |
---|
29 | #include <unistd.h> |
---|
30 | #include <string.h> |
---|
31 | #include <stdio.h> |
---|
32 | |
---|
33 | #include <glib.h> |
---|
34 | |
---|
35 | #include "block.h" |
---|
36 | #include "index.h" |
---|
37 | |
---|
38 | #define d(x) |
---|
39 | /*#define DEBUG*/ |
---|
40 | |
---|
41 | /* marker to define which root keys indicate a single length key */ |
---|
42 | #define BLOCK_ONE (1<<BLOCK_BITS) |
---|
43 | |
---|
44 | /* tail blocks only contain tail data ... */ |
---|
45 | /* and we pack it in, similar to the key data, only more compact */ |
---|
46 | struct _tailblock { |
---|
47 | unsigned int next:32-BLOCK_BITS; /* only needs to point to block numbers */ |
---|
48 | unsigned int used:BLOCK_BITS; /* how many entries are used */ |
---|
49 | union { |
---|
50 | unsigned char offset[BLOCK_SIZE-4]; /* works upto blocksize of 1024 bytes */ |
---|
51 | nameid_t data[(BLOCK_SIZE-4)/4]; |
---|
52 | } tailblock_u; |
---|
53 | }; |
---|
54 | #define tb_offset tailblock_u.offset |
---|
55 | #define tb_data tailblock_u.data |
---|
56 | |
---|
57 | /* map a tail index to a block index */ |
---|
58 | #define TAIL_INDEX(b) ((b) & (BLOCK_SIZE-1)) |
---|
59 | /* map a tail index to a block number */ |
---|
60 | #define TAIL_BLOCK(b) ((b) & ~(BLOCK_SIZE-1)) |
---|
61 | /* map a block + index to a tailid */ |
---|
62 | #define TAIL_KEY(b, i) (((b) & ~(BLOCK_SIZE-1)) | ((i) & (BLOCK_SIZE-1))) |
---|
63 | |
---|
64 | #define TAIL_THRESHOLD ((BLOCK_SIZE-8)/6) |
---|
65 | |
---|
66 | static struct _IBEXStore *disk_create(struct _memcache *bc); |
---|
67 | static int disk_sync(struct _IBEXStore *store); |
---|
68 | static int disk_close(struct _IBEXStore *store); |
---|
69 | |
---|
70 | static blockid_t disk_add(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, nameid_t data); |
---|
71 | static blockid_t disk_add_list(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, GArray *data); |
---|
72 | static blockid_t disk_remove(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, nameid_t data); |
---|
73 | static void disk_free(struct _IBEXStore *store, blockid_t head, blockid_t tail); |
---|
74 | |
---|
75 | static gboolean disk_find(struct _IBEXStore *store, blockid_t head, blockid_t tail, nameid_t data); |
---|
76 | static GArray *disk_get(struct _IBEXStore *store, blockid_t head, blockid_t tail); |
---|
77 | |
---|
78 | struct _IBEXStoreClass ibex_diskarray_class = { |
---|
79 | disk_create, disk_sync, disk_close, |
---|
80 | disk_add, disk_add_list, |
---|
81 | disk_remove, disk_free, |
---|
82 | disk_find, disk_get |
---|
83 | }; |
---|
84 | |
---|
85 | |
---|
86 | static int |
---|
87 | tail_info(struct _memcache *blocks, struct _tailblock *bucket, nameid_t tailid, blockid_t **startptr) |
---|
88 | { |
---|
89 | blockid_t *start, *end; |
---|
90 | int index; |
---|
91 | |
---|
92 | /* get start/end of area to zap */ |
---|
93 | index = TAIL_INDEX(tailid); |
---|
94 | start = &bucket->tb_data[bucket->tb_offset[index]]; |
---|
95 | if (index == 0) { |
---|
96 | end = &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])]; |
---|
97 | } else { |
---|
98 | end = &bucket->tb_data[bucket->tb_offset[index-1]]; |
---|
99 | } |
---|
100 | if (startptr) |
---|
101 | *startptr = start; |
---|
102 | |
---|
103 | ibex_block_cache_assert(blocks, end >= start); |
---|
104 | |
---|
105 | return end-start; |
---|
106 | } |
---|
107 | |
---|
108 | /* compresses (or expand) the bucket entry, to the new size */ |
---|
109 | static void |
---|
110 | tail_compress(struct _memcache *blocks, struct _tailblock *bucket, int index, int newsize) |
---|
111 | { |
---|
112 | int i; |
---|
113 | blockid_t *start, *end, *newstart; |
---|
114 | |
---|
115 | /* get start/end of area to zap */ |
---|
116 | start = &bucket->tb_data[bucket->tb_offset[index]]; |
---|
117 | if (index == 0) { |
---|
118 | end = &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])]; |
---|
119 | } else { |
---|
120 | end = &bucket->tb_data[bucket->tb_offset[index-1]]; |
---|
121 | } |
---|
122 | |
---|
123 | if (end-start == newsize) |
---|
124 | return; |
---|
125 | |
---|
126 | /* |
---|
127 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXyyyyy |
---|
128 | 0 20 25 |
---|
129 | |
---|
130 | newsize = 0 |
---|
131 | end = 25 |
---|
132 | newstart = 0 |
---|
133 | start = 20 |
---|
134 | |
---|
135 | newstart+(end-start)-newsize = 5 |
---|
136 | i = start-newstart |
---|
137 | |
---|
138 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXyyyyy |
---|
139 | 0 20 25 |
---|
140 | |
---|
141 | newsize = 2 |
---|
142 | end = 25 |
---|
143 | newstart = 0 |
---|
144 | start = 20 |
---|
145 | |
---|
146 | newstart+(end-start)-newsize = 3 |
---|
147 | i = start-newstart + MIN(end-start, newsize)) = 22 |
---|
148 | |
---|
149 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
---|
150 | 5 25 |
---|
151 | newsize = 5 |
---|
152 | end = 25 |
---|
153 | start = 25 |
---|
154 | newstart = 5 |
---|
155 | |
---|
156 | newstart+(end-start)-newsize = 0 |
---|
157 | i = start-newstart = 20 |
---|
158 | |
---|
159 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXyy |
---|
160 | 3 23 25 |
---|
161 | newsize = 5 |
---|
162 | end = 25 |
---|
163 | start = 23 |
---|
164 | newstart = 3 |
---|
165 | |
---|
166 | newstart+(end-start)-newsize = 0 |
---|
167 | i = start-newstart + MIN(end-start, newsize) = 22 |
---|
168 | |
---|
169 | */ |
---|
170 | |
---|
171 | |
---|
172 | /* fixup data */ |
---|
173 | newstart = &bucket->tb_data[bucket->tb_offset[bucket->used-1]]; |
---|
174 | |
---|
175 | ibex_block_cache_assert(blocks, newstart+(end-start)-newsize <= &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])]); |
---|
176 | ibex_block_cache_assert(blocks, newstart + (start-newstart) + MIN(end-start, newsize) <= &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])]); |
---|
177 | ibex_block_cache_assert(blocks, newstart+(end-start)-newsize >= (blockid_t *) &bucket->tb_offset[bucket->used]); |
---|
178 | ibex_block_cache_assert(blocks, newstart + (start-newstart) + MIN(end-start, newsize) >= (blockid_t *) &bucket->tb_offset[bucket->used]); |
---|
179 | |
---|
180 | memmove(newstart+(end-start)-newsize, newstart, ((start-newstart)+MIN(end-start, newsize)) * sizeof(blockid_t)); |
---|
181 | |
---|
182 | /* fixup key pointers */ |
---|
183 | for (i=index;i<bucket->used;i++) { |
---|
184 | bucket->tb_offset[i] += (end-start)-newsize; |
---|
185 | } |
---|
186 | ibex_block_dirty((struct _block *)bucket); |
---|
187 | } |
---|
188 | |
---|
189 | /* |
---|
190 | returns the number of blockid's free |
---|
191 | */ |
---|
192 | static int |
---|
193 | tail_space(struct _tailblock *tail) |
---|
194 | { |
---|
195 | if (tail->used == 0) |
---|
196 | return sizeof(tail->tb_data)/sizeof(tail->tb_data[0])-1; |
---|
197 | |
---|
198 | return &tail->tb_data[tail->tb_offset[tail->used-1]] |
---|
199 | - (blockid_t *)&tail->tb_offset[tail->used] - 1; |
---|
200 | } |
---|
201 | |
---|
202 | #if 0 |
---|
203 | static void |
---|
204 | tail_dump(struct _memcache *blocks, blockid_t tailid) |
---|
205 | { |
---|
206 | int i; |
---|
207 | struct _tailblock *tail = (struct _tailblock *)ibex_block_read(blocks, TAIL_BLOCK(tailid)); |
---|
208 | |
---|
209 | printf("Block %d, used %d\n", tailid, tail->used); |
---|
210 | for (i=0;i<sizeof(struct _tailblock)/sizeof(unsigned int);i++) { |
---|
211 | printf(" %08x", ((unsigned int *)tail)[i]); |
---|
212 | } |
---|
213 | printf("\n"); |
---|
214 | } |
---|
215 | #endif |
---|
216 | |
---|
217 | static blockid_t |
---|
218 | tail_get(struct _memcache *blocks, int size) |
---|
219 | { |
---|
220 | blockid_t tailid; |
---|
221 | struct _tailblock *tail; |
---|
222 | int freeindex; |
---|
223 | blockid_t *end; |
---|
224 | int i, count = 0; |
---|
225 | |
---|
226 | d(printf("looking for a tail node with %d items in it\n", size)); |
---|
227 | |
---|
228 | /* look for a node with enough space, if we dont find it fairly |
---|
229 | quickly, just quit. needs a better free algorithm i think ... */ |
---|
230 | tailid = blocks->root.tail; |
---|
231 | while (tailid && count<5) { |
---|
232 | int space; |
---|
233 | |
---|
234 | d(printf(" checking tail node %d\n", tailid)); |
---|
235 | |
---|
236 | tail = (struct _tailblock *)ibex_block_read(blocks, tailid); |
---|
237 | |
---|
238 | if (tail->used == 0) { |
---|
239 | /* assume its big enough ... */ |
---|
240 | tail->used = 1; |
---|
241 | tail->tb_offset[0] = sizeof(tail->tb_data)/sizeof(tail->tb_data[0]) - size; |
---|
242 | d(printf("allocated %d (%d), used %d\n", tailid, tailid, tail->used)); |
---|
243 | ibex_block_dirty((struct _block *)tail); |
---|
244 | |
---|
245 | ibex_block_cache_assert(blocks, &tail->tb_offset[tail->used-1] |
---|
246 | < (unsigned char *) &tail->tb_data[tail->tb_offset[tail->used-1]]); |
---|
247 | |
---|
248 | return tailid; |
---|
249 | } |
---|
250 | |
---|
251 | ibex_block_cache_assert(blocks, &tail->tb_offset[tail->used-1] |
---|
252 | < (unsigned char *) &tail->tb_data[tail->tb_offset[tail->used-1]]); |
---|
253 | |
---|
254 | /* see if we have a free slot first */ |
---|
255 | freeindex = -1; |
---|
256 | end = &tail->tb_data[sizeof(tail->tb_data)/sizeof(tail->tb_data[0])]; |
---|
257 | for (i=0;i<tail->used;i++) { |
---|
258 | if (end == &tail->tb_data[tail->tb_offset[i]]) { |
---|
259 | freeindex = i; |
---|
260 | break; |
---|
261 | } |
---|
262 | end = &tail->tb_data[tail->tb_offset[i]]; |
---|
263 | } |
---|
264 | |
---|
265 | /* determine how much space we have available - incl any extra header we might need */ |
---|
266 | space = ((char *)&tail->tb_data[tail->tb_offset[tail->used-1]]) |
---|
267 | - ((char *)&tail->tb_offset[tail->used]) |
---|
268 | /* FIXMEL work out why this is out a little bit */ |
---|
269 | - 8; |
---|
270 | if (freeindex == -1) |
---|
271 | space -= sizeof(tail->tb_offset[0]); |
---|
272 | |
---|
273 | /* if we have enough, set it up, creating a new entry if necessary */ |
---|
274 | /* for some really odd reason the compiler promotes this expression to unsigned, hence |
---|
275 | the requirement for the space>0 check ... */ |
---|
276 | if (space>0 && space > size*sizeof(blockid_t)) { |
---|
277 | d(printf("space = %d, size = %d size*sizeof() = %d truth = %d\n", space, size, size*sizeof(blockid_t), space>size*sizeof(blockid_t))); |
---|
278 | if (freeindex == -1) { |
---|
279 | freeindex = tail->used; |
---|
280 | tail->tb_offset[tail->used] = tail->tb_offset[tail->used-1]; |
---|
281 | tail->used++; |
---|
282 | } |
---|
283 | tail_compress(blocks, tail, freeindex, size); |
---|
284 | ibex_block_dirty((struct _block *)tail); |
---|
285 | d(printf("allocated %d (%d), used %d\n", tailid, TAIL_KEY(tailid, freeindex), tail->used)); |
---|
286 | return TAIL_KEY(tailid, freeindex); |
---|
287 | } |
---|
288 | count++; |
---|
289 | tailid = block_location(tail->next); |
---|
290 | } |
---|
291 | |
---|
292 | d(printf("allocating new data node for tail data\n")); |
---|
293 | tailid = ibex_block_get(blocks); |
---|
294 | tail = (struct _tailblock *)ibex_block_read(blocks, tailid); |
---|
295 | tail->next = block_number(blocks->root.tail); |
---|
296 | blocks->root.tail = tailid; |
---|
297 | tail->used = 1; |
---|
298 | tail->tb_offset[0] = sizeof(tail->tb_data)/sizeof(tail->tb_data[0]) - size; |
---|
299 | ibex_block_dirty((struct _block *)tail); |
---|
300 | d(printf("allocated %d (%d), used %d\n", tailid, TAIL_KEY(tailid, 0), tail->used)); |
---|
301 | |
---|
302 | g_assert(&tail->tb_offset[tail->used-1] |
---|
303 | < (unsigned char *) &tail->tb_data[tail->tb_offset[tail->used-1]]); |
---|
304 | |
---|
305 | return TAIL_KEY(tailid, 0); |
---|
306 | } |
---|
307 | |
---|
308 | static void |
---|
309 | tail_free(struct _memcache *blocks, blockid_t tailid) |
---|
310 | { |
---|
311 | struct _tailblock *tail; |
---|
312 | |
---|
313 | d(printf("freeing tail id %d\n", tailid)); |
---|
314 | |
---|
315 | if (tailid == 0) |
---|
316 | return; |
---|
317 | |
---|
318 | tail = (struct _tailblock *)ibex_block_read(blocks, TAIL_BLOCK(tailid)); |
---|
319 | d(printf(" tail %d used %d\n", tailid, tail->used)); |
---|
320 | ibex_block_cache_assert(blocks, tail->used); |
---|
321 | if (TAIL_INDEX(tailid) == tail->used - 1) { |
---|
322 | tail->used --; |
---|
323 | } else { |
---|
324 | tail_compress(blocks, tail, TAIL_INDEX(tailid), 0); |
---|
325 | } |
---|
326 | ibex_block_dirty((struct _block *)tail); |
---|
327 | } |
---|
328 | |
---|
329 | static struct _IBEXStore *disk_create(struct _memcache *bc) |
---|
330 | { |
---|
331 | struct _IBEXStore *store; |
---|
332 | |
---|
333 | store = g_malloc0(sizeof(*store)); |
---|
334 | store->klass = &ibex_diskarray_class; |
---|
335 | store->blocks = bc; |
---|
336 | |
---|
337 | return store; |
---|
338 | } |
---|
339 | |
---|
340 | static int disk_sync(struct _IBEXStore *store) |
---|
341 | { |
---|
342 | /* no cache, nop */ |
---|
343 | return 0; |
---|
344 | } |
---|
345 | |
---|
346 | static int disk_close(struct _IBEXStore *store) |
---|
347 | { |
---|
348 | g_free(store); |
---|
349 | return 0; |
---|
350 | } |
---|
351 | |
---|
352 | static blockid_t |
---|
353 | disk_add(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, nameid_t data) |
---|
354 | { |
---|
355 | struct _block *block; |
---|
356 | struct _block *newblock; |
---|
357 | blockid_t new, head = *headptr /*, tail = *tailptr*/; |
---|
358 | |
---|
359 | g_error("Inbimplemented"); |
---|
360 | |
---|
361 | if (head == 0) { |
---|
362 | head = ibex_block_get(store->blocks); |
---|
363 | } |
---|
364 | block = ibex_block_read(store->blocks, head); |
---|
365 | |
---|
366 | d(printf("adding record %d to block %d (next = %d)\n", data, head, block->next)); |
---|
367 | |
---|
368 | if (block->used < sizeof(block->bl_data)/sizeof(block->bl_data[0])) { |
---|
369 | (printf("adding record into block %d %d\n", head, data)); |
---|
370 | block->bl_data[block->used] = data; |
---|
371 | block->used++; |
---|
372 | ibex_block_dirty(block); |
---|
373 | return head; |
---|
374 | } else { |
---|
375 | new = ibex_block_get(store->blocks); |
---|
376 | newblock = ibex_block_read(store->blocks, new); |
---|
377 | newblock->next = block_number(head); |
---|
378 | newblock->bl_data[0] = data; |
---|
379 | newblock->used = 1; |
---|
380 | d(printf("adding record into new %d %d, next =%d\n", new, data, newblock->next)); |
---|
381 | ibex_block_dirty(newblock); |
---|
382 | return new; |
---|
383 | } |
---|
384 | } |
---|
385 | |
---|
386 | static blockid_t |
---|
387 | disk_add_blocks_internal(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, GArray *data) |
---|
388 | { |
---|
389 | blockid_t head = *headptr, tail = *tailptr, new; |
---|
390 | int tocopy; |
---|
391 | struct _tailblock *tailblock; |
---|
392 | struct _block *block, *newblock; |
---|
393 | int space, copied = 0, left; |
---|
394 | |
---|
395 | /* assumes this funciton is in control of any tail creation */ |
---|
396 | ibex_block_cache_assert(store->blocks, tail == 0); |
---|
397 | |
---|
398 | d(printf("Adding %d items to block list\n", data->len)); |
---|
399 | |
---|
400 | if (head == 0) { |
---|
401 | head = ibex_block_get(store->blocks); |
---|
402 | d(printf("allocating new head %d\n", head)); |
---|
403 | } |
---|
404 | block = ibex_block_read(store->blocks, head); |
---|
405 | |
---|
406 | /* ensure the first block is full before proceeding */ |
---|
407 | space = sizeof(block->bl_data)/sizeof(block->bl_data[0]) - block->used; |
---|
408 | if (space) { |
---|
409 | tocopy = MIN(data->len, space); |
---|
410 | memcpy(block->bl_data+block->used, &g_array_index(data, blockid_t, copied), tocopy*sizeof(blockid_t)); |
---|
411 | block->used += tocopy; |
---|
412 | ibex_block_dirty(block); |
---|
413 | copied = tocopy; |
---|
414 | d(printf("copied %d items to left over last node\n", tocopy)); |
---|
415 | } |
---|
416 | |
---|
417 | while (copied < data->len) { |
---|
418 | left = data->len - copied; |
---|
419 | /* do we drop the rest in a tail? */ |
---|
420 | if (left < TAIL_THRESHOLD) { |
---|
421 | d(printf("Storing remaining %d items in tail\n", left)); |
---|
422 | tocopy = left; |
---|
423 | new = tail_get(store->blocks, tocopy); |
---|
424 | tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(new)); |
---|
425 | memcpy(&tailblock->tb_data[tailblock->tb_offset[TAIL_INDEX(new)]], |
---|
426 | &g_array_index(data, blockid_t, copied), tocopy*sizeof(blockid_t)); |
---|
427 | *tailptr = new; |
---|
428 | } else { |
---|
429 | new = ibex_block_get(store->blocks); |
---|
430 | newblock = (struct _block *)ibex_block_read(store->blocks, new); |
---|
431 | newblock->next = block_number(head); |
---|
432 | tocopy = MIN(left, sizeof(block->bl_data)/sizeof(block->bl_data[0])); |
---|
433 | d(printf("storing %d items in own block\n", tocopy)); |
---|
434 | memcpy(newblock->bl_data, &g_array_index(data, blockid_t, copied), tocopy*sizeof(blockid_t)); |
---|
435 | newblock->used = tocopy; |
---|
436 | block = newblock; |
---|
437 | head = new; |
---|
438 | ibex_block_dirty(newblock); |
---|
439 | } |
---|
440 | copied += tocopy; |
---|
441 | } |
---|
442 | |
---|
443 | *headptr = head; |
---|
444 | return head; |
---|
445 | } |
---|
446 | /* |
---|
447 | case 1: |
---|
448 | no head, no tail, adding a lot of data |
---|
449 | add blocks until the last, which goes in a tail node |
---|
450 | case 2: |
---|
451 | no head, no tail, adding a little data |
---|
452 | just add a tail node |
---|
453 | case 3: |
---|
454 | no head, tail, total exceeds a block |
---|
455 | merge as much as possible into new full blocks, then the remainder in the tail |
---|
456 | case 4: |
---|
457 | no head, tail, room in existing tail for data |
---|
458 | add new data to tail node |
---|
459 | case 5: |
---|
460 | no head, tail, no room in existing tail for data |
---|
461 | add a new tail node, copy data across, free old tail node |
---|
462 | */ |
---|
463 | |
---|
464 | static blockid_t |
---|
465 | disk_add_list(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, GArray *data) |
---|
466 | { |
---|
467 | blockid_t new, head = *headptr, tail = *tailptr, *start; |
---|
468 | struct _tailblock *tailblock, *tailnew; |
---|
469 | int len; |
---|
470 | GArray *tmpdata = NULL; |
---|
471 | |
---|
472 | d(printf("adding %d items head=%d tail=%d\n", data->len, head, tail)); |
---|
473 | if (data->len == 0) |
---|
474 | return head; |
---|
475 | |
---|
476 | /* store length=1 data in the tail pointer */ |
---|
477 | if (head == 0 && tail == 0 && data->len == 1) { |
---|
478 | *headptr = BLOCK_ONE; |
---|
479 | *tailptr = g_array_index(data, blockid_t, 0); |
---|
480 | return BLOCK_ONE; |
---|
481 | } |
---|
482 | /* if we got length=1 data to append to, upgrade it to a real block list */ |
---|
483 | if (head == BLOCK_ONE) { |
---|
484 | tmpdata = g_array_new(0, 0, sizeof(blockid_t)); |
---|
485 | g_array_append_vals(tmpdata, data->data, data->len); |
---|
486 | g_array_append_val(tmpdata, tail); |
---|
487 | head = *headptr = 0; |
---|
488 | tail = *tailptr = 0; |
---|
489 | } |
---|
490 | |
---|
491 | /* if we have no head, then check the tail */ |
---|
492 | if (head == 0) { |
---|
493 | if (tail == 0) { |
---|
494 | if (data->len >= TAIL_THRESHOLD) { |
---|
495 | /* add normally */ |
---|
496 | head = disk_add_blocks_internal(store, headptr, tailptr, data); |
---|
497 | } else { |
---|
498 | /* else add to a tail block */ |
---|
499 | new = tail_get(store->blocks, data->len); |
---|
500 | d(printf("adding %d items to a tail block %d\n", data->len, new)); |
---|
501 | tailnew = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(new)); |
---|
502 | memcpy(&tailnew->tb_data[tailnew->tb_offset[TAIL_INDEX(new)]], |
---|
503 | data->data, data->len*sizeof(blockid_t)); |
---|
504 | *tailptr = new; |
---|
505 | ibex_block_dirty((struct _block *)tailnew); |
---|
506 | } |
---|
507 | } else { |
---|
508 | tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail)); |
---|
509 | len = tail_info(store->blocks, tailblock, tail, &start); |
---|
510 | /* case 3 */ |
---|
511 | if (len + data->len >= TAIL_THRESHOLD) { |
---|
512 | /* this is suboptimal, but should work - merge the tail data with |
---|
513 | our new data, and write it out */ |
---|
514 | if (tmpdata == NULL) { |
---|
515 | tmpdata = g_array_new(0, 0, sizeof(blockid_t)); |
---|
516 | g_array_append_vals(tmpdata, data->data, data->len); |
---|
517 | } |
---|
518 | g_array_append_vals(tmpdata, start, len); |
---|
519 | *tailptr = 0; |
---|
520 | tail_free(store->blocks, tail); |
---|
521 | head = disk_add_blocks_internal(store, headptr, tailptr, tmpdata); |
---|
522 | } else if (tail_space(tailblock) >= data->len) { |
---|
523 | /* can we just expand this in our node, or do we need a new one? */ |
---|
524 | tail_compress(store->blocks, tailblock, TAIL_INDEX(tail), data->len + len); |
---|
525 | memcpy(&tailblock->tb_data[tailblock->tb_offset[TAIL_INDEX(tail)] + len], |
---|
526 | data->data, data->len * sizeof(blockid_t)); |
---|
527 | ibex_block_dirty((struct _block *)tailblock); |
---|
528 | } else { |
---|
529 | /* we need to allocate a new tail node */ |
---|
530 | new = tail_get(store->blocks, data->len + len); |
---|
531 | /* and copy the data across */ |
---|
532 | tailnew = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(new)); |
---|
533 | memcpy(&tailnew->tb_data[tailnew->tb_offset[TAIL_INDEX(new)]], |
---|
534 | start, len*sizeof(blockid_t)); |
---|
535 | memcpy(&tailnew->tb_data[tailnew->tb_offset[TAIL_INDEX(new)]+len], |
---|
536 | data->data, data->len*sizeof(blockid_t)); |
---|
537 | tail_free(store->blocks, tail); |
---|
538 | ibex_block_dirty((struct _block *)tailnew); |
---|
539 | *tailptr = new; |
---|
540 | } |
---|
541 | } |
---|
542 | } else { |
---|
543 | if (tail) { |
---|
544 | /* read/merge the tail with the new data, rewrite out. |
---|
545 | suboptimal, but it should be 'ok' ? */ |
---|
546 | tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail)); |
---|
547 | len = tail_info(store->blocks, tailblock, tail, &start); |
---|
548 | tmpdata = g_array_new(0, 0, sizeof(blockid_t)); |
---|
549 | g_array_append_vals(tmpdata, start, len); |
---|
550 | g_array_append_vals(tmpdata, data->data, data->len); |
---|
551 | *tailptr = 0; |
---|
552 | tail_free(store->blocks, tail); |
---|
553 | head = disk_add_blocks_internal(store, headptr, tailptr, tmpdata); |
---|
554 | } else { |
---|
555 | head = disk_add_blocks_internal(store, headptr, tailptr, data); |
---|
556 | } |
---|
557 | } |
---|
558 | if (tmpdata) |
---|
559 | g_array_free(tmpdata, TRUE); |
---|
560 | |
---|
561 | return head; |
---|
562 | } |
---|
563 | |
---|
564 | static blockid_t |
---|
565 | disk_remove(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, nameid_t data) |
---|
566 | { |
---|
567 | blockid_t head = *headptr, node = *headptr, tail = *tailptr; |
---|
568 | int i; |
---|
569 | |
---|
570 | /* special case for 1-item nodes */ |
---|
571 | if (head == BLOCK_ONE) { |
---|
572 | if (tail == data) { |
---|
573 | *tailptr = 0; |
---|
574 | *headptr = 0; |
---|
575 | head = 0; |
---|
576 | } |
---|
577 | return head; |
---|
578 | } |
---|
579 | |
---|
580 | d(printf("removing %d from %d\n", data, head)); |
---|
581 | while (node) { |
---|
582 | struct _block *block = ibex_block_read(store->blocks, node); |
---|
583 | |
---|
584 | for (i=0;i<block->used;i++) { |
---|
585 | if (block->bl_data[i] == data) { |
---|
586 | struct _block *start = ibex_block_read(store->blocks, head); |
---|
587 | |
---|
588 | d(printf("removing data from block %d\n ", head)); |
---|
589 | |
---|
590 | start->used--; |
---|
591 | block->bl_data[i] = start->bl_data[start->used]; |
---|
592 | if (start->used == 0) { |
---|
593 | blockid_t new; |
---|
594 | |
---|
595 | d(printf("dropping block %d, new head = %d\n", head, start->next)); |
---|
596 | new = block_location(start->next); |
---|
597 | start->next = block_number(store->blocks->root.free); |
---|
598 | store->blocks->root.free = head; |
---|
599 | head = new; |
---|
600 | } |
---|
601 | ibex_block_dirty(block); |
---|
602 | ibex_block_dirty(start); |
---|
603 | *headptr = head; |
---|
604 | return head; |
---|
605 | } |
---|
606 | } |
---|
607 | node = block_location(block->next); |
---|
608 | } |
---|
609 | |
---|
610 | if (tail) { |
---|
611 | struct _tailblock *tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail)); |
---|
612 | int len; |
---|
613 | blockid_t *start; |
---|
614 | |
---|
615 | len = tail_info(store->blocks, tailblock, tail, &start); |
---|
616 | for (i=0;i<len;i++) { |
---|
617 | if (start[i] == data) { |
---|
618 | for (;i<len-1;i++) |
---|
619 | start[i] = start[i+1]; |
---|
620 | if (len == 1) |
---|
621 | *tailptr = 0; |
---|
622 | if (len == 1 && (tailblock->used-1) == TAIL_INDEX(tail)) { |
---|
623 | d(printf("dropping/unlinking tailblock %d (%d) used = %d\n", |
---|
624 | TAIL_BLOCK(tail), tail, tailblock->used)); |
---|
625 | tailblock->used--; |
---|
626 | /* drop/unlink block? */ |
---|
627 | ibex_block_dirty((struct _block *)tailblock); |
---|
628 | *tailptr = 0; |
---|
629 | } else { |
---|
630 | tail_compress(store->blocks, tailblock, TAIL_INDEX(tail), len-1); |
---|
631 | ibex_block_dirty((struct _block *)tailblock); |
---|
632 | } |
---|
633 | } |
---|
634 | } |
---|
635 | |
---|
636 | } |
---|
637 | |
---|
638 | return head; |
---|
639 | } |
---|
640 | |
---|
641 | static void disk_free(struct _IBEXStore *store, blockid_t head, blockid_t tail) |
---|
642 | { |
---|
643 | blockid_t next; |
---|
644 | struct _block *block; |
---|
645 | |
---|
646 | if (head == BLOCK_ONE) |
---|
647 | return; |
---|
648 | |
---|
649 | while (head) { |
---|
650 | d(printf("freeing block %d\n", head)); |
---|
651 | block = ibex_block_read(store->blocks, head); |
---|
652 | next = block_location(block->next); |
---|
653 | ibex_block_free(store->blocks, head); |
---|
654 | head = next; |
---|
655 | } |
---|
656 | if (tail) { |
---|
657 | struct _tailblock *tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail)); |
---|
658 | d(printf("freeing tail block %d (%d)\n", TAIL_BLOCK(tail), tail)); |
---|
659 | tail_compress(store->blocks, tailblock, TAIL_INDEX(tail), 0); |
---|
660 | ibex_block_dirty((struct _block *)tailblock); |
---|
661 | } |
---|
662 | } |
---|
663 | |
---|
664 | static gboolean |
---|
665 | disk_find(struct _IBEXStore *store, blockid_t head, blockid_t tail, nameid_t data) |
---|
666 | { |
---|
667 | blockid_t node = head; |
---|
668 | int i; |
---|
669 | |
---|
670 | d(printf("finding %d from %d\n", data, head)); |
---|
671 | |
---|
672 | if (head == BLOCK_ONE) |
---|
673 | return data == tail; |
---|
674 | |
---|
675 | /* first check full blocks */ |
---|
676 | while (node) { |
---|
677 | struct _block *block = ibex_block_read(store->blocks, node); |
---|
678 | |
---|
679 | for (i=0;i<block->used;i++) { |
---|
680 | if (block->bl_data[i] == data) { |
---|
681 | return TRUE; |
---|
682 | } |
---|
683 | } |
---|
684 | node = block_location(block->next); |
---|
685 | } |
---|
686 | |
---|
687 | /* then check tail */ |
---|
688 | if (tail) { |
---|
689 | struct _tailblock *tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail)); |
---|
690 | int len; |
---|
691 | blockid_t *start; |
---|
692 | |
---|
693 | len = tail_info(store->blocks, tailblock, tail, &start); |
---|
694 | for (i=0;i<len;i++) { |
---|
695 | if (start[i] == data) |
---|
696 | return TRUE; |
---|
697 | } |
---|
698 | } |
---|
699 | |
---|
700 | return FALSE; |
---|
701 | } |
---|
702 | |
---|
703 | static GArray * |
---|
704 | disk_get(struct _IBEXStore *store, blockid_t head, blockid_t tail) |
---|
705 | { |
---|
706 | GArray *result = g_array_new(0, 0, sizeof(nameid_t)); |
---|
707 | |
---|
708 | if (head == BLOCK_ONE) { |
---|
709 | g_array_append_val(result, tail); |
---|
710 | return result; |
---|
711 | } |
---|
712 | |
---|
713 | while (head) { |
---|
714 | struct _block *block = ibex_block_read(store->blocks, head); |
---|
715 | |
---|
716 | d(printf("getting data from block %d\n", head)); |
---|
717 | |
---|
718 | g_array_append_vals(result, block->bl_data, block->used); |
---|
719 | head = block_location(block->next); |
---|
720 | d(printf("next = %d\n", head)); |
---|
721 | } |
---|
722 | |
---|
723 | /* chuck on any tail data as well */ |
---|
724 | if (tail) { |
---|
725 | struct _tailblock *tailblock; |
---|
726 | int len; |
---|
727 | blockid_t *start; |
---|
728 | |
---|
729 | tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail)); |
---|
730 | len = tail_info(store->blocks, tailblock, tail, &start); |
---|
731 | g_array_append_vals(result, start, len); |
---|
732 | } |
---|
733 | return result; |
---|
734 | } |
---|
735 | |
---|
736 | void ibex_diskarray_dump(struct _memcache *blocks, blockid_t head, blockid_t tail); |
---|
737 | |
---|
738 | void |
---|
739 | ibex_diskarray_dump(struct _memcache *blocks, blockid_t head, blockid_t tail) |
---|
740 | { |
---|
741 | blockid_t node = head; |
---|
742 | |
---|
743 | printf("dumping list %d tail %d\n", node, tail); |
---|
744 | if (head == BLOCK_ONE) { |
---|
745 | printf(" 1 length index: %d\n", tail); |
---|
746 | return; |
---|
747 | } |
---|
748 | |
---|
749 | while (node) { |
---|
750 | struct _block *block = ibex_block_read(blocks, node); |
---|
751 | int i; |
---|
752 | |
---|
753 | printf(" block %d used %d\n ", node, block->used); |
---|
754 | for (i=0;i<block->used;i++) { |
---|
755 | printf(" %d", block->bl_data[i]); |
---|
756 | } |
---|
757 | printf("\n"); |
---|
758 | node = block_location(block->next); |
---|
759 | } |
---|
760 | |
---|
761 | printf("tail: "); |
---|
762 | if (tail) { |
---|
763 | struct _tailblock *tailblock; |
---|
764 | int len; |
---|
765 | blockid_t *start; |
---|
766 | int i; |
---|
767 | |
---|
768 | tailblock = (struct _tailblock *)ibex_block_read(blocks, TAIL_BLOCK(tail)); |
---|
769 | len = tail_info(blocks, tailblock, tail, &start); |
---|
770 | for (i=0;i<len;i++) |
---|
771 | printf(" %d", start[i]); |
---|
772 | } |
---|
773 | printf("\n"); |
---|
774 | } |
---|
775 | |
---|
776 | #if 0 |
---|
777 | int main(int argc, char **argv) |
---|
778 | { |
---|
779 | struct _memcache *bc; |
---|
780 | struct _IBEXStore *disk; |
---|
781 | int i; |
---|
782 | blockid_t data[100]; |
---|
783 | GArray adata; |
---|
784 | blockid_t head=0, tail=0; |
---|
785 | |
---|
786 | for (i=0;i<100;i++) { |
---|
787 | data[i] = i; |
---|
788 | } |
---|
789 | |
---|
790 | bc = ibex_block_cache_open("index.db", O_CREAT|O_RDWR, 0600); |
---|
791 | disk = ibex_diskarray_class.create(bc); |
---|
792 | |
---|
793 | head = 0; |
---|
794 | tail = 0; |
---|
795 | adata.data = data; |
---|
796 | adata.len = 70; |
---|
797 | for (i=0;i<100;i++) { |
---|
798 | printf("Loop %d\n", i); |
---|
799 | ibex_diskarray_class.add_list(disk, &head, &tail, &adata); |
---|
800 | ibex_diskarray_dump(bc, head, tail); |
---|
801 | } |
---|
802 | |
---|
803 | #if 0 |
---|
804 | for (i=1;i<100;i++) { |
---|
805 | printf("inserting %d items\n", i); |
---|
806 | adata.data = data; |
---|
807 | adata.len = i; |
---|
808 | head=0; |
---|
809 | tail=0; |
---|
810 | ibex_diskarray_class.add_list(disk, &head, &tail, &adata); |
---|
811 | ibex_diskarray_dump(bc, head, tail); |
---|
812 | } |
---|
813 | #endif |
---|
814 | ibex_diskarray_class.close(disk); |
---|
815 | ibex_block_cache_close(bc); |
---|
816 | return 0; |
---|
817 | } |
---|
818 | |
---|
819 | #endif |
---|