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