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