source: trunk/third/evolution/libibex/disktail.c @ 16787

Revision 16787, 23.4 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16786, which included commits to RCS files with non-trunk default branches.
Line 
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 */
46struct _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
66static struct _IBEXStore *disk_create(struct _memcache *bc);
67static int disk_sync(struct _IBEXStore *store);
68static int disk_close(struct _IBEXStore *store);
69
70static blockid_t disk_add(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, nameid_t data);
71static blockid_t disk_add_list(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, GArray *data);
72static blockid_t disk_remove(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, nameid_t data);
73static void disk_free(struct _IBEXStore *store, blockid_t head, blockid_t tail);
74
75static gboolean disk_find(struct _IBEXStore *store, blockid_t head, blockid_t tail, nameid_t data);
76static GArray *disk_get(struct _IBEXStore *store, blockid_t head, blockid_t tail);
77
78struct _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
86static int
87tail_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 */
109static void
110tail_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*/
192static int
193tail_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
203static void
204tail_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
217static blockid_t
218tail_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
308static void
309tail_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
329static 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
340static int disk_sync(struct _IBEXStore *store)
341{
342        /* no cache, nop */
343        return 0;
344}
345
346static int disk_close(struct _IBEXStore *store)
347{
348        g_free(store);
349        return 0;
350}
351
352static blockid_t
353disk_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
386static blockid_t
387disk_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
464static blockid_t
465disk_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
564static blockid_t
565disk_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
641static 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
664static gboolean
665disk_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
703static GArray *
704disk_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
736void ibex_diskarray_dump(struct _memcache *blocks, blockid_t head, blockid_t tail);
737
738void
739ibex_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
777int 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
Note: See TracBrowser for help on using the repository browser.