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