Fixed memory-consumption bug in memory.c
[idzebra-moved-to-github.git] / isam / memory.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: memory.c,v $
7  * Revision 1.7  1995-12-06 09:59:46  quinn
8  * Fixed memory-consumption bug in memory.c
9  * Added more blocksizes to the default ISAM configuration.
10  *
11  * Revision 1.6  1995/09/04  12:33:47  adam
12  * Various cleanup. YAZ util used instead.
13  *
14  * Revision 1.5  1994/09/28  16:58:33  quinn
15  * Small mod.
16  *
17  * Revision 1.4  1994/09/27  20:03:52  quinn
18  * Seems relatively bug-free.
19  *
20  * Revision 1.3  1994/09/26  17:11:30  quinn
21  * Trivial
22  *
23  * Revision 1.2  1994/09/26  17:06:35  quinn
24  * Back again...
25  *
26  * Revision 1.1  1994/09/26  16:07:56  quinn
27  * Most of the functionality in place.
28  *
29  */
30
31 /*
32  * This module accesses and rearranges the records of the tables.
33  */
34
35 #include <assert.h>
36 #include <stdio.h>
37
38 #include <alexutil.h>
39 #include <isam.h>
40
41 int is_mbuf_size[3] = { 0, 1024, 4096 };
42
43 static is_mblock *mblock_tmplist = 0, *mblock_freelist = 0;
44 static is_mbuf *mbuf_freelist[3] = {0, 0, 0};
45
46 #define MALLOC_CHUNK 20
47
48 is_mblock *xmalloc_mblock()
49 {
50     is_mblock *tmp;
51     int i;
52
53     if (!mblock_freelist)
54     {
55         mblock_freelist = xmalloc(sizeof(is_mblock) * MALLOC_CHUNK);
56         for (i = 0; i < MALLOC_CHUNK - 1; i++)
57             mblock_freelist[i].next = &mblock_freelist[i+1];
58         mblock_freelist[i].next = 0;
59     }
60     tmp = mblock_freelist;
61     mblock_freelist = mblock_freelist->next;
62     tmp->next = 0;
63     tmp->state = IS_MBSTATE_UNREAD;
64     tmp->data = 0;
65     return tmp;
66 }
67
68 is_mbuf *xmalloc_mbuf(int type)
69 {
70     is_mbuf *tmp;
71
72     if (mbuf_freelist[type])
73     {
74         tmp = mbuf_freelist[type];
75         mbuf_freelist[type] = tmp->next;
76     }
77     else
78     {
79         tmp = xmalloc(sizeof(is_mbuf) + is_mbuf_size[type]);
80         tmp->type = type;
81     }
82     tmp->refcount = type ? 1 : 0;
83     tmp->offset = tmp->num = tmp->cur_record = 0;
84     tmp->data = (char*) tmp + sizeof(is_mbuf);
85     tmp->next = 0;
86     return tmp;
87 }
88
89 void xfree_mbuf(is_mbuf *p)
90 {
91     p->next = mbuf_freelist[p->type];
92     mbuf_freelist[p->type] = p;
93 }
94
95 void xfree_mbufs(is_mbuf *l)
96 {
97     is_mbuf *p;
98
99     while (l)
100     {
101         p = l->next;
102         xfree_mbuf(l);
103         l = p;
104     }
105 }
106
107 void xfree_mblock(is_mblock *p)
108 {
109     xfree_mbufs(p->data);
110     p->next = mblock_freelist;
111     mblock_freelist = p;
112 }
113
114 void xrelease_mblock(is_mblock *p)
115 {
116     p->next = mblock_tmplist;
117     mblock_tmplist = p;
118 }
119
120 void xfree_mblocks(is_mblock *l)
121 {
122     is_mblock *p;
123
124     while (l)
125     {
126         p = l->next;
127         xfree_mblock(l);
128         l = p;
129     }
130 }
131
132 void is_m_establish_tab(ISAM is, is_mtable *tab, ISAM_P pos)
133 {
134     tab->data = xmalloc_mblock();
135     if (pos > 0)
136     {
137         tab->pos_type = is_type(pos);
138         tab->num_records = -1;
139         tab->data->num_records = -1;
140         tab->data->diskpos = is_block(pos);
141         tab->data->state = IS_MBSTATE_UNREAD;
142         tab->data->data = 0;
143         tab->cur_mblock = tab->data;
144         tab->cur_mblock->cur_mbuf = 0;
145     }
146     else /* new block */
147     {
148         tab->pos_type = 0;
149         tab->num_records = 0;
150         tab->data->num_records = 0;
151         tab->data->diskpos = -1;
152         tab->data->state = IS_MBSTATE_CLEAN;
153         tab->data->data = xmalloc_mbuf(IS_MBUF_TYPE_LARGE);
154         tab->cur_mblock = tab->data;
155         tab->cur_mblock->cur_mbuf = tab->data->data;
156         tab->cur_mblock->cur_mbuf->cur_record = 0;
157     }
158     tab->is = is;
159 }
160
161 void is_m_release_tab(is_mtable *tab)
162 {
163     xfree_mblocks(tab->data);
164     xfree_mblocks(mblock_tmplist);
165     mblock_tmplist = 0;
166 }
167
168 void is_m_rewind(is_mtable *tab)
169 {
170     tab->cur_mblock = tab->data;
171     if (tab->data)
172     {
173         tab->data->cur_mbuf = tab->data->data;
174         if (tab->data->data)
175             tab->data->data->cur_record = 0;
176     }
177 }
178
179 static int read_current_full(is_mtable *tab, is_mblock *mblock)
180 {
181     if (is_p_read_full(tab, mblock) < 0)
182         return -1;
183     if (mblock->nextpos && !mblock->next)
184     {
185         mblock->next = xmalloc_mblock();
186         mblock->next->diskpos = mblock->nextpos;
187         mblock->next->state = IS_MBSTATE_UNREAD;
188         mblock->next->data = 0;
189     }
190     mblock->cur_mbuf = mblock->data;
191     mblock->data->cur_record = 0;
192     return 0;
193 }
194
195 int is_m_read_full(is_mtable *tab, is_mblock *mblock)
196 {
197     return read_current_full(tab, mblock);
198 }
199
200 /*
201  * replace the record right behind the pointer.
202  */
203 void is_m_replace_record(is_mtable *tab, const void *rec)
204 {
205     is_mbuf *mbuf = tab->cur_mblock->cur_mbuf;
206     
207     /* we assume that block is already in memory and that we are in the
208      * right mbuf, and that it has space for us. */
209     memcpy(mbuf->data + mbuf->offset + (mbuf->cur_record - 1) *
210         is_keysize(tab->is), rec, is_keysize(tab->is));
211     tab->cur_mblock->state = IS_MBSTATE_DIRTY;
212 }
213
214 /*
215  * Delete the record right behind the pointer.
216  */
217 void is_m_delete_record(is_mtable *tab)
218 {
219     is_mbuf *mbuf, *new;
220
221     mbuf = tab->cur_mblock->cur_mbuf;
222     if (mbuf->cur_record >= mbuf->num)  /* top of mbuf */
223     {
224         mbuf->num--;
225         mbuf->cur_record--;
226     }
227     else /* middle of a block */
228     {
229         new = xmalloc_mbuf(IS_MBUF_TYPE_SMALL);
230         new->next = mbuf->next;
231         mbuf->next = new;
232         new->data = mbuf->data;
233         mbuf->refcount++;
234         new->offset = mbuf->offset + mbuf->cur_record * is_keysize(tab->is);
235         new->num = mbuf->num - mbuf->cur_record;
236         mbuf->num = mbuf->cur_record -1;
237         mbuf = mbuf->next;
238         mbuf->cur_record = 0;
239     }
240     tab->num_records--;
241     tab->cur_mblock->num_records--;
242     tab->cur_mblock->state = tab->data->state = IS_MBSTATE_DIRTY;
243 }
244
245 int is_m_write_record(is_mtable *tab, const void *rec)
246 {
247     is_mbuf *mbuf, *oldnext, *dmbuf;
248
249     /* make sure block is all in memory */
250     if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
251         if (read_current_full(tab, tab->cur_mblock) < 0)
252             return -1;
253     mbuf = tab->cur_mblock->cur_mbuf;
254     if (mbuf->cur_record >= mbuf->num)  /* top of mbuf */
255     {
256         /* mbuf is reference or full */
257         if (mbuf->refcount != 1 || mbuf->offset + (mbuf->num + 1) *
258             is_keysize(tab->is) > is_mbuf_size[mbuf->type])
259         {
260             oldnext = mbuf->next;
261             mbuf->next = xmalloc_mbuf(IS_MBUF_TYPE_LARGE);
262             mbuf->next->next = oldnext;
263             mbuf = mbuf->next;
264             tab->cur_mblock->cur_mbuf = mbuf;
265             mbuf->cur_record = 0;
266         }
267     }
268     else
269     {
270         oldnext = mbuf->next;
271         mbuf->next = xmalloc_mbuf(IS_MBUF_TYPE_MEDIUM);
272         mbuf->next->next = dmbuf = xmalloc_mbuf(IS_MBUF_TYPE_SMALL);
273         dmbuf->data = mbuf->data;
274         dmbuf->next = oldnext;
275         dmbuf->offset = mbuf->offset + mbuf->cur_record * is_keysize(tab->is);
276         dmbuf->num = mbuf->num - mbuf->cur_record;
277         mbuf->num -= dmbuf->num;
278         mbuf->refcount++;
279         mbuf = tab->cur_mblock->cur_mbuf = mbuf->next;
280         mbuf->cur_record = 0;
281     }
282     logf (LOG_DEBUG, "is_m_write_rec(rec == %d)", mbuf->cur_record);
283     memcpy(mbuf->data + mbuf->offset + mbuf->cur_record * is_keysize(tab->is),
284         rec, is_keysize(tab->is));
285     mbuf->num++;
286     mbuf->cur_record++;
287     tab->num_records++;
288     tab->cur_mblock->num_records++;
289     tab->cur_mblock->state = tab->data->state = IS_MBSTATE_DIRTY;
290     return 0;
291 }
292
293 void is_m_unread_record(is_mtable *tab)
294 {
295     assert(tab->cur_mblock->cur_mbuf->cur_record);
296     tab->cur_mblock->cur_mbuf->cur_record--;
297 }
298
299 /*
300  * non-destructive read.
301  */
302 int is_m_peek_record(is_mtable *tab, void *rec)
303 {
304     is_mbuf *mbuf;
305     is_mblock *mblock;
306
307     /* make sure block is all in memory */
308     if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
309         if (read_current_full(tab, tab->cur_mblock) < 0)
310             return -1;
311     mblock = tab->cur_mblock;
312     mbuf = mblock->cur_mbuf;
313     if (mbuf->cur_record >= mbuf->num) /* are we at end of mbuf? */
314     {
315         if (!mbuf->next) /* end of mblock */
316         {
317             if (mblock->next)
318             {
319                 mblock = mblock->next;
320                 if (mblock->state <= IS_MBSTATE_PARTIAL)
321                     if (read_current_full(tab, mblock) < 0)
322                         return -1;
323                 mbuf = mblock->data;
324             }
325             else
326                 return 0;   /* EOTable */
327         }
328         else
329             mbuf = mbuf->next;
330         mbuf->cur_record = 0;
331     }
332     memcpy(rec, mbuf->data + mbuf->offset + mbuf->cur_record *
333         is_keysize(tab->is), is_keysize(tab->is));
334     return 1;
335 }
336
337 int is_m_read_record(is_mtable *tab, void *buf)
338 {
339     is_mbuf *mbuf;
340
341     /* make sure block is all in memory */
342     if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
343         if (read_current_full(tab, tab->cur_mblock) < 0)
344             return -1;
345     mbuf = tab->cur_mblock->cur_mbuf;
346     if (mbuf->cur_record >= mbuf->num) /* are we at end of mbuf? */
347     {
348         if (!mbuf->next) /* end of mblock */
349         {
350             if (tab->cur_mblock->state == IS_MBSTATE_CLEAN)
351             {
352                 xfree_mbufs(tab->cur_mblock->data);
353                 tab->cur_mblock->data = 0;
354                 tab->cur_mblock->state = IS_MBSTATE_UNREAD;
355             }
356             if (tab->cur_mblock->next)
357             {
358                 tab->cur_mblock = tab->cur_mblock->next;
359                 if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
360                     if (read_current_full(tab, tab->cur_mblock) < 0)
361                         return -1;
362                 tab->cur_mblock->cur_mbuf = mbuf = tab->cur_mblock->data;
363             }
364             else
365                 return 0;   /* EOTable */
366         }
367         else
368             tab->cur_mblock->cur_mbuf = mbuf = mbuf->next;
369         mbuf->cur_record = 0;
370     }
371     memcpy(buf, mbuf->data + mbuf->offset + mbuf->cur_record *
372         is_keysize(tab->is), is_keysize(tab->is));
373     mbuf->cur_record++;
374     return 1;
375 }
376
377 /*
378  * TODO: optimize this function by introducing a higher-level search.
379  */
380 int is_m_seek_record(is_mtable *tab, const void *rec)
381 {
382     char peek[IS_MAX_RECORD];
383     int rs;
384
385     for (;;)
386     {
387         if (is_m_read_record(tab, &peek) <= 0)
388             return 1;
389         if ((rs = (*tab->is->cmp)(peek, rec)) > 0)
390         {
391             is_m_unread_record(tab);
392             return 1;
393         }
394         else if (rs == 0)
395             return 0;
396     }
397 }
398
399 int is_m_num_records(is_mtable *tab)
400 {
401     if (tab->data->state < IS_MBSTATE_PARTIAL)
402         if (read_current_full(tab, tab->data) < 0)
403         {
404             logf (LOG_FATAL, "read full failed");
405             exit(1);
406         }
407     return tab->num_records;
408 }