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