Back again...
[idzebra-moved-to-github.git] / isam / physical.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: physical.c,v $
7  * Revision 1.2  1994-09-26 17:06:36  quinn
8  * Back again...
9  *
10  * Revision 1.1  1994/09/26  16:07:57  quinn
11  * Most of the functionality in place.
12  *
13  */
14
15 /*
16  * This module handles the representation of tables in the bfiles.
17  */
18
19 #include <assert.h>
20
21 #include <isam.h>
22 #include <ismemory.h>
23
24 static int is_freestore_alloc(ISAM is, int type)
25 {
26     int tmp;
27
28     if (is->types[type].freelist >= 0)
29     {
30         tmp = is->types[type].freelist;
31         if (bf_read(is->types[type].bf, tmp, 0, sizeof(tmp),
32             &is->types[type].freelist) <=0)
33         {
34             log(LOG_FATAL, "Failed to allocate block");
35             exit(1);
36         }
37     }
38     else
39         tmp = is->types[type].top++;
40
41     return tmp;
42 }
43
44 static void is_freestore_free(ISAM is, int type, int block)
45 {
46     int tmp;
47
48     tmp = is->types[type].freelist;
49     is->types[type].freelist = block;
50     if (bf_write(is->types[type].bf, block, 0, sizeof(tmp), &tmp) < 0)
51     {
52         log(LOG_FATAL, "Failed to deallocate block.");
53         exit(1);
54     }
55 }
56
57 /* this code must be modified to handle an index */
58 int is_p_read_partial(is_mtable *tab, is_mblock *block)
59 {
60     int toread;
61     is_mbuf *buf;
62
63     assert(block->state == IS_MBSTATE_UNREAD);
64     block->data = buf =  xmalloc_mbuf(IS_MBUF_TYPE_LARGE);
65     toread = tab->is->types[tab->pos_type].blocksize;
66     if (toread > is_mbuf_size[buf->type])
67     {
68         toread = is_mbuf_size[buf->type];
69         block->state = IS_MBSTATE_PARTIAL;
70     }
71     else
72         block->state = IS_MBSTATE_CLEAN;
73     if (bf_read(tab->is->types[tab->pos_type].bf, block->diskpos, 0, toread,
74         buf->data) < 0)
75     {
76         log(LOG_FATAL, "bfread failed.");
77         return -1;
78     }
79     /* extract header info */
80     buf->offset = 0;
81     memcpy(&block->num_records, buf->data, sizeof(block->num_records));
82     buf->offset += sizeof(block->num_records);
83     memcpy(&block->nextpos, buf->data + buf->offset,
84         sizeof(block->nextpos));
85     buf->offset += sizeof(block->nextpos);
86     if (block == tab->data) /* first block */
87     {
88         memcpy(&tab->num_records, buf->data + buf->offset,
89             sizeof(tab->num_records));
90         buf->offset +=sizeof(tab->num_records);
91     }
92     buf->num = (toread - buf->offset) / is_keysize(tab->is);
93     if (buf->num >= block->num_records)
94     {
95         buf->num = block->num_records;
96         block->state = IS_MBSTATE_CLEAN;
97     }
98     else
99         block->bread = buf->num * is_keysize(tab->is);
100     return 0;
101 }
102
103 int is_p_read_full(is_mtable *tab, is_mblock *block)
104 {
105     is_mbuf *buf;
106     int dread, toread;
107
108     if (block->state == IS_MBSTATE_UNREAD && is_p_read_partial(tab, block) < 0)
109     {
110         log(LOG_FATAL, "partial read failed.");
111         return -1;
112     }
113     if (block->state == IS_MBSTATE_PARTIAL)
114     {
115         buf = block->data;
116         dread = block->data->num;
117         while (dread < block->num_records)
118         {
119             buf->next = xmalloc_mbuf(IS_MBUF_TYPE_LARGE);
120             buf = buf->next;
121
122             toread = is_mbuf_size[buf->type] / is_keysize(tab->is);
123             if (toread > block->num_records - dread)
124                 toread = block->num_records - dread;
125
126             if (bf_read(tab->is->types[tab->pos_type].bf, block->diskpos, block->bread, toread *
127                 is_keysize(tab->is), buf->data) < 0)
128             {
129                 log(LOG_FATAL, "bfread failed.");
130                 return -1;
131             }
132             buf->offset = 0;
133             buf->num = toread;
134             dread += toread;
135             block->bread += toread * is_keysize(tab->is);
136         }
137     }
138     return 0;
139 }
140
141 /*
142  * write dirty blocks to bfile.
143  * Allocate blocks as necessary.
144  */
145 void is_p_sync(is_mtable *tab)
146 {
147     is_mblock *p;
148     is_mbuf *b;
149     int sum, v;
150     isam_blocktype *type;
151
152     type = &tab->is->types[tab->pos_type];
153     for (p = tab->data; p; p = p->next)
154     {
155         /* make sure that blocks are allocated. */
156         if (p->diskpos < 0)
157             p->diskpos = is_freestore_alloc(tab->is, tab->pos_type);
158         if (p->next)
159         {
160             if (p->next->diskpos < 0)
161                 p->nextpos = p->next->diskpos = is_freestore_alloc(tab->is,
162                     tab->pos_type);
163             else
164                 p->nextpos = p->next->diskpos;
165         }
166         sum = 0;
167         memcpy(type->dbuf, &p->num_records, sizeof(p->num_records));
168         sum += sizeof(p->num_records);
169         memcpy(type->dbuf + sum, &p->nextpos, sizeof(p->nextpos));
170         sum += sizeof(p->nextpos);
171         if (p == tab->data)  /* first block */
172         {
173             memcpy(type->dbuf + sum, &tab->num_records,
174                 sizeof(tab->num_records));
175             sum += sizeof(tab->num_records);
176         }
177         for (b = p->data; b; b = b->next)
178         {
179             memcpy(type->dbuf + sum, b->data + b->offset, v = b->num *
180                 is_keysize(tab->is));
181             sum += v;
182             assert(sum <= type->blocksize);
183         }
184         if (bf_write(type->bf, p->diskpos, 0, sum, type->dbuf) < 0)
185         {
186             log(LOG_FATAL, "Failed to write block.");
187             exit(1);
188         }
189     }
190 }
191
192 /*
193  * Free all disk blocks associated with table.
194  */
195 void is_p_unmap(is_mtable *tab)
196 {
197     is_mblock *p;
198
199     for (p = tab->data; p; p = p->next)
200         if (p->diskpos >= 0)
201         {
202             is_freestore_free(tab->is, tab->pos_type, p->diskpos);
203             p->diskpos = -1;
204         }
205 }
206
207 static is_mbuf *mbuf_takehead(is_mbuf **mb, int *num, int keysize)
208 {
209     is_mbuf *p = 0, **pp = &p, *new;
210     int toget = *num;
211
212     while (*mb && toget >= (*mb)->num)
213     {
214         toget -= (*mb)->num;
215         *pp = *mb;
216         *mb = (*mb)->next;
217         (*pp)->next = 0;
218         pp = &(*pp)->next;
219     }
220     if (toget > 0 && *mb)
221     {
222         new = xmalloc_mbuf(IS_MBUF_TYPE_SMALL);
223         new->next = (*mb)->next;
224         (*mb)->next = new;
225         new->data = (*mb)->data;
226         (*mb)->refcount++;
227         new->offset = (*mb)->offset + toget * keysize;
228         new->num = (*mb)->num - toget;
229         (*mb)->num = toget;
230         *pp = *mb;
231         *mb = (*mb)->next;
232         (*pp)->next = 0;
233         toget = 0;
234     }
235     *num -= toget;
236     return p;
237 }
238
239 /*
240  * Split up individual blocks which have grown too large.
241  * is_p_align and is_p_remap are alternative functions which trade off
242  * speed in updating versus optimum usage of disk blocks.
243  */
244 void is_p_align(is_mtable *tab)
245 {
246     is_mblock *mblock, *new;
247     is_mbuf *mbufs, *mbp;
248     int blocks, recsblock;
249
250     log(LOG_DEBUG, "Realigning table.");
251     for (mblock = tab->data; mblock; mblock = mblock->next)
252     {
253         if (mblock->state == IS_MBSTATE_DIRTY && mblock->num_records >
254             (mblock == tab->data ?
255             tab->is->types[tab->pos_type].max_keys_block0 :
256             tab->is->types[tab->pos_type].max_keys_block))
257         {
258             blocks = tab->num_records /
259             tab->is->types[tab->pos_type].nice_keys_block;
260             if (tab->num_records %
261                 tab->is->types[tab->pos_type].nice_keys_block)
262                 blocks++;
263             recsblock = tab->num_records / blocks;
264             if (recsblock < 1)
265                 recsblock = 1;
266             mbufs = mblock->data;
267             while ((mbp = mbuf_takehead(&mbufs, &recsblock,
268                 is_keysize(tab->is))))
269             {
270                 new = xmalloc_mblock();
271                 new->diskpos = -1;
272                 new->state = IS_MBSTATE_DIRTY;
273                 new->next = mblock->next;
274                 mblock->next = new;
275                 mblock->data = mbp;
276                 mblock->num_records = recsblock;
277                 mblock = mblock->next;
278             }
279         }
280     }
281 }
282
283 /*
284  * Reorganize data in blocks for minimum block usage and quick access.
285  * Free surplus blocks.
286  * is_p_align and is_p_remap are alternative functions which trade off
287  * speed in updating versus optimum usage of disk blocks.
288  */
289 void is_p_remap(is_mtable *tab)
290 {
291     is_mbuf *mbufs, **bufpp, *mbp;
292     is_mblock *blockp, **blockpp;
293     int recsblock, blocks;
294
295     log(LOG_DEBUG, "Remapping table.");
296     /* collect all data */
297     bufpp = &mbufs;
298     for (blockp = tab->data; blockp; blockp = blockp->next)
299     {
300         *bufpp = blockp->data;
301         while (*bufpp)
302             bufpp = &(*bufpp)->next;
303         blockp->data = 0;
304     }
305     blocks = tab->num_records / tab->is->types[tab->pos_type].nice_keys_block;
306     if (tab->num_records % tab->is->types[tab->pos_type].nice_keys_block)
307         blocks++;
308     recsblock = tab->num_records / blocks;
309     if (recsblock < 1)
310         recsblock = 1;
311     blockpp = &tab->data;
312     while ((mbp = mbuf_takehead(&mbufs, &recsblock, is_keysize(tab->is))))
313     {
314         if (!*blockpp)
315         {
316             *blockpp = xmalloc_mblock();
317             (*blockpp)->diskpos = -1;
318         }
319         (*blockpp)->data = mbp;
320         (*blockpp)->num_records = recsblock;
321         (*blockpp)->state = IS_MBSTATE_DIRTY;
322         blockpp = &(*blockpp)->next;
323     }
324 }