820 lines
23 KiB
C
820 lines
23 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 2000 Ximian, Inc.
|
|
*
|
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/* a disk based array storage class that stores the tails of data lists
|
|
in common blocks */
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include "block.h"
|
|
#include "index.h"
|
|
|
|
#define d(x)
|
|
/*#define DEBUG*/
|
|
|
|
/* marker to define which root keys indicate a single length key */
|
|
#define BLOCK_ONE (1<<BLOCK_BITS)
|
|
|
|
/* tail blocks only contain tail data ... */
|
|
/* and we pack it in, similar to the key data, only more compact */
|
|
struct _tailblock {
|
|
unsigned int next:32-BLOCK_BITS; /* only needs to point to block numbers */
|
|
unsigned int used:BLOCK_BITS; /* how many entries are used */
|
|
union {
|
|
unsigned char offset[BLOCK_SIZE-4]; /* works upto blocksize of 1024 bytes */
|
|
nameid_t data[(BLOCK_SIZE-4)/4];
|
|
} tailblock_u;
|
|
};
|
|
#define tb_offset tailblock_u.offset
|
|
#define tb_data tailblock_u.data
|
|
|
|
/* map a tail index to a block index */
|
|
#define TAIL_INDEX(b) ((b) & (BLOCK_SIZE-1))
|
|
/* map a tail index to a block number */
|
|
#define TAIL_BLOCK(b) ((b) & ~(BLOCK_SIZE-1))
|
|
/* map a block + index to a tailid */
|
|
#define TAIL_KEY(b, i) (((b) & ~(BLOCK_SIZE-1)) | ((i) & (BLOCK_SIZE-1)))
|
|
|
|
#define TAIL_THRESHOLD ((BLOCK_SIZE-8)/6)
|
|
|
|
static struct _IBEXStore *disk_create(struct _memcache *bc);
|
|
static int disk_sync(struct _IBEXStore *store);
|
|
static int disk_close(struct _IBEXStore *store);
|
|
|
|
static blockid_t disk_add(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, nameid_t data);
|
|
static blockid_t disk_add_list(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, GArray *data);
|
|
static blockid_t disk_remove(struct _IBEXStore *store, blockid_t *head, blockid_t *tail, nameid_t data);
|
|
static void disk_free(struct _IBEXStore *store, blockid_t head, blockid_t tail);
|
|
|
|
static gboolean disk_find(struct _IBEXStore *store, blockid_t head, blockid_t tail, nameid_t data);
|
|
static GArray *disk_get(struct _IBEXStore *store, blockid_t head, blockid_t tail);
|
|
|
|
struct _IBEXStoreClass ibex_diskarray_class = {
|
|
disk_create, disk_sync, disk_close,
|
|
disk_add, disk_add_list,
|
|
disk_remove, disk_free,
|
|
disk_find, disk_get
|
|
};
|
|
|
|
|
|
static int
|
|
tail_info(struct _memcache *blocks, struct _tailblock *bucket, nameid_t tailid, blockid_t **startptr)
|
|
{
|
|
blockid_t *start, *end;
|
|
int index;
|
|
|
|
/* get start/end of area to zap */
|
|
index = TAIL_INDEX(tailid);
|
|
start = &bucket->tb_data[bucket->tb_offset[index]];
|
|
if (index == 0) {
|
|
end = &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])];
|
|
} else {
|
|
end = &bucket->tb_data[bucket->tb_offset[index-1]];
|
|
}
|
|
if (startptr)
|
|
*startptr = start;
|
|
|
|
ibex_block_cache_assert(blocks, end >= start);
|
|
|
|
return end-start;
|
|
}
|
|
|
|
/* compresses (or expand) the bucket entry, to the new size */
|
|
static void
|
|
tail_compress(struct _memcache *blocks, struct _tailblock *bucket, int index, int newsize)
|
|
{
|
|
int i;
|
|
blockid_t *start, *end, *newstart;
|
|
|
|
/* get start/end of area to zap */
|
|
start = &bucket->tb_data[bucket->tb_offset[index]];
|
|
if (index == 0) {
|
|
end = &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])];
|
|
} else {
|
|
end = &bucket->tb_data[bucket->tb_offset[index-1]];
|
|
}
|
|
|
|
if (end-start == newsize)
|
|
return;
|
|
|
|
/*
|
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXyyyyy
|
|
0 20 25
|
|
|
|
newsize = 0
|
|
end = 25
|
|
newstart = 0
|
|
start = 20
|
|
|
|
newstart+(end-start)-newsize = 5
|
|
i = start-newstart
|
|
|
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXyyyyy
|
|
0 20 25
|
|
|
|
newsize = 2
|
|
end = 25
|
|
newstart = 0
|
|
start = 20
|
|
|
|
newstart+(end-start)-newsize = 3
|
|
i = start-newstart + MIN(end-start, newsize)) = 22
|
|
|
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
5 25
|
|
newsize = 5
|
|
end = 25
|
|
start = 25
|
|
newstart = 5
|
|
|
|
newstart+(end-start)-newsize = 0
|
|
i = start-newstart = 20
|
|
|
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXyy
|
|
3 23 25
|
|
newsize = 5
|
|
end = 25
|
|
start = 23
|
|
newstart = 3
|
|
|
|
newstart+(end-start)-newsize = 0
|
|
i = start-newstart + MIN(end-start, newsize) = 22
|
|
|
|
*/
|
|
|
|
|
|
/* fixup data */
|
|
newstart = &bucket->tb_data[bucket->tb_offset[bucket->used-1]];
|
|
|
|
ibex_block_cache_assert(blocks, newstart+(end-start)-newsize <= &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])]);
|
|
ibex_block_cache_assert(blocks, newstart + (start-newstart) + MIN(end-start, newsize) <= &bucket->tb_data[sizeof(bucket->tb_data)/sizeof(bucket->tb_data[0])]);
|
|
ibex_block_cache_assert(blocks, newstart+(end-start)-newsize >= (blockid_t *) &bucket->tb_offset[bucket->used]);
|
|
ibex_block_cache_assert(blocks, newstart + (start-newstart) + MIN(end-start, newsize) >= (blockid_t *) &bucket->tb_offset[bucket->used]);
|
|
|
|
memmove(newstart+(end-start)-newsize, newstart, ((start-newstart)+MIN(end-start, newsize)) * sizeof(blockid_t));
|
|
|
|
/* fixup key pointers */
|
|
for (i=index;i<bucket->used;i++) {
|
|
bucket->tb_offset[i] += (end-start)-newsize;
|
|
}
|
|
ibex_block_dirty((struct _block *)bucket);
|
|
}
|
|
|
|
/*
|
|
returns the number of blockid's free
|
|
*/
|
|
static int
|
|
tail_space(struct _tailblock *tail)
|
|
{
|
|
if (tail->used == 0)
|
|
return sizeof(tail->tb_data)/sizeof(tail->tb_data[0])-1;
|
|
|
|
return &tail->tb_data[tail->tb_offset[tail->used-1]]
|
|
- (blockid_t *)&tail->tb_offset[tail->used] - 1;
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
tail_dump(struct _memcache *blocks, blockid_t tailid)
|
|
{
|
|
int i;
|
|
struct _tailblock *tail = (struct _tailblock *)ibex_block_read(blocks, TAIL_BLOCK(tailid));
|
|
|
|
printf("Block %d, used %d\n", tailid, tail->used);
|
|
for (i=0;i<sizeof(struct _tailblock)/sizeof(unsigned int);i++) {
|
|
printf(" %08x", ((unsigned int *)tail)[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
static blockid_t
|
|
tail_get(struct _memcache *blocks, int size)
|
|
{
|
|
blockid_t tailid;
|
|
struct _tailblock *tail;
|
|
int freeindex;
|
|
blockid_t *end;
|
|
int i, count = 0;
|
|
|
|
d(printf("looking for a tail node with %d items in it\n", size));
|
|
|
|
/* look for a node with enough space, if we dont find it fairly
|
|
quickly, just quit. needs a better free algorithm i think ... */
|
|
tailid = blocks->root.tail;
|
|
while (tailid && count<5) {
|
|
int space;
|
|
|
|
d(printf(" checking tail node %d\n", tailid));
|
|
|
|
tail = (struct _tailblock *)ibex_block_read(blocks, tailid);
|
|
|
|
if (tail->used == 0) {
|
|
/* assume its big enough ... */
|
|
tail->used = 1;
|
|
tail->tb_offset[0] = sizeof(tail->tb_data)/sizeof(tail->tb_data[0]) - size;
|
|
d(printf("allocated %d (%d), used %d\n", tailid, tailid, tail->used));
|
|
ibex_block_dirty((struct _block *)tail);
|
|
|
|
ibex_block_cache_assert(blocks, &tail->tb_offset[tail->used-1]
|
|
< (unsigned char *) &tail->tb_data[tail->tb_offset[tail->used-1]]);
|
|
|
|
return tailid;
|
|
}
|
|
|
|
ibex_block_cache_assert(blocks, &tail->tb_offset[tail->used-1]
|
|
< (unsigned char *) &tail->tb_data[tail->tb_offset[tail->used-1]]);
|
|
|
|
/* see if we have a free slot first */
|
|
freeindex = -1;
|
|
end = &tail->tb_data[sizeof(tail->tb_data)/sizeof(tail->tb_data[0])];
|
|
for (i=0;i<tail->used;i++) {
|
|
if (end == &tail->tb_data[tail->tb_offset[i]]) {
|
|
freeindex = i;
|
|
break;
|
|
}
|
|
end = &tail->tb_data[tail->tb_offset[i]];
|
|
}
|
|
|
|
/* determine how much space we have available - incl any extra header we might need */
|
|
space = ((char *)&tail->tb_data[tail->tb_offset[tail->used-1]])
|
|
- ((char *)&tail->tb_offset[tail->used])
|
|
/* FIXMEL work out why this is out a little bit */
|
|
- 8;
|
|
if (freeindex == -1)
|
|
space -= sizeof(tail->tb_offset[0]);
|
|
|
|
/* if we have enough, set it up, creating a new entry if necessary */
|
|
/* for some really odd reason the compiler promotes this expression to unsigned, hence
|
|
the requirement for the space>0 check ... */
|
|
if (space>0 && space > size*sizeof(blockid_t)) {
|
|
d(printf("space = %d, size = %d size*sizeof() = %d truth = %d\n", space, size, size*sizeof(blockid_t), space>size*sizeof(blockid_t)));
|
|
if (freeindex == -1) {
|
|
freeindex = tail->used;
|
|
tail->tb_offset[tail->used] = tail->tb_offset[tail->used-1];
|
|
tail->used++;
|
|
}
|
|
tail_compress(blocks, tail, freeindex, size);
|
|
ibex_block_dirty((struct _block *)tail);
|
|
d(printf("allocated %d (%d), used %d\n", tailid, TAIL_KEY(tailid, freeindex), tail->used));
|
|
return TAIL_KEY(tailid, freeindex);
|
|
}
|
|
count++;
|
|
tailid = block_location(tail->next);
|
|
}
|
|
|
|
d(printf("allocating new data node for tail data\n"));
|
|
tailid = ibex_block_get(blocks);
|
|
tail = (struct _tailblock *)ibex_block_read(blocks, tailid);
|
|
tail->next = block_number(blocks->root.tail);
|
|
blocks->root.tail = tailid;
|
|
tail->used = 1;
|
|
tail->tb_offset[0] = sizeof(tail->tb_data)/sizeof(tail->tb_data[0]) - size;
|
|
ibex_block_dirty((struct _block *)tail);
|
|
d(printf("allocated %d (%d), used %d\n", tailid, TAIL_KEY(tailid, 0), tail->used));
|
|
|
|
g_assert(&tail->tb_offset[tail->used-1]
|
|
< (unsigned char *) &tail->tb_data[tail->tb_offset[tail->used-1]]);
|
|
|
|
return TAIL_KEY(tailid, 0);
|
|
}
|
|
|
|
static void
|
|
tail_free(struct _memcache *blocks, blockid_t tailid)
|
|
{
|
|
struct _tailblock *tail;
|
|
|
|
d(printf("freeing tail id %d\n", tailid));
|
|
|
|
if (tailid == 0)
|
|
return;
|
|
|
|
tail = (struct _tailblock *)ibex_block_read(blocks, TAIL_BLOCK(tailid));
|
|
d(printf(" tail %d used %d\n", tailid, tail->used));
|
|
ibex_block_cache_assert(blocks, tail->used);
|
|
if (TAIL_INDEX(tailid) == tail->used - 1) {
|
|
tail->used --;
|
|
} else {
|
|
tail_compress(blocks, tail, TAIL_INDEX(tailid), 0);
|
|
}
|
|
ibex_block_dirty((struct _block *)tail);
|
|
}
|
|
|
|
static struct _IBEXStore *disk_create(struct _memcache *bc)
|
|
{
|
|
struct _IBEXStore *store;
|
|
|
|
store = g_malloc0(sizeof(*store));
|
|
store->klass = &ibex_diskarray_class;
|
|
store->blocks = bc;
|
|
|
|
return store;
|
|
}
|
|
|
|
static int disk_sync(struct _IBEXStore *store)
|
|
{
|
|
/* no cache, nop */
|
|
return 0;
|
|
}
|
|
|
|
static int disk_close(struct _IBEXStore *store)
|
|
{
|
|
g_free(store);
|
|
return 0;
|
|
}
|
|
|
|
static blockid_t
|
|
disk_add(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, nameid_t data)
|
|
{
|
|
struct _block *block;
|
|
struct _block *newblock;
|
|
blockid_t new, head = *headptr /*, tail = *tailptr*/;
|
|
|
|
g_error("Inbimplemented");
|
|
|
|
if (head == 0) {
|
|
head = ibex_block_get(store->blocks);
|
|
}
|
|
block = ibex_block_read(store->blocks, head);
|
|
|
|
d(printf("adding record %d to block %d (next = %d)\n", data, head, block->next));
|
|
|
|
if (block->used < sizeof(block->bl_data)/sizeof(block->bl_data[0])) {
|
|
(printf("adding record into block %d %d\n", head, data));
|
|
block->bl_data[block->used] = data;
|
|
block->used++;
|
|
ibex_block_dirty(block);
|
|
return head;
|
|
} else {
|
|
new = ibex_block_get(store->blocks);
|
|
newblock = ibex_block_read(store->blocks, new);
|
|
newblock->next = block_number(head);
|
|
newblock->bl_data[0] = data;
|
|
newblock->used = 1;
|
|
d(printf("adding record into new %d %d, next =%d\n", new, data, newblock->next));
|
|
ibex_block_dirty(newblock);
|
|
return new;
|
|
}
|
|
}
|
|
|
|
static blockid_t
|
|
disk_add_blocks_internal(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, GArray *data)
|
|
{
|
|
blockid_t head = *headptr, tail = *tailptr, new;
|
|
int tocopy;
|
|
struct _tailblock *tailblock;
|
|
struct _block *block, *newblock;
|
|
int space, copied = 0, left;
|
|
|
|
/* assumes this funciton is in control of any tail creation */
|
|
ibex_block_cache_assert(store->blocks, tail == 0);
|
|
|
|
d(printf("Adding %d items to block list\n", data->len));
|
|
|
|
if (head == 0) {
|
|
head = ibex_block_get(store->blocks);
|
|
d(printf("allocating new head %d\n", head));
|
|
}
|
|
block = ibex_block_read(store->blocks, head);
|
|
|
|
/* ensure the first block is full before proceeding */
|
|
space = sizeof(block->bl_data)/sizeof(block->bl_data[0]) - block->used;
|
|
if (space) {
|
|
tocopy = MIN(data->len, space);
|
|
memcpy(block->bl_data+block->used, &g_array_index(data, blockid_t, copied), tocopy*sizeof(blockid_t));
|
|
block->used += tocopy;
|
|
ibex_block_dirty(block);
|
|
copied = tocopy;
|
|
d(printf("copied %d items to left over last node\n", tocopy));
|
|
}
|
|
|
|
while (copied < data->len) {
|
|
left = data->len - copied;
|
|
/* do we drop the rest in a tail? */
|
|
if (left < TAIL_THRESHOLD) {
|
|
d(printf("Storing remaining %d items in tail\n", left));
|
|
tocopy = left;
|
|
new = tail_get(store->blocks, tocopy);
|
|
tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(new));
|
|
memcpy(&tailblock->tb_data[tailblock->tb_offset[TAIL_INDEX(new)]],
|
|
&g_array_index(data, blockid_t, copied), tocopy*sizeof(blockid_t));
|
|
*tailptr = new;
|
|
} else {
|
|
new = ibex_block_get(store->blocks);
|
|
newblock = (struct _block *)ibex_block_read(store->blocks, new);
|
|
newblock->next = block_number(head);
|
|
tocopy = MIN(left, sizeof(block->bl_data)/sizeof(block->bl_data[0]));
|
|
d(printf("storing %d items in own block\n", tocopy));
|
|
memcpy(newblock->bl_data, &g_array_index(data, blockid_t, copied), tocopy*sizeof(blockid_t));
|
|
newblock->used = tocopy;
|
|
block = newblock;
|
|
head = new;
|
|
ibex_block_dirty(newblock);
|
|
}
|
|
copied += tocopy;
|
|
}
|
|
|
|
*headptr = head;
|
|
return head;
|
|
}
|
|
/*
|
|
case 1:
|
|
no head, no tail, adding a lot of data
|
|
add blocks until the last, which goes in a tail node
|
|
case 2:
|
|
no head, no tail, adding a little data
|
|
just add a tail node
|
|
case 3:
|
|
no head, tail, total exceeds a block
|
|
merge as much as possible into new full blocks, then the remainder in the tail
|
|
case 4:
|
|
no head, tail, room in existing tail for data
|
|
add new data to tail node
|
|
case 5:
|
|
no head, tail, no room in existing tail for data
|
|
add a new tail node, copy data across, free old tail node
|
|
*/
|
|
|
|
static blockid_t
|
|
disk_add_list(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, GArray *data)
|
|
{
|
|
blockid_t new, head = *headptr, tail = *tailptr, *start;
|
|
struct _tailblock *tailblock, *tailnew;
|
|
int len;
|
|
GArray *tmpdata = NULL;
|
|
|
|
d(printf("adding %d items head=%d tail=%d\n", data->len, head, tail));
|
|
if (data->len == 0)
|
|
return head;
|
|
|
|
/* store length=1 data in the tail pointer */
|
|
if (head == 0 && tail == 0 && data->len == 1) {
|
|
*headptr = BLOCK_ONE;
|
|
*tailptr = g_array_index(data, blockid_t, 0);
|
|
return BLOCK_ONE;
|
|
}
|
|
/* if we got length=1 data to append to, upgrade it to a real block list */
|
|
if (head == BLOCK_ONE) {
|
|
tmpdata = g_array_new(0, 0, sizeof(blockid_t));
|
|
g_array_append_vals(tmpdata, data->data, data->len);
|
|
g_array_append_val(tmpdata, tail);
|
|
head = *headptr = 0;
|
|
tail = *tailptr = 0;
|
|
}
|
|
|
|
/* if we have no head, then check the tail */
|
|
if (head == 0) {
|
|
if (tail == 0) {
|
|
if (data->len >= TAIL_THRESHOLD) {
|
|
/* add normally */
|
|
head = disk_add_blocks_internal(store, headptr, tailptr, data);
|
|
} else {
|
|
/* else add to a tail block */
|
|
new = tail_get(store->blocks, data->len);
|
|
d(printf("adding %d items to a tail block %d\n", data->len, new));
|
|
tailnew = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(new));
|
|
memcpy(&tailnew->tb_data[tailnew->tb_offset[TAIL_INDEX(new)]],
|
|
data->data, data->len*sizeof(blockid_t));
|
|
*tailptr = new;
|
|
ibex_block_dirty((struct _block *)tailnew);
|
|
}
|
|
} else {
|
|
tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail));
|
|
len = tail_info(store->blocks, tailblock, tail, &start);
|
|
/* case 3 */
|
|
if (len + data->len >= TAIL_THRESHOLD) {
|
|
/* this is suboptimal, but should work - merge the tail data with
|
|
our new data, and write it out */
|
|
if (tmpdata == NULL) {
|
|
tmpdata = g_array_new(0, 0, sizeof(blockid_t));
|
|
g_array_append_vals(tmpdata, data->data, data->len);
|
|
}
|
|
g_array_append_vals(tmpdata, start, len);
|
|
*tailptr = 0;
|
|
tail_free(store->blocks, tail);
|
|
head = disk_add_blocks_internal(store, headptr, tailptr, tmpdata);
|
|
} else if (tail_space(tailblock) >= data->len) {
|
|
/* can we just expand this in our node, or do we need a new one? */
|
|
tail_compress(store->blocks, tailblock, TAIL_INDEX(tail), data->len + len);
|
|
memcpy(&tailblock->tb_data[tailblock->tb_offset[TAIL_INDEX(tail)] + len],
|
|
data->data, data->len * sizeof(blockid_t));
|
|
ibex_block_dirty((struct _block *)tailblock);
|
|
} else {
|
|
/* we need to allocate a new tail node */
|
|
new = tail_get(store->blocks, data->len + len);
|
|
/* and copy the data across */
|
|
tailnew = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(new));
|
|
memcpy(&tailnew->tb_data[tailnew->tb_offset[TAIL_INDEX(new)]],
|
|
start, len*sizeof(blockid_t));
|
|
memcpy(&tailnew->tb_data[tailnew->tb_offset[TAIL_INDEX(new)]+len],
|
|
data->data, data->len*sizeof(blockid_t));
|
|
tail_free(store->blocks, tail);
|
|
ibex_block_dirty((struct _block *)tailnew);
|
|
*tailptr = new;
|
|
}
|
|
}
|
|
} else {
|
|
if (tail) {
|
|
/* read/merge the tail with the new data, rewrite out.
|
|
suboptimal, but it should be 'ok' ? */
|
|
tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail));
|
|
len = tail_info(store->blocks, tailblock, tail, &start);
|
|
tmpdata = g_array_new(0, 0, sizeof(blockid_t));
|
|
g_array_append_vals(tmpdata, start, len);
|
|
g_array_append_vals(tmpdata, data->data, data->len);
|
|
*tailptr = 0;
|
|
tail_free(store->blocks, tail);
|
|
head = disk_add_blocks_internal(store, headptr, tailptr, tmpdata);
|
|
} else {
|
|
head = disk_add_blocks_internal(store, headptr, tailptr, data);
|
|
}
|
|
}
|
|
if (tmpdata)
|
|
g_array_free(tmpdata, TRUE);
|
|
|
|
return head;
|
|
}
|
|
|
|
static blockid_t
|
|
disk_remove(struct _IBEXStore *store, blockid_t *headptr, blockid_t *tailptr, nameid_t data)
|
|
{
|
|
blockid_t head = *headptr, node = *headptr, tail = *tailptr;
|
|
int i;
|
|
|
|
/* special case for 1-item nodes */
|
|
if (head == BLOCK_ONE) {
|
|
if (tail == data) {
|
|
*tailptr = 0;
|
|
*headptr = 0;
|
|
head = 0;
|
|
}
|
|
return head;
|
|
}
|
|
|
|
d(printf("removing %d from %d\n", data, head));
|
|
while (node) {
|
|
struct _block *block = ibex_block_read(store->blocks, node);
|
|
|
|
for (i=0;i<block->used;i++) {
|
|
if (block->bl_data[i] == data) {
|
|
struct _block *start = ibex_block_read(store->blocks, head);
|
|
|
|
d(printf("removing data from block %d\n ", head));
|
|
|
|
start->used--;
|
|
block->bl_data[i] = start->bl_data[start->used];
|
|
if (start->used == 0) {
|
|
blockid_t new;
|
|
|
|
d(printf("dropping block %d, new head = %d\n", head, start->next));
|
|
new = block_location(start->next);
|
|
start->next = block_number(store->blocks->root.free);
|
|
store->blocks->root.free = head;
|
|
head = new;
|
|
}
|
|
ibex_block_dirty(block);
|
|
ibex_block_dirty(start);
|
|
*headptr = head;
|
|
return head;
|
|
}
|
|
}
|
|
node = block_location(block->next);
|
|
}
|
|
|
|
if (tail) {
|
|
struct _tailblock *tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail));
|
|
int len;
|
|
blockid_t *start;
|
|
|
|
len = tail_info(store->blocks, tailblock, tail, &start);
|
|
for (i=0;i<len;i++) {
|
|
if (start[i] == data) {
|
|
for (;i<len-1;i++)
|
|
start[i] = start[i+1];
|
|
if (len == 1)
|
|
*tailptr = 0;
|
|
if (len == 1 && (tailblock->used-1) == TAIL_INDEX(tail)) {
|
|
d(printf("dropping/unlinking tailblock %d (%d) used = %d\n",
|
|
TAIL_BLOCK(tail), tail, tailblock->used));
|
|
tailblock->used--;
|
|
/* drop/unlink block? */
|
|
ibex_block_dirty((struct _block *)tailblock);
|
|
*tailptr = 0;
|
|
} else {
|
|
tail_compress(store->blocks, tailblock, TAIL_INDEX(tail), len-1);
|
|
ibex_block_dirty((struct _block *)tailblock);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
static void disk_free(struct _IBEXStore *store, blockid_t head, blockid_t tail)
|
|
{
|
|
blockid_t next;
|
|
struct _block *block;
|
|
|
|
if (head == BLOCK_ONE)
|
|
return;
|
|
|
|
while (head) {
|
|
d(printf("freeing block %d\n", head));
|
|
block = ibex_block_read(store->blocks, head);
|
|
next = block_location(block->next);
|
|
ibex_block_free(store->blocks, head);
|
|
head = next;
|
|
}
|
|
if (tail) {
|
|
struct _tailblock *tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail));
|
|
d(printf("freeing tail block %d (%d)\n", TAIL_BLOCK(tail), tail));
|
|
tail_compress(store->blocks, tailblock, TAIL_INDEX(tail), 0);
|
|
ibex_block_dirty((struct _block *)tailblock);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
disk_find(struct _IBEXStore *store, blockid_t head, blockid_t tail, nameid_t data)
|
|
{
|
|
blockid_t node = head;
|
|
int i;
|
|
|
|
d(printf("finding %d from %d\n", data, head));
|
|
|
|
if (head == BLOCK_ONE)
|
|
return data == tail;
|
|
|
|
/* first check full blocks */
|
|
while (node) {
|
|
struct _block *block = ibex_block_read(store->blocks, node);
|
|
|
|
for (i=0;i<block->used;i++) {
|
|
if (block->bl_data[i] == data) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
node = block_location(block->next);
|
|
}
|
|
|
|
/* then check tail */
|
|
if (tail) {
|
|
struct _tailblock *tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail));
|
|
int len;
|
|
blockid_t *start;
|
|
|
|
len = tail_info(store->blocks, tailblock, tail, &start);
|
|
for (i=0;i<len;i++) {
|
|
if (start[i] == data)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GArray *
|
|
disk_get(struct _IBEXStore *store, blockid_t head, blockid_t tail)
|
|
{
|
|
GArray *result = g_array_new(0, 0, sizeof(nameid_t));
|
|
|
|
if (head == BLOCK_ONE) {
|
|
g_array_append_val(result, tail);
|
|
return result;
|
|
}
|
|
|
|
while (head) {
|
|
struct _block *block = ibex_block_read(store->blocks, head);
|
|
|
|
d(printf("getting data from block %d\n", head));
|
|
|
|
g_array_append_vals(result, block->bl_data, block->used);
|
|
head = block_location(block->next);
|
|
d(printf("next = %d\n", head));
|
|
}
|
|
|
|
/* chuck on any tail data as well */
|
|
if (tail) {
|
|
struct _tailblock *tailblock;
|
|
int len;
|
|
blockid_t *start;
|
|
|
|
tailblock = (struct _tailblock *)ibex_block_read(store->blocks, TAIL_BLOCK(tail));
|
|
len = tail_info(store->blocks, tailblock, tail, &start);
|
|
g_array_append_vals(result, start, len);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ibex_diskarray_dump(struct _memcache *blocks, blockid_t head, blockid_t tail);
|
|
|
|
void
|
|
ibex_diskarray_dump(struct _memcache *blocks, blockid_t head, blockid_t tail)
|
|
{
|
|
blockid_t node = head;
|
|
|
|
printf("dumping list %d tail %d\n", node, tail);
|
|
if (head == BLOCK_ONE) {
|
|
printf(" 1 length index: %d\n", tail);
|
|
return;
|
|
}
|
|
|
|
while (node) {
|
|
struct _block *block = ibex_block_read(blocks, node);
|
|
int i;
|
|
|
|
printf(" block %d used %d\n ", node, block->used);
|
|
for (i=0;i<block->used;i++) {
|
|
printf(" %d", block->bl_data[i]);
|
|
}
|
|
printf("\n");
|
|
node = block_location(block->next);
|
|
}
|
|
|
|
printf("tail: ");
|
|
if (tail) {
|
|
struct _tailblock *tailblock;
|
|
int len;
|
|
blockid_t *start;
|
|
int i;
|
|
|
|
tailblock = (struct _tailblock *)ibex_block_read(blocks, TAIL_BLOCK(tail));
|
|
len = tail_info(blocks, tailblock, tail, &start);
|
|
for (i=0;i<len;i++)
|
|
printf(" %d", start[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
#if 0
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct _memcache *bc;
|
|
struct _IBEXStore *disk;
|
|
int i;
|
|
blockid_t data[100];
|
|
GArray adata;
|
|
blockid_t head=0, tail=0;
|
|
|
|
for (i=0;i<100;i++) {
|
|
data[i] = i;
|
|
}
|
|
|
|
bc = ibex_block_cache_open("index.db", O_CREAT|O_RDWR, 0600);
|
|
disk = ibex_diskarray_class.create(bc);
|
|
|
|
head = 0;
|
|
tail = 0;
|
|
adata.data = data;
|
|
adata.len = 70;
|
|
for (i=0;i<100;i++) {
|
|
printf("Loop %d\n", i);
|
|
ibex_diskarray_class.add_list(disk, &head, &tail, &adata);
|
|
ibex_diskarray_dump(bc, head, tail);
|
|
}
|
|
|
|
#if 0
|
|
for (i=1;i<100;i++) {
|
|
printf("inserting %d items\n", i);
|
|
adata.data = data;
|
|
adata.len = i;
|
|
head=0;
|
|
tail=0;
|
|
ibex_diskarray_class.add_list(disk, &head, &tail, &adata);
|
|
ibex_diskarray_dump(bc, head, tail);
|
|
}
|
|
#endif
|
|
ibex_diskarray_class.close(disk);
|
|
ibex_block_cache_close(bc);
|
|
return 0;
|
|
}
|
|
|
|
#endif
|