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