Bug fix: during isc_merge operations, some pages weren't marked dirty
[idzebra-moved-to-github.git] / isamc / isamc.c
1 /*
2  * Copyright (c) 1995-1996, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: isamc.c,v $
7  * Revision 1.7  1997-02-12 20:42:43  adam
8  * Bug fix: during isc_merge operations, some pages weren't marked dirty
9  * even though they should be. At this point the merge operation marks
10  * a page dirty if the previous page changed at all. A better approach is
11  * to mark it dirty if the last key written changed in previous page.
12  *
13  * Revision 1.6  1996/11/08 11:15:29  adam
14  * Number of keys in chain are stored in first block and the function
15  * to retrieve this information, isc_pp_num is implemented.
16  *
17  * Revision 1.5  1996/11/04 14:08:57  adam
18  * Optimized free block usage.
19  *
20  * Revision 1.4  1996/11/01 13:36:46  adam
21  * New element, max_blocks_mem, that control how many blocks of max size
22  * to store in memory during isc_merge.
23  * Function isc_merge now ignores delete/update of identical keys and
24  * the proper blocks are then non-dirty and not written in flush_blocks.
25  *
26  * Revision 1.3  1996/11/01  08:59:14  adam
27  * First version of isc_merge that supports update/delete.
28  *
29  * Revision 1.2  1996/10/29 16:44:56  adam
30  * Work on isc_merge.
31  *
32  * Revision 1.1  1996/10/29  13:40:48  adam
33  * First work.
34  *
35  */
36
37 /* 
38  * TODO:
39  *   Reduction to lower categories in isc_merge
40  */
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <string.h>
44 #include <stdio.h>
45
46 #include <log.h>
47 #include "isamc-p.h"
48
49 static void release_fc (ISAMC is, int cat);
50 static void init_fc (ISAMC is, int cat);
51
52 ISAMC_M isc_getmethod (void)
53 {
54     static struct ISAMC_filecat_s def_cat[] = {
55         {   32,    28,     0,    20 },
56         {  512,   490,   100,    20 },
57         { 4096,  3950,  1000,    20 },
58         {32768, 32000, 10000,     0 },
59     };
60     ISAMC_M m = xmalloc (sizeof(*m));
61     m->filecat = def_cat;
62
63     m->code_start = NULL;
64     m->code_item = NULL;
65     m->code_stop = NULL;
66
67     m->compare_item = NULL;
68
69     m->debug = 1;
70
71     m->max_blocks_mem = 10;
72
73     return m;
74 }
75
76
77 ISAMC isc_open (const char *name, int writeflag, ISAMC_M method)
78 {
79     ISAMC is;
80     ISAMC_filecat filecat;
81     int i = 0;
82     int max_buf_size = 0;
83
84     is = xmalloc (sizeof(*is));
85
86     is->method = xmalloc (sizeof(*is->method));
87     memcpy (is->method, method, sizeof(*method));
88     filecat = is->method->filecat;
89     assert (filecat);
90
91     /* determine number of block categories */
92     if (is->method->debug)
93         logf (LOG_LOG, "isc: bsize  ifill  mfill mblocks");
94     do
95     {
96         if (is->method->debug)
97             logf (LOG_LOG, "isc:%6d %6d %6d %6d",
98                   filecat[i].bsize, filecat[i].ifill, 
99                   filecat[i].mfill, filecat[i].mblocks);
100         if (max_buf_size < filecat[i].mblocks * filecat[i].bsize)
101             max_buf_size = filecat[i].mblocks * filecat[i].bsize;
102     } while (filecat[i++].mblocks);
103     is->no_files = i;
104     is->max_cat = --i;
105     /* max_buf_size is the larget buffer to be used during merge */
106     max_buf_size = (1 + max_buf_size / filecat[i].bsize) * filecat[i].bsize;
107     if (max_buf_size < (1+is->method->max_blocks_mem) * filecat[i].bsize)
108         max_buf_size = (1+is->method->max_blocks_mem) * filecat[i].bsize;
109     if (is->method->debug)
110         logf (LOG_LOG, "isc: max_buf_size %d", max_buf_size);
111     
112     assert (is->no_files > 0);
113     is->files = xmalloc (sizeof(*is->files)*is->no_files);
114     if (writeflag)
115     {
116         is->merge_buf = xmalloc (max_buf_size+256);
117         memset (is->merge_buf, 0, max_buf_size+256);
118     }
119     else
120         is->merge_buf = NULL;
121     for (i = 0; i<is->no_files; i++)
122     {
123         char fname[512];
124
125         sprintf (fname, "%s%c", name, i+'A');
126         is->files[i].bf = bf_open (fname, is->method->filecat[i].bsize,
127                                    writeflag);
128         is->files[i].head_is_dirty = 0;
129         if (!bf_read (is->files[i].bf, 0, 0, sizeof(ISAMC_head),
130                      &is->files[i].head))
131         {
132             is->files[i].head.lastblock = 1;
133             is->files[i].head.freelist = 0;
134         }
135         is->files[i].no_writes = 0;
136         is->files[i].no_reads = 0;
137         is->files[i].no_skip_writes = 0;
138         is->files[i].no_allocated = 0;
139         is->files[i].no_released = 0;
140         is->files[i].no_remap = 0;
141
142         init_fc (is, i);
143     }
144     return is;
145 }
146
147 int isc_close (ISAMC is)
148 {
149     int i;
150
151     if (is->method->debug)
152         logf (LOG_LOG, "isc:  writes   reads skipped   alloc released  remap");
153     for (i = 0; i<is->no_files; i++)
154     {
155         release_fc (is, i);
156         assert (is->files[i].bf);
157         if (is->files[i].head_is_dirty)
158             bf_write (is->files[i].bf, 0, 0, sizeof(ISAMC_head),
159                  &is->files[i].head);
160         if (is->method->debug)
161             logf (LOG_LOG, "isc:%8d%8d%8d%8d%8d%8d",
162                   is->files[i].no_writes,
163                   is->files[i].no_reads,
164                   is->files[i].no_skip_writes,
165                   is->files[i].no_allocated,
166                   is->files[i].no_released,
167                   is->files[i].no_remap);
168         xfree (is->files[i].fc_list);
169         bf_close (is->files[i].bf);
170     }
171     xfree (is->files);
172     xfree (is->merge_buf);
173     xfree (is);
174     return 0;
175 }
176
177 int isc_read_block (ISAMC is, int cat, int pos, char *dst)
178 {
179     ++(is->files[cat].no_reads);
180     return bf_read (is->files[cat].bf, pos, 0, 0, dst);
181 }
182
183 int isc_write_block (ISAMC is, int cat, int pos, char *src)
184 {
185     ++(is->files[cat].no_writes);
186     if (is->method->debug > 2)
187         logf (LOG_LOG, "isc: write_block %d %d", cat, pos);
188     return bf_write (is->files[cat].bf, pos, 0, 0, src);
189 }
190
191 int isc_write_dblock (ISAMC is, int cat, int pos, char *src,
192                       int nextpos, int offset)
193 {
194     unsigned short size = offset + ISAMC_BLOCK_OFFSET_N;
195     if (is->method->debug > 2)
196         logf (LOG_LOG, "isc: write_dblock. size=%d nextpos=%d",
197               (int) size, nextpos);
198     src -= ISAMC_BLOCK_OFFSET_N;
199     memcpy (src, &nextpos, sizeof(int));
200     memcpy (src + sizeof(int), &size, sizeof(size));
201     return isc_write_block (is, cat, pos, src);
202 }
203
204 static int alloc_block (ISAMC is, int cat)
205 {
206     int block;
207     char buf[sizeof(int)];
208
209     is->files[cat].head_is_dirty = 1;
210     (is->files[cat].no_allocated)++;
211     if ((block = is->files[cat].head.freelist))
212     {
213         bf_read (is->files[cat].bf, block, 0, sizeof(int), buf);
214         memcpy (&is->files[cat].head.freelist, buf, sizeof(int));
215     }
216     else
217         block = (is->files[cat].head.lastblock)++;
218     return block;
219 }
220
221 int isc_alloc_block (ISAMC is, int cat)
222 {
223     int block = 0;
224
225     if (is->files[cat].fc_list)
226     {
227         int j, nb;
228         for (j = 0; j < is->files[cat].fc_max; j++)
229             if ((nb = is->files[cat].fc_list[j]) && (!block || nb < block))
230             {
231                 is->files[cat].fc_list[j] = 0;
232                 break;
233             }
234     }
235     if (!block)
236         block = alloc_block (is, cat);
237     if (is->method->debug > 3)
238         logf (LOG_LOG, "isc: alloc_block in cat %d: %d", cat, block);
239     return block;
240 }
241
242 static void release_block (ISAMC is, int cat, int pos)
243 {
244     char buf[sizeof(int)];
245    
246     (is->files[cat].no_released)++;
247     is->files[cat].head_is_dirty = 1; 
248     memcpy (buf, &is->files[cat].head.freelist, sizeof(int));
249     is->files[cat].head.freelist = pos;
250     bf_write (is->files[cat].bf, pos, 0, sizeof(int), buf);
251 }
252
253 void isc_release_block (ISAMC is, int cat, int pos)
254 {
255     if (is->method->debug > 3)
256         logf (LOG_LOG, "isc: release_block in cat %d: %d", cat, pos);
257     if (is->files[cat].fc_list)
258     {
259         int j;
260         for (j = 0; j<is->files[cat].fc_max; j++)
261             if (!is->files[cat].fc_list[j])
262             {
263                 is->files[cat].fc_list[j] = pos;
264                 return;
265             }
266     }
267     release_block (is, cat, pos);
268 }
269
270 static void init_fc (ISAMC is, int cat)
271 {
272     int j = 100;
273         
274     is->files[cat].fc_max = j;
275     is->files[cat].fc_list = xmalloc (sizeof(*is->files[0].fc_list) * j);
276     while (--j >= 0)
277         is->files[cat].fc_list[j] = 0;
278 }
279
280 static void release_fc (ISAMC is, int cat)
281 {
282     int b, j = is->files[cat].fc_max;
283
284     while (--j >= 0)
285         if ((b = is->files[cat].fc_list[j]))
286         {
287             release_block (is, cat, b);
288             is->files[cat].fc_list[j] = 0;
289         }
290 }
291
292 void isc_pp_close (ISAMC_PP pp)
293 {
294     ISAMC is = pp->is;
295
296     (*is->method->code_stop)(ISAMC_DECODE, pp->decodeClientData);
297     xfree (pp->buf);
298     xfree (pp);
299 }
300
301 ISAMC_PP isc_pp_open (ISAMC is, ISAMC_P ipos)
302 {
303     ISAMC_PP pp = xmalloc (sizeof(*pp));
304     char *src;
305    
306     pp->cat = isc_type(ipos);
307     pp->pos = isc_block(ipos); 
308
309     src = pp->buf = xmalloc (is->method->filecat[pp->cat].bsize);
310
311     pp->next = 0;
312     pp->size = 0;
313     pp->offset = 0;
314     pp->is = is;
315     pp->decodeClientData = (*is->method->code_start)(ISAMC_DECODE);
316     pp->deleteFlag = 0;
317     pp->numKeys = 0;
318
319     if (pp->pos)
320     {
321         src = pp->buf;
322         isc_read_block (is, pp->cat, pp->pos, src);
323         memcpy (&pp->next, src, sizeof(pp->next));
324         src += sizeof(pp->next);
325         memcpy (&pp->size, src, sizeof(pp->size));
326         src += sizeof(pp->size);
327         memcpy (&pp->numKeys, src, sizeof(pp->numKeys));
328         src += sizeof(pp->numKeys);
329         assert (pp->next != pp->pos);
330         pp->offset = src - pp->buf; 
331         assert (pp->offset == ISAMC_BLOCK_OFFSET_1);
332         if (is->method->debug > 2)
333             logf (LOG_LOG, "isc: read_block size=%d %d %d",
334                  pp->size, pp->cat, pp->pos);
335     }
336     return pp;
337 }
338
339 /* returns non-zero if item could be read; 0 otherwise */
340 int isc_pp_read (ISAMC_PP pp, void *buf)
341 {
342     return isc_read_item (pp, (char **) &buf);
343 }
344
345 /* returns non-zero if item could be read; 0 otherwise */
346 int isc_read_item (ISAMC_PP pp, char **dst)
347 {
348     ISAMC is = pp->is;
349     char *src = pp->buf + pp->offset;
350
351     if (pp->offset >= pp->size)
352     {
353         pp->pos = pp->next;
354         if (!pp->pos)
355             return 0;
356         src = pp->buf;
357         isc_read_block (is, pp->cat, pp->pos, src);
358         memcpy (&pp->next, src, sizeof(pp->next));
359         src += sizeof(pp->next);
360         memcpy (&pp->size, src, sizeof(pp->size));
361         src += sizeof(pp->size);
362         /* assume block is non-empty */
363         assert (src - pp->buf == ISAMC_BLOCK_OFFSET_N);
364         assert (pp->next != pp->pos);
365         if (pp->deleteFlag)
366             isc_release_block (is, pp->cat, pp->pos);
367         (*is->method->code_item)(ISAMC_DECODE, pp->decodeClientData, dst, &src);
368         pp->offset = src - pp->buf; 
369         if (is->method->debug > 2)
370             logf (LOG_LOG, "isc: read_block size=%d %d %d",
371                  pp->size, pp->cat, pp->pos);
372         return 2;
373     }
374     (*is->method->code_item)(ISAMC_DECODE, pp->decodeClientData, dst, &src);
375     pp->offset = src - pp->buf; 
376     return 1;
377 }
378
379 int isc_pp_num (ISAMC_PP pp)
380 {
381     return pp->numKeys;
382 }
383