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