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