New update method: the 'old' keys are saved for each records.
[idzebra-moved-to-github.git] / index / recindex.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recindex.c,v $
7  * Revision 1.4  1995-11-20 16:59:46  adam
8  * New update method: the 'old' keys are saved for each records.
9  *
10  * Revision 1.3  1995/11/16  15:34:55  adam
11  * Uses new record management system in both indexer and server.
12  *
13  * Revision 1.2  1995/11/15  19:13:08  adam
14  * Work on record management.
15  *
16  * Revision 1.1  1995/11/15  14:46:20  adam
17  * Started work on better record management system.
18  *
19  */
20 #include <stdio.h>
21 #include <assert.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <assert.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27
28 #include "recindex.h"
29
30 struct records_info {
31     int rw;
32     int index_fd;
33     char *index_fname;
34     int data_fd;
35     char *data_fname;
36     struct records_head {
37         char magic[8];
38         int no_records;
39         int index_free;
40         int index_last;
41         int data_size;
42         int data_slack;
43         int data_used;
44     } head;
45     char *tmp_buf;
46     int tmp_size;
47     int cache_size;
48     int cache_cur;
49     int cache_max;
50     struct record_cache_entry *record_cache;
51 };
52
53 struct record_cache_entry {
54     Record rec;
55     int dirty;
56 };
57
58 struct record_index_entry {
59     union {
60         struct {
61             int offset;
62             int size;
63         } used;
64         struct {
65             int next;
66         } free;
67     } u;
68 };
69
70 #define REC_HEAD_MAGIC "rechead"
71
72 char *rec_strdup (const char *s, size_t *len)
73 {
74     char *p;
75
76     if (!s)
77     {
78         *len = 0;
79         return NULL;
80     }
81     *len = strlen(s)+1;
82     p = malloc (*len);
83     if (!p)
84     {
85         logf (LOG_FATAL|LOG_ERRNO, "malloc");
86         exit (1);
87     }
88     strcpy (p, s);
89     return p;
90 }
91
92 static void rec_write_head (Records p)
93 {
94     int r;
95
96     assert (p);
97     assert (p->index_fd != -1);
98     if (lseek (p->index_fd, (off_t) 0, SEEK_SET) == -1)
99     {
100         logf (LOG_FATAL|LOG_ERRNO, "lseek to 0 in %s", p->index_fname);
101         exit (1);
102     }
103     r = write (p->index_fd, &p->head, sizeof(p->head));    
104     switch (r)
105     {
106     case -1:
107         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
108         exit (1);
109     case sizeof(p->head):
110         break;
111     default:
112         logf (LOG_FATAL, "write head of %s. wrote %d", p->index_fname, r);
113         exit (1);
114     }
115 }
116
117 Records rec_open (int rw)
118 {
119     Records p;
120     int r;
121
122     if (!(p = malloc (sizeof(*p))))
123     {
124         logf (LOG_FATAL|LOG_ERRNO, "malloc");
125         exit (1);
126     }
127     p->rw = rw;
128     p->tmp_buf = NULL;
129     p->tmp_size = 0;
130     p->data_fname = "recdata";
131     p->data_fd = -1;
132     p->index_fname = "recindex";
133     p->index_fd = open (p->index_fname,
134                         rw ? (O_RDWR|O_CREAT) : O_RDONLY, 0666);
135     if (p->index_fd == -1)
136     {
137         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
138         exit (1);
139     }
140     r = read (p->index_fd, &p->head, sizeof(p->head));
141     switch (r)
142     {
143     case -1:
144         logf (LOG_FATAL|LOG_ERRNO, "read %s", p->index_fname);
145         exit (1);
146     case 0:
147         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
148         p->head.index_free = 0;
149         p->head.index_last = 1;
150         p->head.no_records = 0;
151         p->head.data_size = 0;
152         p->head.data_slack = 0;
153         p->head.data_used = 0;
154         if (rw)
155             rec_write_head (p);
156         break;
157     case sizeof(p->head):
158         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
159         {
160             logf (LOG_FATAL, "read %s. bad header", p->index_fname);
161             exit (1);
162         }
163         break;
164     default:
165         logf (LOG_FATAL, "read head of %s. expected %d. got %d",
166               p->index_fname, sizeof(p->head), r);
167         exit (1);
168     }
169     p->data_fd = open (p->data_fname,
170                        rw ? (O_RDWR|O_CREAT) : O_RDONLY, 0666);
171     if (p->data_fd == -1)
172     {
173         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->data_fname);
174         exit (1);
175     }
176     p->cache_max = 10;
177     p->cache_cur = 0;
178     if (!(p->record_cache = malloc (sizeof(*p->record_cache)*p->cache_max)))
179     {
180         logf (LOG_FATAL|LOG_ERRNO, "malloc");
181         exit (1);
182     }
183     return p;
184 }
185
186 static void read_indx (Records p, int sysno, void *buf, int itemsize)
187 {
188     int r;
189     off_t pos = (sysno-1)*itemsize + sizeof(p->head);
190
191     if (lseek (p->index_fd, pos, SEEK_SET) == (pos) -1)
192     {
193         logf (LOG_FATAL|LOG_ERRNO, "seek in %s to pos %ld",
194               p->index_fname, (long) pos);
195         exit (1);
196     }
197     r = read (p->index_fd, buf, itemsize);
198     if (r != itemsize)
199     {
200         if (r == -1)
201             logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
202                   p->index_fname, (long) pos);
203         else
204             logf (LOG_FATAL, "read in %s at pos %ld",
205                   p->index_fname, (long) pos);
206         exit (1);
207     }
208 }
209
210 static void rec_write_single (Records p, Record rec)
211 {
212     struct record_index_entry entry;
213     int r, i, size = 0, got;
214     char *cptr;
215     off_t pos = (rec->sysno-1)*sizeof(entry) + sizeof(p->head);
216
217     for (i = 0; i < REC_NO_INFO; i++)
218         if (!rec->info[i])
219             size += sizeof(*rec->size);
220         else
221             size += sizeof(*rec->size) + rec->size[i];
222     
223     entry.u.used.offset = p->head.data_size;
224     entry.u.used.size = size;
225     p->head.data_size += size;
226     p->head.data_used += size;
227
228     if (lseek (p->index_fd, pos, SEEK_SET) == (pos) -1)
229     {
230         logf (LOG_FATAL|LOG_ERRNO, "seek in %s to pos %ld",
231               p->index_fname, (long) pos);
232         exit (1);
233     }
234     r = write (p->index_fd, &entry, sizeof(entry));
235     if (r != sizeof(entry))
236     {
237         if (r == -1)
238             logf (LOG_FATAL|LOG_ERRNO, "write of %s at pos %ld",
239                   p->index_fname, (long) pos);
240         else
241             logf (LOG_FATAL, "write of %s at pos %ld",
242                   p->index_fname, (long) pos);
243         exit (1);
244     }
245     if (lseek (p->data_fd, entry.u.used.offset, SEEK_SET) == -1) 
246     {
247         logf (LOG_FATAL|LOG_ERRNO, "lseek in %s to pos %ld",
248               p->data_fname, entry.u.used.offset);
249         exit (1);
250     }
251     if (p->tmp_size < entry.u.used.size) 
252     {
253         free (p->tmp_buf);
254         p->tmp_size = entry.u.used.size + 16384;
255         if (!(p->tmp_buf = malloc (p->tmp_size)))
256         {
257             logf (LOG_FATAL|LOG_ERRNO, "malloc");
258             exit (1);
259         }
260     }
261     cptr = p->tmp_buf;
262     for (i = 0; i < REC_NO_INFO; i++)
263     {
264         memcpy (cptr, &rec->size[i], sizeof(*rec->size));
265         cptr += sizeof(*rec->size);
266         if (rec->info[i])
267         {
268             memcpy (cptr, rec->info[i], rec->size[i]);
269             cptr += rec->size[i];
270         }
271     }
272     for (got = 0; got < entry.u.used.size; got += r)
273     {
274         r = write (p->data_fd, p->tmp_buf + got, entry.u.used.size - got);
275         if (r <= 0)
276         {
277             logf (LOG_FATAL|LOG_ERRNO, "write of %s", p->data_fname);
278             exit (1);
279         }
280     }
281 }
282
283 static void rec_cache_flush (Records p)
284 {
285     int i;
286     for (i = 0; i<p->cache_cur; i++)
287     {
288         struct record_cache_entry *e = p->record_cache + i;
289         if (e->dirty)
290             rec_write_single (p, e->rec);
291         rec_rm (&e->rec);
292     }
293     p->cache_cur = 0;
294 }
295
296 static Record *rec_cache_lookup (Records p, int sysno, int dirty)
297 {
298     int i;
299     for (i = 0; i<p->cache_cur; i++)
300     {
301         struct record_cache_entry *e = p->record_cache + i;
302         if (e->rec->sysno == sysno)
303         {
304             if (dirty)
305                 e->dirty = 1;
306             return &e->rec;
307         }
308     }
309     return NULL;
310 }
311
312 static void rec_cache_insert (Records p, Record rec, int dirty)
313 {
314     struct record_cache_entry *e;
315
316     if (p->cache_cur == p->cache_max)
317         rec_cache_flush (p);
318     assert (p->cache_cur < p->cache_max);
319
320     e = p->record_cache + (p->cache_cur)++;
321     e->dirty = dirty;
322     e->rec = rec_cp (rec);
323 }
324
325 void rec_close (Records *p)
326 {
327     assert (*p);
328
329     rec_cache_flush (*p);
330     free ((*p)->record_cache);
331
332     if ((*p)->rw)
333         rec_write_head (*p);
334
335     if ((*p)->index_fd != -1)
336         close ((*p)->index_fd);
337
338     if ((*p)->data_fd != -1)
339         close ((*p)->data_fd);
340
341     free ((*p)->tmp_buf);
342
343     free (*p);
344     *p = NULL;
345 }
346
347 Record rec_get (Records p, int sysno)
348 {
349     int i;
350     Record rec, *recp;
351     struct record_index_entry entry;
352     int r, got;
353     char *nptr;
354
355     assert (sysno > 0);
356     assert (p);
357
358     if ((recp = rec_cache_lookup (p, sysno, 0)))
359         return rec_cp (*recp);
360
361     read_indx (p, sysno, &entry, sizeof(entry));
362     
363     if (!(rec = malloc (sizeof(*rec))))
364     {
365         logf (LOG_FATAL|LOG_ERRNO, "malloc");
366         exit (1);
367     }
368     if (lseek (p->data_fd, entry.u.used.offset, SEEK_SET) == -1) 
369     {
370         logf (LOG_FATAL|LOG_ERRNO, "lseek in %s to pos %ld",
371               p->data_fname, entry.u.used.offset);
372         exit (1);
373     }
374     if (p->tmp_size < entry.u.used.size) 
375     {
376         free (p->tmp_buf);
377         p->tmp_size = entry.u.used.size + 16384;
378         if (!(p->tmp_buf = malloc (p->tmp_size)))
379         {
380             logf (LOG_FATAL|LOG_ERRNO, "malloc");
381             exit (1);
382         }
383     }
384     for (got = 0; got < entry.u.used.size; got += r)
385     {
386         r = read (p->data_fd, p->tmp_buf + got, entry.u.used.size - got);
387         if (r <= 0)
388         {
389             logf (LOG_FATAL|LOG_ERRNO, "read of %s", p->data_fname);
390             exit (1);
391         }
392     }
393     rec->sysno = sysno;
394
395     nptr = p->tmp_buf;
396     for (i = 0; i < REC_NO_INFO; i++)
397     {
398         memcpy (&rec->size[i], nptr, sizeof(*rec->size));
399         nptr += sizeof(*rec->size);
400         if (rec->size[i])
401         {
402             rec->info[i] = malloc (rec->size[i]);
403             memcpy (rec->info[i], nptr, rec->size[i]);
404             nptr += rec->size[i];
405         }
406         else
407             rec->info[i] = NULL;
408     }
409     rec_cache_insert (p, rec, 0);
410     return rec;
411 }
412
413 Record rec_new (Records p)
414 {
415     int sysno, i;
416     Record rec;
417
418     assert (p);
419     if (!(rec = malloc (sizeof(*rec))))
420     {
421         logf (LOG_FATAL|LOG_ERRNO, "malloc");
422         exit (1);
423     }
424     if (p->head.index_free == 0)
425         sysno = (p->head.index_last)++;
426     else
427     {
428         struct record_index_entry entry;
429
430         read_indx (p, p->head.index_free, &entry, sizeof(entry));
431         sysno = p->head.index_free;
432         p->head.index_free = entry.u.free.next;
433     }
434     (p->head.no_records)++;
435     rec->sysno = sysno;
436     for (i = 0; i < REC_NO_INFO; i++)
437     {
438         rec->info[i] = NULL;
439         rec->size[i] = 0;
440     }
441     rec_cache_insert (p, rec, 1);
442     return rec;
443 }
444
445 void rec_put (Records p, Record *recpp)
446 {
447     Record *recp;
448
449     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, 1)))
450     {
451         rec_rm (recp);
452         *recp = *recpp;
453     }
454     else
455     {
456         rec_cache_insert (p, *recpp, 1);
457         rec_rm (recpp);
458     }
459     *recpp = NULL;
460 }
461
462 void rec_rm (Record *recpp)
463 {
464     int i;
465     for (i = 0; i < REC_NO_INFO; i++)
466         free ((*recpp)->info[i]);
467     free (*recpp);
468     *recpp = NULL;
469 }
470
471 Record rec_cp (Record rec)
472 {
473     Record n;
474     int i;
475
476     if (!(n = malloc (sizeof(*n))))
477     {
478         logf (LOG_FATAL|LOG_ERRNO, "malloc");
479         exit (1);
480     }
481     n->sysno = rec->sysno;
482     for (i = 0; i < REC_NO_INFO; i++)
483         if (!rec->info[i])
484         {
485             n->info[i] = NULL;
486             n->size[i] = 0;
487         }
488         else
489         {
490             n->size[i] = rec->size[i];
491             if (!(n->info[i] = malloc (rec->size[i])))
492             {
493                 logf (LOG_FATAL|LOG_ERRNO, "malloc. rec_cp");
494                 exit (1);
495             }
496             memcpy (n->info[i], rec->info[i], rec->size[i]);
497         }
498     return n;
499 }