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