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