Most of the functionality in place.
[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.1  1994-09-26 16:07:56  quinn
8  * Most of the functionality in place.
9  *
10  */
11
12 /*
13  * This module accesses and rearranges the records of the tables.
14  */
15
16 #include <assert.h>
17
18 #include <util.h>
19 #include "memory.h"
20 #include "physical.h"
21 #include <isam.h>
22
23 int is_mbuf_size[3] = { 0, 1024, 4096 };
24
25 /*
26  * TODO: make internal memory-management scheme for these units.
27  */
28
29 is_mblock *xmalloc_mblock()
30 {
31     is_mblock *tmp;
32
33     tmp = xmalloc(sizeof(is_mblock));
34     tmp->next = 0;
35     return tmp;
36 }
37
38 is_mbuf *xmalloc_mbuf(int type)
39 {
40     is_mbuf *tmp;
41
42     tmp = xmalloc(sizeof(is_mbuf) + is_mbuf_size[type]);
43     tmp->type = type;
44     tmp->refcount = type ? 1 : 0;
45     tmp->offset = tmp->num = tmp->cur_record = 0;
46     tmp->data = (char*) tmp + sizeof(is_mbuf);
47     tmp->next = 0;
48     return tmp;
49 }
50
51 #if 0
52 void xfree_mblock(is_mblock *p)
53 {
54     xfree(p);
55 }
56 #endif
57
58 #if 0
59 void xfree_mbuf(is_mblock *p)
60 {
61     xfree(p);
62 }
63 #endif
64
65 void is_m_establish_tab(ISAM is, is_mtable *tab, ISAM_P pos)
66 {
67     tab->data = xmalloc_mblock();
68     if (pos > 0)
69     {
70         tab->pos_type = is_type(pos);
71         tab->num_records = -1;
72         tab->data->num_records = -1;
73         tab->data->diskpos = is_block(pos);
74         tab->data->state = IS_MBSTATE_UNREAD;
75         tab->data->data = 0;
76         tab->cur_mblock = tab->data;
77         tab->cur_mblock->cur_mbuf = 0;
78     }
79     else /* new block */
80     {
81         tab->pos_type = 0;
82         tab->num_records = 0;
83         tab->data->num_records = 0;
84         tab->data->diskpos = -1;
85         tab->data->state = IS_MBSTATE_CLEAN;
86         tab->data->data = xmalloc_mbuf(IS_MBUF_TYPE_LARGE);
87         tab->cur_mblock = tab->data;
88         tab->cur_mblock->cur_mbuf = tab->data->data;
89         tab->cur_mblock->cur_mbuf->cur_record = 0;
90     }
91     tab->is = is;
92 }
93
94 void is_m_rewind(is_mtable *tab)
95 {
96     tab->cur_mblock = tab->data;
97     if (tab->data)
98     {
99         tab->data->cur_mbuf = tab->data->data;
100         if (tab->data->data)
101             tab->data->data->cur_record = 0;
102     }
103 }
104
105 static int read_current_full(is_mtable *tab, is_mblock *mblock)
106 {
107     if (is_p_read_full(tab, mblock) < 0)
108         return -1;
109     if (mblock->nextpos && !mblock->next)
110     {
111         mblock->next = xmalloc_mblock();
112         mblock->next->diskpos = mblock->nextpos;
113         mblock->next->state = IS_MBSTATE_UNREAD;
114         mblock->next->data = 0;
115     }
116     mblock->cur_mbuf = mblock->data;
117     mblock->data->cur_record = 0;
118     return 0;
119 }
120
121 int is_m_read_full(is_mtable *tab, is_mblock *mblock)
122 {
123     return read_current_full(tab, mblock);
124 }
125
126 /*
127  * replace the record right behind the pointer.
128  */
129 void is_m_replace_record(is_mtable *tab, const void *rec)
130 {
131     is_mbuf *mbuf = tab->cur_mblock->cur_mbuf;
132     
133     /* we assume that block is already in memory and that we are in the
134      * right mbuf, and that it has space for us. */
135     memcpy(mbuf->data + mbuf->offset + (mbuf->cur_record - 1) *
136         is_keysize(tab->is), rec, is_keysize(tab->is));
137     tab->cur_mblock->state = IS_MBSTATE_DIRTY;
138 }
139
140 /*
141  * Delete the record right behind the pointer.
142  */
143 void is_m_delete_record(is_mtable *tab)
144 {
145     is_mbuf *mbuf, *new;
146
147     mbuf = tab->cur_mblock->cur_mbuf;
148     if (mbuf->cur_record >= mbuf->num)  /* top of mbuf */
149     {
150         mbuf->num--;
151         mbuf->cur_record--;
152     }
153     else /* middle of a block */
154     {
155         new = xmalloc_mbuf(IS_MBUF_TYPE_SMALL);
156         new->next = mbuf->next;
157         mbuf->next = new;
158         new->data = mbuf->data;
159         mbuf->refcount++;
160         new->offset = mbuf->offset + mbuf->cur_record * is_keysize(tab->is);
161         new->num = mbuf->num - mbuf->cur_record;
162         mbuf->num = mbuf->cur_record -1;
163         mbuf = mbuf->next;
164         mbuf->cur_record = 0;
165     }
166     tab->num_records--;
167     tab->cur_mblock->num_records--;
168     tab->cur_mblock->state = tab->data->state = IS_MBSTATE_DIRTY;
169 }
170
171 int is_m_write_record(is_mtable *tab, const void *rec)
172 {
173     is_mbuf *mbuf, *oldnext, *dmbuf;
174
175     /* make sure block is all in memory */
176     if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
177         if (read_current_full(tab, tab->cur_mblock) < 0)
178             return -1;
179     mbuf = tab->cur_mblock->cur_mbuf;
180     if (mbuf->cur_record >= mbuf->num)  /* top of mbuf */
181     {
182         /* mbuf is reference or full */
183         if (mbuf->refcount != 1 || mbuf->offset + (mbuf->num + 1) *
184             is_keysize(tab->is) > is_mbuf_size[mbuf->type])
185         {
186             oldnext = mbuf->next;
187             mbuf->next = xmalloc_mbuf(IS_MBUF_TYPE_LARGE);
188             mbuf->next->next = oldnext;
189             mbuf = mbuf->next;
190             tab->cur_mblock->cur_mbuf = mbuf;
191             mbuf->cur_record = 0;
192         }
193     }
194     else
195     {
196         oldnext = mbuf->next;
197         mbuf->next = xmalloc_mbuf(IS_MBUF_TYPE_MEDIUM);
198         mbuf->next->next = dmbuf = xmalloc_mbuf(IS_MBUF_TYPE_SMALL);
199         dmbuf->data = mbuf->data;
200         dmbuf->next = oldnext;
201         dmbuf->offset = mbuf->offset + mbuf->cur_record * is_keysize(tab->is);
202         dmbuf->num = mbuf->num - mbuf->cur_record;
203         mbuf->num -= dmbuf->num;
204         mbuf->refcount++;
205         mbuf = tab->cur_mblock->cur_mbuf = mbuf->next;
206         mbuf->cur_record = 0;
207     }
208     log(LOG_DEBUG, "is_m_write_rec(rec == %d)", mbuf->cur_record);
209     memcpy(mbuf->data + mbuf->offset + mbuf->cur_record * is_keysize(tab->is),
210         rec, is_keysize(tab->is));
211     mbuf->num++;
212     mbuf->cur_record++;
213     tab->num_records++;
214     tab->cur_mblock->num_records++;
215     tab->cur_mblock->state = tab->data->state = IS_MBSTATE_DIRTY;
216     return 0;
217 }
218
219 void is_m_unread_record(is_mtable *tab)
220 {
221     assert(tab->cur_mblock->cur_mbuf->cur_record);
222     tab->cur_mblock->cur_mbuf->cur_record--;
223 }
224
225 /*
226  * non-destructive read.
227  */
228 int is_m_peek_record(is_mtable *tab, void *rec)
229 {
230     is_mbuf *mbuf;
231
232     /* make sure block is all in memory */
233     if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
234         if (read_current_full(tab, tab->cur_mblock) < 0)
235             return -1;
236     mbuf = tab->cur_mblock->cur_mbuf;
237     if (mbuf->cur_record >= mbuf->num) /* are we at end of mbuf? */
238     {
239         if (!mbuf->next) /* end of mblock */
240         {
241             if (tab->cur_mblock->next)
242             {
243                 tab->cur_mblock = tab->cur_mblock->next;
244                 if (tab->cur_mblock->next->state <= IS_MBSTATE_PARTIAL)
245                     if (read_current_full(tab, tab->cur_mblock->next) < 0)
246                         return -1;
247                 mbuf = tab->cur_mblock->next->data;
248             }
249             else
250                 return 0;   /* EOTable */
251         }
252         else
253             mbuf = mbuf->next;
254         mbuf->cur_record = 0;
255     }
256     memcpy(rec, mbuf->data + mbuf->offset + mbuf->cur_record *
257         is_keysize(tab->is), is_keysize(tab->is));
258     return 1;
259 }
260
261 int is_m_read_record(is_mtable *tab, void *buf)
262 {
263     is_mbuf *mbuf;
264
265     /* make sure block is all in memory */
266     if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
267         if (read_current_full(tab, tab->cur_mblock) < 0)
268             return -1;
269     mbuf = tab->cur_mblock->cur_mbuf;
270     if (mbuf->cur_record >= mbuf->num) /* are we at end of mbuf? */
271     {
272         if (!mbuf->next) /* end of mblock */
273         {
274             if (tab->cur_mblock->next)
275             {
276                 tab->cur_mblock = tab->cur_mblock->next;
277                 if (tab->cur_mblock->state <= IS_MBSTATE_PARTIAL)
278                     if (read_current_full(tab, tab->cur_mblock) < 0)
279                         return -1;
280                 tab->cur_mblock->cur_mbuf = mbuf = tab->cur_mblock->data;
281             }
282             else
283                 return 0;   /* EOTable */
284         }
285         else
286             tab->cur_mblock->cur_mbuf = mbuf = mbuf->next;
287         mbuf->cur_record = 0;
288     }
289     memcpy(buf, mbuf->data + mbuf->offset + mbuf->cur_record *
290         is_keysize(tab->is), is_keysize(tab->is));
291     mbuf->cur_record++;
292     return 1;
293 }
294
295 /*
296  * TODO: optimize this function by introducing a higher-level search.
297  */
298 int is_m_seek_record(is_mtable *tab, const void *rec)
299 {
300     char peek[IS_MAX_RECORD];
301     int rs;
302
303     for (;;)
304     {
305         if (is_m_read_record(tab, &peek) <= 0)
306             return 1;
307         if ((rs = (*tab->is->cmp)(peek, rec)) > 0)
308         {
309             is_m_unread_record(tab);
310             return 1;
311         }
312         else if (rs == 0)
313             return 0;
314     }
315 }