- fix memory leaks
[platform/upstream/libsolv.git] / src / repodata.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repodata.c
10  *
11  * Manage data coming from one repository
12  * 
13  */
14
15 #define _GNU_SOURCE
16 #include <string.h>
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <assert.h>
22
23 #include "repo.h"
24 #include "pool.h"
25 #include "poolid_private.h"
26 #include "util.h"
27
28 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
29                                   unsigned char *out, unsigned int out_len);
30 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
31                                               unsigned int in_len,
32                                               unsigned char *out,
33                                               unsigned int out_len);
34
35 #define REPODATA_BLOCK 255
36
37 void
38 repodata_free(Repodata *data)
39 {
40   sat_free(data->keys);
41   sat_free(data->schemata);
42   sat_free(data->schemadata);
43
44   sat_free(data->spool.strings);
45   sat_free(data->spool.stringspace);
46   sat_free(data->spool.stringhashtbl);
47
48   sat_free(data->dirpool.dirs);
49   sat_free(data->dirpool.dirtraverse);
50
51   sat_free(data->incoredata);
52   sat_free(data->incoreoffset);
53   sat_free(data->verticaloffset);
54
55   sat_free(data->blob_store);
56   sat_free(data->pages);
57   sat_free(data->mapped);
58
59   sat_free(data->vincore);
60
61   sat_free(data->attrs);
62   sat_free(data->attrdata);
63   sat_free(data->attriddata);
64   
65   if (data->fp)
66     fclose(data->fp);
67 }
68
69 static unsigned char *
70 data_read_id(unsigned char *dp, Id *idp)
71 {
72   Id x = 0;
73   unsigned char c;
74   for (;;)
75     {
76       c = *dp++;
77       if (!(c & 0x80))
78         {
79           *idp = (x << 7) ^ c;
80           return dp;
81         }
82       x = (x << 7) ^ c ^ 128;
83     }
84 }
85
86 static unsigned char *
87 data_read_ideof(unsigned char *dp, Id *idp, int *eof)
88 {
89   Id x = 0;
90   unsigned char c;
91   for (;;)
92     {
93       c = *dp++;
94       if (!(c & 0x80))
95         {
96           if (c & 0x40)
97             {
98               c ^= 0x40;
99               *eof = 0;
100             }
101           else
102             *eof = 1;
103           *idp = (x << 6) ^ c;
104           return dp;
105         }
106       x = (x << 7) ^ c ^ 128;
107     }
108 }
109
110 static unsigned char *
111 data_skip(unsigned char *dp, int type)
112 {
113   unsigned char x;
114   switch (type)
115     {
116     case TYPE_VOID:
117     case TYPE_CONSTANT:
118       return dp;
119     case TYPE_ID:
120     case TYPE_NUM:
121     case TYPE_DIR:
122       while ((*dp & 0x80) != 0)
123         dp++;
124       return dp + 1;
125     case TYPE_IDARRAY:
126       while ((*dp & 0xc0) != 0)
127         dp++;
128       return dp + 1;
129     case TYPE_STR:
130       while ((*dp) != 0)
131         dp++;
132       return dp + 1;
133     case TYPE_DIRSTRARRAY:
134       for (;;)
135         {
136           while ((*dp & 0x80) != 0)
137             dp++;
138           x = *dp++;
139           while ((*dp) != 0)
140             dp++;
141           dp++;
142           if (!(x & 0x40))
143             return dp;
144         }
145     case TYPE_DIRNUMNUMARRAY:
146       for (;;)
147         {
148           while ((*dp & 0x80) != 0)
149             dp++;
150           dp++;
151           while ((*dp & 0x80) != 0)
152             dp++;
153           dp++;
154           while ((*dp & 0x80) != 0)
155             dp++;
156           if (!(*dp & 0x40))
157             return dp + 1;
158           dp++;
159         }
160     default:
161       fprintf(stderr, "unknown type in data_skip\n");
162       exit(1);
163     }
164 }
165
166 static unsigned char *
167 data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
168 {
169   kv->eof = 1;
170   if (!dp)
171     return 0;
172   switch (key->type)
173     {
174     case TYPE_VOID:
175       return dp;
176     case TYPE_CONSTANT:
177       kv->num = key->size;
178       return dp;
179     case TYPE_STR:
180       kv->str = (const char *)dp;
181       return dp + strlen(kv->str) + 1;
182     case TYPE_ID:
183       return data_read_id(dp, &kv->id);
184     case TYPE_NUM:
185       return data_read_id(dp, &kv->num);
186     case TYPE_IDARRAY:
187       return data_read_ideof(dp, &kv->id, &kv->eof);
188     case TYPE_DIR:
189       return data_read_id(dp, &kv->id);
190     case TYPE_DIRSTRARRAY:
191       dp = data_read_ideof(dp, &kv->id, &kv->eof);
192       kv->str = (const char *)dp;
193       return dp + strlen(kv->str) + 1;
194     case TYPE_DIRNUMNUMARRAY:
195       dp = data_read_id(dp, &kv->id);
196       dp = data_read_id(dp, &kv->num);
197       return data_read_ideof(dp, &kv->num2, &kv->eof);
198     default:
199       return 0;
200     }
201 }
202
203 static unsigned char *
204 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
205 {
206   Id k, *keyp;
207
208   keyp = data->schemadata + data->schemata[schemaid];
209   while ((k = *keyp++) != 0)
210     {
211       if (k == key)
212         return dp;
213       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
214         {
215           dp = data_skip(dp, TYPE_ID);  /* skip that offset */
216           dp = data_skip(dp, TYPE_ID);  /* skip that length */
217           continue;
218         }
219       if (data->keys[k].storage != KEY_STORAGE_INCORE)
220         continue;
221       dp = data_skip(dp, data->keys[k].type);
222     }
223   return 0;
224 }
225
226 #define BLOB_PAGEBITS 15
227 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
228
229 static unsigned char *
230 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
231 {
232 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
233    and are consecutive.  Return a pointer to the mapping of PSTART.  */
234   unsigned char buf[BLOB_PAGESIZE];
235   unsigned int i;
236
237   /* Quick check in case all pages are there already and consecutive.  */
238   for (i = pstart; i <= pend; i++)
239     if (data->pages[i].mapped_at == -1
240         || (i > pstart
241             && data->pages[i].mapped_at
242                != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
243       break;
244   if (i > pend)
245     return data->blob_store + data->pages[pstart].mapped_at;
246
247   /* Ensure that we can map the numbers of pages we need at all.  */
248   if (pend - pstart + 1 > data->ncanmap)
249     {
250       unsigned int oldcan = data->ncanmap;
251       data->ncanmap = pend - pstart + 1;
252       if (data->ncanmap < 4)
253         data->ncanmap = 4;
254       data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
255       memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
256       data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
257 #ifdef DEBUG_PAGING
258       fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
259 #endif
260     }
261
262   /* Now search for "cheap" space in our store.  Space is cheap if it's either
263      free (very cheap) or contains pages we search for anyway.  */
264
265   /* Setup cost array.  */
266   unsigned int cost[data->ncanmap];
267   for (i = 0; i < data->ncanmap; i++)
268     {
269       unsigned int pnum = data->mapped[i];
270       if (pnum == 0)
271         cost[i] = 0;
272       else
273         {
274           pnum--;
275           Attrblobpage *p = data->pages + pnum;
276           assert (p->mapped_at != -1);
277           if (pnum >= pstart && pnum <= pend)
278             cost[i] = 1;
279           else
280             cost[i] = 3;
281         }
282     }
283
284   /* And search for cheapest space.  */
285   unsigned int best_cost = -1;
286   unsigned int best = 0;
287   unsigned int same_cost = 0;
288   for (i = 0; i + pend - pstart < data->ncanmap; i++)
289     {
290       unsigned int c = cost[i];
291       unsigned int j;
292       for (j = 0; j < pend - pstart + 1; j++)
293         c += cost[i+j];
294       if (c < best_cost)
295         best_cost = c, best = i;
296       else if (c == best_cost)
297         same_cost++;
298       /* A null cost won't become better.  */
299       if (c == 0)
300         break;
301     }
302   /* If all places have the same cost we would thrash on slot 0.  Avoid
303      this by doing a round-robin strategy in this case.  */
304   if (same_cost == data->ncanmap - pend + pstart - 1)
305     best = data->rr_counter++ % (data->ncanmap - pend + pstart);
306
307   /* So we want to map our pages from [best] to [best+pend-pstart].
308      Use a very simple strategy, which doesn't make the best use of
309      our resources, but works.  Throw away all pages in that range
310      (even ours) then copy around ours (in case they were outside the 
311      range) or read them in.  */
312   for (i = best; i < best + pend - pstart + 1; i++)
313     {
314       unsigned int pnum = data->mapped[i];
315       if (pnum--
316           /* If this page is exactly at the right place already,
317              no need to evict it.  */
318           && pnum != pstart + i - best)
319         {
320           /* Evict this page.  */
321 #ifdef DEBUG_PAGING
322           fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
323 #endif
324           cost[i] = 0;
325           data->mapped[i] = 0;
326           data->pages[pnum].mapped_at = -1;
327         }
328     }
329
330   /* Everything is free now.  Read in the pages we want.  */
331   for (i = pstart; i <= pend; i++)
332     {
333       Attrblobpage *p = data->pages + i;
334       unsigned int pnum = i - pstart + best;
335       void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
336       if (p->mapped_at != -1)
337         {
338           if (p->mapped_at != pnum * BLOB_PAGESIZE)
339             {
340 #ifdef DEBUG_PAGING
341               fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
342 #endif
343               /* Still mapped somewhere else, so just copy it from there.  */
344               memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
345               data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
346             }
347         }
348       else
349         {
350           unsigned int in_len = p->file_size;
351           unsigned int compressed = in_len & 1;
352           in_len >>= 1;
353 #ifdef DEBUG_PAGING
354           fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
355 #endif
356           /* Not mapped, so read in this page.  */
357           if (fseek(data->fp, p->file_offset, SEEK_SET) < 0)
358             {
359               perror ("mapping fseek");
360               exit (1);
361             }
362           if (fread(compressed ? buf : dest, in_len, 1, data->fp) != 1)
363             {
364               perror ("mapping fread");
365               exit (1);
366             }
367           if (compressed)
368             {
369               unsigned int out_len;
370               out_len = unchecked_decompress_buf(buf, in_len,
371                                                   dest, BLOB_PAGESIZE);
372               if (out_len != BLOB_PAGESIZE
373                   && i < data->num_pages - 1)
374                 {
375                   fprintf (stderr, "can't decompress\n");
376                   exit (1);
377                 }
378 #ifdef DEBUG_PAGING
379               fprintf (stderr, " (expand %d to %d)", in_len, out_len);
380 #endif
381             }
382 #ifdef DEBUG_PAGING
383           fprintf (stderr, "\n");
384 #endif
385         }
386       p->mapped_at = pnum * BLOB_PAGESIZE;
387       data->mapped[pnum] = i + 1;
388     }
389   return data->blob_store + best * BLOB_PAGESIZE;
390 }
391
392 static unsigned char *
393 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
394 {
395   unsigned char *dp;
396   if (key->type == TYPE_VOID)
397     return 0;
398   if (off >= data->lastverticaloffset)
399     {
400       off -= data->lastverticaloffset;
401       if (off + len > data->vincorelen)
402         return 0;
403       return data->vincore + off;
404     }
405   if (!data->fp)
406     return 0;
407   if (off + len > key->size)
408     return 0;
409   /* we now have the offset, go into vertical */
410   off += data->verticaloffset[key - data->keys];
411   dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
412   if (dp)
413     dp += off % BLOB_PAGESIZE;
414   return dp;
415 }
416
417 static inline unsigned char *
418 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
419 {
420   unsigned char *dp = *dpp;
421
422   if (!dp)
423     return 0;
424   if (key->storage == KEY_STORAGE_INCORE)
425     {
426       /* hmm, this is a bit expensive */
427       *dpp = data_skip(dp, key->type);
428       return dp;
429     }
430   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
431     {
432       Id off, len;
433       dp = data_read_id(dp, &off);
434       dp = data_read_id(dp, &len);
435       *dpp = dp;
436       return make_vertical_available(data, key, off, len);
437     }
438   return 0;
439 }
440
441 static inline int
442 maybe_load_repodata(Repodata *data)
443 {
444   if (data->state == REPODATA_STUB)
445     {
446       if (data->loadcallback)
447         data->loadcallback(data);
448       else
449         data->state = REPODATA_ERROR;
450     }
451   if (data->state == REPODATA_AVAILABLE)
452     return 1;
453   data->state = REPODATA_ERROR;
454   return 0;
455 }
456
457 const char *
458 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
459 {
460   Id schema;
461   Repokey *key;
462   Id id, *keyp;
463   unsigned char *dp;
464
465   if (!maybe_load_repodata (data))
466     return 0;
467
468   dp = data->incoredata + data->incoreoffset[entry];
469   dp = data_read_id(dp, &schema);
470   /* make sure the schema of this solvable contains the key */
471   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
472     if (!*keyp)
473       return 0;
474   dp = forward_to_key(data, keyid, schema, dp);
475   key = data->keys + keyid;
476   dp = get_data(data, key, &dp);
477   if (!dp)
478     return 0;
479   if (key->type == TYPE_STR)
480     return (const char *)dp;
481   if (key->type != TYPE_ID)
482     return 0;
483   /* id type, must either use global or local string store*/
484   dp = data_read_id(dp, &id);
485   if (data->localpool)
486     return data->spool.stringspace + data->spool.strings[id];
487   return id2str(data->repo->pool, id);
488 }
489
490 int
491 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
492 {
493   Id schema;
494   Repokey *key;
495   Id *keyp;
496   KeyValue kv;
497   unsigned char *dp;
498
499   *value = 0;
500
501   if (!maybe_load_repodata (data))
502     return 0;
503
504   dp = data->incoredata + data->incoreoffset[entry];
505   dp = data_read_id(dp, &schema);
506   /* make sure the schema of this solvable contains the key */
507   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
508     if (!*keyp)
509       return 0;
510   dp = forward_to_key(data, keyid, schema, dp);
511   key = data->keys + keyid;
512   dp = get_data(data, key, &dp);
513   if (!dp)
514     return 0;
515   if (key->type == TYPE_NUM
516       || key->type == TYPE_U32
517       || key->type == TYPE_CONSTANT)
518     {
519       dp = data_fetch(dp, &kv, key);
520       *value = kv.num;
521       return 1;
522     }
523   return 0;
524 }
525
526 void
527 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
528 {
529   Id schema;
530   Repokey *key;
531   Id k, keyid, *kp, *keyp;
532   unsigned char *dp, *ddp;
533   int onekey = 0;
534   int stop;
535   KeyValue kv;
536
537   if (!maybe_load_repodata (data))
538     return;
539
540   dp = data->incoredata + data->incoreoffset[entry];
541   dp = data_read_id(dp, &schema);
542   keyp = data->schemadata + data->schemata[schema];
543   if (keyname)
544     {
545       /* search in a specific key */
546       for (kp = keyp; (k = *kp++) != 0; )
547         if (data->keys[k].name == keyname)
548           break;
549       if (k == 0)
550         return;
551       dp = forward_to_key(data, k, schema, dp);
552       if (!dp)
553         return;
554       keyp = kp - 1;
555       onekey = 1;
556     }
557   while ((keyid = *keyp++) != 0)
558     {
559       stop = 0;
560       key = data->keys + keyid;
561       ddp = get_data(data, key, &dp);
562       do
563         {
564           ddp = data_fetch(ddp, &kv, key);
565           if (!ddp)
566             break;
567           stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
568         }
569       while (!kv.eof && !stop);
570       if (onekey || stop > SEARCH_NEXT_KEY)
571         return;
572     }
573 }
574
575 void
576 repodata_init(Repodata *data, Repo *repo, int localpool)
577 {
578   memset(data, 0, sizeof (*data));
579   data->repo = repo;
580   data->localpool = localpool;
581   if (localpool)
582     stringpool_init_empty(&data->spool);
583   data->keys = sat_calloc(1, sizeof(Repokey));
584   data->nkeys = 1;
585   data->schemata = sat_calloc(1, sizeof(Id));
586   data->schemadata = sat_calloc(1, sizeof(Id));
587   data->nschemata = 1;
588   data->schemadatalen = 1;
589   data->start = repo->start;
590   data->end = repo->end;
591   data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
592 }
593
594 /* extend repodata so that it includes solvables p */
595 void
596 repodata_extend(Repodata *data, Id p)
597 {
598   if (data->start == data->end)
599     data->start = data->end = p;
600   if (p >= data->end)
601     {
602       int old = data->end - data->start;
603       int new = p - data->end + 1;
604       if (data->attrs)
605         {
606           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
607           memset(data->attrs + old, 0, new * sizeof(Id *));
608         }
609       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
610       memset(data->incoreoffset + old, 0, new * sizeof(Id));
611       data->end = p + 1;
612     }
613   if (p < data->start)
614     {
615       int old = data->end - data->start;
616       int new = data->start - p;
617       if (data->attrs)
618         {
619           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
620           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
621           memset(data->attrs, 0, new * sizeof(Id *));
622         }
623       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
624       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
625       memset(data->incoreoffset, 0, new * sizeof(Id));
626       data->start = p;
627     }
628 }
629
630 void
631 repodata_extend_block(Repodata *data, Id start, Id num)
632 {
633   if (!num)
634     return;
635   if (!data->incoreoffset)
636     {
637       data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
638       memset(data->incoreoffset, 0, num * sizeof(Id));
639       data->start = start;
640       data->end = start + num;
641       return;
642     }
643   repodata_extend(data, start);
644   if (num > 1)
645     repodata_extend(data, start + num - 1);
646 }
647
648 #define REPODATA_ATTRS_BLOCK 63
649 #define REPODATA_ATTRDATA_BLOCK 1023
650 #define REPODATA_ATTRIDDATA_BLOCK 63
651
652 static void
653 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
654 {
655   Id *pp;
656   int i;
657   if (!data->attrs)
658     {
659       data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
660       memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
661     }
662   i = 0;
663   if (data->attrs[entry])
664     {
665       for (pp = data->attrs[entry]; *pp; pp += 2)
666         /* Determine equality based on the name only, allows us to change
667            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
668         if (data->keys[*pp].name == data->keys[keyid].name)
669           break;
670       if (*pp)
671         {
672           if (overwrite)
673             {
674               pp[0] = keyid;
675               pp[1] = val;
676             }
677           return;
678         }
679       i = pp - data->attrs[entry];
680     }
681   data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
682   pp = data->attrs[entry] + i;
683   *pp++ = keyid;
684   *pp++ = val;
685   *pp = 0;
686 }
687
688 void
689 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
690 {
691   Id keyid;
692
693   /* find key in keys */
694   for (keyid = 1; keyid < data->nkeys; keyid++)
695     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
696       {
697         if (key->type == TYPE_CONSTANT && key->size != data->keys[keyid].size)
698           continue;
699         break;
700       }
701   if (keyid == data->nkeys)
702     {
703       /* allocate new key */
704       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
705       data->keys[data->nkeys++] = *key;
706       if (data->verticaloffset)
707         {
708           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
709           data->verticaloffset[data->nkeys - 1] = 0;
710         }
711     }
712   repodata_insert_keyid(data, entry, keyid, val, 1);
713 }
714
715 void
716 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
717 {
718   Repokey key;
719   key.name = keyname;
720   key.type = TYPE_ID;
721   key.size = 0;
722   key.storage = KEY_STORAGE_INCORE;
723   repodata_set(data, entry, &key, id);
724 }
725
726 void
727 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
728 {
729   Repokey key;
730   key.name = keyname;
731   key.type = TYPE_NUM;
732   key.size = 0;
733   key.storage = KEY_STORAGE_INCORE;
734   repodata_set(data, entry, &key, num);
735 }
736
737 void
738 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
739 {
740   Repokey key;
741   Id id;
742   if (data->localpool)
743     id = stringpool_str2id(&data->spool, str, 1);
744   else
745     id = str2id(data->repo->pool, str, 1);
746   key.name = keyname;
747   key.type = TYPE_ID;
748   key.size = 0;
749   key.storage = KEY_STORAGE_INCORE;
750   repodata_set(data, entry, &key, id);
751 }
752
753 void
754 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
755 {
756   Repokey key;
757   key.name = keyname;
758   key.type = TYPE_CONSTANT;
759   key.size = constant;
760   key.storage = KEY_STORAGE_INCORE;
761   repodata_set(data, entry, &key, 0);
762 }
763
764 void
765 repodata_set_void(Repodata *data, Id entry, Id keyname)
766 {
767   Repokey key;
768   key.name = keyname;
769   key.type = TYPE_VOID;
770   key.size = 0;
771   key.storage = KEY_STORAGE_INCORE;
772   repodata_set(data, entry, &key, 0);
773 }
774
775 void
776 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
777 {
778   Repokey key;
779   int l;
780
781   l = strlen(str) + 1;
782   key.name = keyname;
783   key.type = TYPE_STR;
784   key.size = 0;
785   key.storage = KEY_STORAGE_INCORE;
786   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
787   memcpy(data->attrdata + data->attrdatalen, str, l);
788   repodata_set(data, entry, &key, data->attrdatalen);
789   data->attrdatalen += l;
790 }
791
792 void
793 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
794 {
795   Id *ida, *pp;
796   Repokey key;
797
798 #if 0
799 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
800 #endif
801   if (data->attrs && data->attrs[entry])
802     {
803       for (pp = data->attrs[entry]; *pp; pp += 2)
804         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRNUMNUMARRAY)
805           break;
806       if (*pp)
807         {
808           int oldsize = 0;
809           for (ida = data->attriddata + pp[1]; *ida; ida += 3)
810             oldsize += 3;
811           if (ida + 1 == data->attriddata + data->attriddatalen)
812             {
813               /* this was the last entry, just append it */
814               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
815               data->attriddatalen--;    /* overwrite terminating 0  */
816             }
817           else
818             {
819               /* too bad. move to back. */
820               data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
821               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
822               pp[1] = data->attriddatalen;
823               data->attriddatalen += oldsize;
824             }
825           data->attriddata[data->attriddatalen++] = dir;
826           data->attriddata[data->attriddatalen++] = num;
827           data->attriddata[data->attriddatalen++] = num2;
828           data->attriddata[data->attriddatalen++] = 0;
829           return;
830         }
831     }
832   key.name = keyname;
833   key.type = TYPE_DIRNUMNUMARRAY;
834   key.size = 0;
835   key.storage = KEY_STORAGE_INCORE;
836   data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
837   repodata_set(data, entry, &key, data->attriddatalen);
838   data->attriddata[data->attriddatalen++] = dir;
839   data->attriddata[data->attriddatalen++] = num;
840   data->attriddata[data->attriddatalen++] = num2;
841   data->attriddata[data->attriddatalen++] = 0;
842 }
843
844 void
845 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
846 {
847   Id *ida, *pp, stroff;
848   Repokey key;
849   int l;
850
851   l = strlen(str) + 1;
852   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
853   memcpy(data->attrdata + data->attrdatalen, str, l);
854   stroff = data->attrdatalen;
855   data->attrdatalen += l;
856
857 #if 0
858 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
859 #endif
860   if (data->attrs && data->attrs[entry])
861     {
862       for (pp = data->attrs[entry]; *pp; pp += 2)
863         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRSTRARRAY)
864           break;
865       if (*pp)
866         {
867           int oldsize = 0;
868           for (ida = data->attriddata + pp[1]; *ida; ida += 2)
869             oldsize += 2;
870           if (ida + 1 == data->attriddata + data->attriddatalen)
871             {
872               /* this was the last entry, just append it */
873               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
874               data->attriddatalen--;    /* overwrite terminating 0  */
875             }
876           else
877             {
878               /* too bad. move to back. */
879               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
880               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
881               pp[1] = data->attriddatalen;
882               data->attriddatalen += oldsize;
883             }
884           data->attriddata[data->attriddatalen++] = dir;
885           data->attriddata[data->attriddatalen++] = stroff;
886           data->attriddata[data->attriddatalen++] = 0;
887           return;
888         }
889     }
890   key.name = keyname;
891   key.type = TYPE_DIRSTRARRAY;
892   key.size = 0;
893   key.storage = KEY_STORAGE_INCORE;
894   data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
895   repodata_set(data, entry, &key, data->attriddatalen);
896   data->attriddata[data->attriddatalen++] = dir;
897   data->attriddata[data->attriddatalen++] = stroff;
898   data->attriddata[data->attriddatalen++] = 0;
899 }
900
901 void
902 repodata_merge_attrs (Repodata *data, Id dest, Id src)
903 {
904   Id *keyp;
905   for (keyp = data->attrs[src]; *keyp; keyp += 2)
906     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
907 }
908
909 /*********************************/
910
911 /* unify with repo_write! */
912
913 #define EXTDATA_BLOCK 1023
914 #define SCHEMATA_BLOCK 31
915 #define SCHEMATADATA_BLOCK 255
916
917 struct extdata {
918   unsigned char *buf;
919   int len;
920 };
921
922 static void
923 data_addid(struct extdata *xd, Id x)
924 {
925   unsigned char *dp;
926   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
927   dp = xd->buf + xd->len;
928
929   if (x >= (1 << 14))
930     {
931       if (x >= (1 << 28))
932         *dp++ = (x >> 28) | 128;
933       if (x >= (1 << 21))
934         *dp++ = (x >> 21) | 128;
935       *dp++ = (x >> 14) | 128;
936     }
937   if (x >= (1 << 7))
938     *dp++ = (x >> 7) | 128;
939   *dp++ = x & 127;
940   xd->len = dp - xd->buf;
941 }
942
943 static void
944 data_addideof(struct extdata *xd, Id x, int eof)
945 {
946   if (x >= 64)
947     x = (x & 63) | ((x & ~63) << 1);
948   data_addid(xd, (eof ? x: x | 64));
949 }
950
951 static void
952 data_addblob(struct extdata *xd, unsigned char *blob, int len)
953 {
954   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
955   memcpy(xd->buf + xd->len, blob, len);
956   xd->len += len;
957 }
958
959 /*********************************/
960
961 static void
962 addschema_prepare(Repodata *data, Id *schematacache)
963 {
964   int h, len, i;
965   Id *sp;
966
967   memset(schematacache, 0, 256 * sizeof(Id));
968   for (i = 0; i < data->nschemata; i++)
969     {
970       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
971         h = h * 7 + *sp++;
972       h &= 255;
973       schematacache[h] = i + 1;
974     }
975   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
976   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
977 }
978
979 static Id
980 addschema(Repodata *data, Id *schema, Id *schematacache)
981 {
982   int h, len; 
983   Id *sp, cid; 
984
985   for (sp = schema, len = 0, h = 0; *sp; len++)
986     h = h * 7 + *sp++;
987   h &= 255; 
988   len++;
989
990   cid = schematacache[h];
991   if (cid)
992     {    
993       cid--;
994       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
995         return cid;
996       /* cache conflict */
997       for (cid = 0; cid < data->nschemata; cid++)
998         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
999           return cid;
1000     }
1001   /* a new one. make room. */
1002   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1003   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1004   /* add schema */
1005   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1006   data->schemata[data->nschemata] = data->schemadatalen;
1007   data->schemadatalen += len;
1008   schematacache[h] = data->nschemata + 1;
1009 #if 0
1010 fprintf(stderr, "addschema: new schema\n");
1011 #endif
1012   return data->nschemata++; 
1013 }
1014
1015
1016 void
1017 repodata_internalize(Repodata *data)
1018 {
1019   Repokey *key;
1020   Id id, entry, nentry, *ida;
1021   Id schematacache[256];
1022   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1023   unsigned char *dp, *ndp;
1024   int newschema, oldcount;
1025   struct extdata newincore;
1026   struct extdata newvincore;
1027
1028   if (!data->attrs)
1029     return;
1030
1031   newvincore.buf = data->vincore;
1032   newvincore.len = data->vincorelen;
1033
1034   schema = sat_malloc2(data->nkeys, sizeof(Id));
1035   seen = sat_malloc2(data->nkeys, sizeof(Id));
1036
1037   /* Merge the data already existing (in data->schemata, ->incoredata and
1038      friends) with the new attributes in data->attrs[].  */
1039   nentry = data->end - data->start;
1040   addschema_prepare(data, schematacache);
1041   memset(&newincore, 0, sizeof(newincore));
1042   for (entry = 0; entry < nentry; entry++)
1043     {
1044       memset(seen, 0, data->nkeys * sizeof(Id));
1045       sp = schema;
1046       dp = data->incoredata + data->incoreoffset[entry];
1047       if (data->incoredata)
1048         dp = data_read_id(dp, &oldschema);
1049       else
1050         oldschema = 0;
1051 #if 0
1052 fprintf(stderr, "oldschema %d\n", oldschema);
1053 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1054 fprintf(stderr, "schemadata %p\n", data->schemadata);
1055 #endif
1056       /* seen: -1: old data  0: skipped  >0: id + 1 */
1057       newschema = 0;
1058       oldcount = 0;
1059       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1060         {
1061           if (seen[*keyp])
1062             {
1063               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1064               exit(1);
1065             }
1066           seen[*keyp] = -1;
1067           *sp++ = *keyp;
1068           oldcount++;
1069         }
1070       if (data->attrs[entry])
1071         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1072           {
1073             if (!seen[*keyp])
1074               {
1075                 newschema = 1;
1076                 *sp++ = *keyp;
1077               }
1078             seen[*keyp] = keyp[1] + 1;
1079           }
1080       *sp++ = 0;
1081       if (newschema)
1082         /* Ideally we'd like to sort the new schema here, to ensure
1083            schema equality independend of the ordering.  We can't do that
1084            yet.  For once see below (old ids need to come before new ids).
1085            An additional difficulty is that we also need to move
1086            the values with the keys.  */
1087         schemaid = addschema(data, schema, schematacache);
1088       else
1089         schemaid = oldschema;
1090
1091
1092       /* Now create data blob.  We walk through the (possibly new) schema
1093          and either copy over old data, or insert the new.  */
1094       /* XXX Here we rely on the fact that the (new) schema has the form
1095          o1 o2 o3 o4 ... | n1 n2 n3 ...
1096          (oX being the old keyids (possibly overwritten), and nX being
1097           the new keyids).  This rules out sorting the keyids in order
1098          to ensure a small schema count.  */
1099       data->incoreoffset[entry] = newincore.len;
1100       data_addid(&newincore, schemaid);
1101       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1102         {
1103           key = data->keys + *keyp;
1104           ndp = dp;
1105           if (oldcount)
1106             {
1107               /* Skip the data associated with this old key.  */
1108               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1109                 {
1110                   ndp = data_skip(dp, TYPE_ID);
1111                   ndp = data_skip(ndp, TYPE_ID);
1112                 }
1113               else if (key->storage == KEY_STORAGE_INCORE)
1114                 ndp = data_skip(dp, key->type);
1115               oldcount--;
1116             }
1117           if (seen[*keyp] == -1)
1118             {
1119               /* If this key was an old one _and_ was not overwritten with
1120                  a different value copy over the old value (we skipped it
1121                  above).  */
1122               if (dp != ndp)
1123                 data_addblob(&newincore, dp, ndp - dp);
1124               seen[*keyp] = 0;
1125             }
1126           else if (seen[*keyp])
1127             {
1128               /* Otherwise we have a new value.  Parse it into the internal
1129                  form.  */
1130               struct extdata *xd;
1131               unsigned int oldvincorelen = 0;
1132
1133               xd = &newincore;
1134               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1135                 {
1136                   xd = &newvincore;
1137                   oldvincorelen = xd->len;
1138                 }
1139               id = seen[*keyp] - 1;
1140               switch (key->type)
1141                 {
1142                 case TYPE_VOID:
1143                 case TYPE_CONSTANT:
1144                   break;
1145                 case TYPE_STR:
1146                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1147                   break;
1148                 case TYPE_ID:
1149                 case TYPE_NUM:
1150                 case TYPE_DIR:
1151                   data_addid(xd, id);
1152                   break;
1153                 case TYPE_DIRNUMNUMARRAY:
1154                   for (ida = data->attriddata + id; *ida; ida += 3)
1155                     {
1156                       data_addid(xd, ida[0]);
1157                       data_addid(xd, ida[1]);
1158                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1159                     }
1160                   break;
1161                 case TYPE_DIRSTRARRAY:
1162                   for (ida = data->attriddata + id; *ida; ida += 2)
1163                     {
1164                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1165                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1166                     }
1167                   break;
1168                 default:
1169                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1170                   exit(1);
1171                 }
1172               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1173                 {
1174                   /* put offset/len in incore */
1175                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1176                   oldvincorelen = xd->len - oldvincorelen;
1177                   data_addid(&newincore, oldvincorelen);
1178                 }
1179             }
1180           dp = ndp;
1181         }
1182       if (data->attrs[entry])
1183         sat_free(data->attrs[entry]);
1184     }
1185   sat_free(schema);
1186   sat_free(seen);
1187
1188   sat_free(data->incoredata);
1189   data->incoredata = newincore.buf;
1190   data->incoredatalen = newincore.len;
1191   data->incoredatafree = 0;
1192   
1193   sat_free(data->vincore);
1194   data->vincore = newvincore.buf;
1195   data->vincorelen = newvincore.len;
1196
1197   data->attrs = sat_free(data->attrs);
1198   data->attrdata = sat_free(data->attrdata);
1199   data->attriddata = sat_free(data->attriddata);
1200   data->attrdatalen = 0;
1201   data->attriddatalen = 0;
1202 }
1203
1204 Id
1205 repodata_str2dir(Repodata *data, const char *dir, int create)
1206 {
1207   Id id, parent;
1208   const char *dire;
1209
1210   parent = 0;
1211   while (*dir == '/' && dir[1] == '/')
1212     dir++;
1213   while (*dir)
1214     {
1215       dire = strchrnul(dir, '/');
1216       if (data->localpool)
1217         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1218       else
1219         id = strn2id(data->repo->pool, dir, dire - dir, create);
1220       if (!id)
1221         return 0;
1222       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1223       if (!parent)
1224         return 0;
1225       if (!*dire)
1226         break;
1227       dir = dire + 1;
1228       while (*dir == '/')
1229         dir++;
1230     }
1231   return parent;
1232 }
1233
1234 unsigned int
1235 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1236 {
1237   return compress_buf(page, len, cpage, max);
1238 }
1239
1240 #define SOLV_ERROR_EOF              3
1241
1242 static inline unsigned int
1243 read_u32(FILE *fp)
1244 {
1245   int c, i;
1246   unsigned int x = 0; 
1247
1248   for (i = 0; i < 4; i++) 
1249     {    
1250       c = getc(fp);
1251       if (c == EOF) 
1252         return 0;
1253       x = (x << 8) | c; 
1254     }    
1255   return x;
1256 }
1257
1258 /* Try to either setup on-demand paging (using FP as backing
1259    file), or in case that doesn't work (FP not seekable) slurps in
1260    all pages and deactivates paging.  */
1261
1262 void
1263 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1264 {
1265   FILE *fp = data->fp;
1266   unsigned int npages;
1267   unsigned int i;
1268   unsigned int can_seek;
1269   long cur_file_ofs;
1270   unsigned char buf[BLOB_PAGESIZE];
1271   if (pagesz != BLOB_PAGESIZE)
1272     {
1273       /* We could handle this by slurping in everything.  */
1274       fprintf (stderr, "non matching page size\n");
1275       exit (1);
1276     }
1277   can_seek = 1;
1278   if ((cur_file_ofs = ftell(fp)) < 0)
1279     can_seek = 0;
1280   clearerr (fp);
1281 #ifdef DEBUG_PAGING
1282   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1283 #endif
1284   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1285
1286   data->num_pages = npages;
1287   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1288
1289   /* If we can't seek on our input we have to slurp in everything.  */
1290   if (!can_seek)
1291     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1292   for (i = 0; i < npages; i++)
1293     {
1294       unsigned int in_len = read_u32(fp);
1295       unsigned int compressed = in_len & 1;
1296       Attrblobpage *p = data->pages + i;
1297       in_len >>= 1;
1298 #ifdef DEBUG_PAGING
1299       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1300                i, in_len, compressed ? "" : "not ");
1301 #endif
1302       if (can_seek)
1303         {
1304           cur_file_ofs += 4;
1305           p->mapped_at = -1;
1306           p->file_offset = cur_file_ofs;
1307           p->file_size = in_len * 2 + compressed;
1308           if (fseek(fp, in_len, SEEK_CUR) < 0)
1309             {
1310               perror ("fseek");
1311               fprintf (stderr, "can't seek after we thought we can\n");
1312               /* We can't fall back to non-seeking behaviour as we already
1313                  read over some data pages without storing them away.  */
1314               exit (1);
1315             }
1316           cur_file_ofs += in_len;
1317         }
1318       else
1319         {
1320           unsigned int out_len;
1321           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1322           p->mapped_at = i * BLOB_PAGESIZE;
1323           p->file_offset = 0;
1324           p->file_size = 0;
1325           /* We can't seek, so suck everything in.  */
1326           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1327             {
1328               perror ("fread");
1329               exit (1);
1330             }
1331           if (compressed)
1332             {
1333               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1334               if (out_len != BLOB_PAGESIZE
1335                   && i < npages - 1)
1336                 {
1337                   fprintf (stderr, "can't decompress\n");
1338                   exit (1);
1339                 }
1340             }
1341         }
1342     }
1343
1344   if (can_seek)
1345     {
1346       /* If we are here we were able to seek to all page
1347          positions, so activate paging by copying FP into our structure.
1348          We dup() the file, so that our callers can fclose() it and we
1349          still have it open.  But this means that we share file positions
1350          with the input filedesc.  So in case our caller reads it after us,
1351          and calls back into us we might change the file position unexpectedly
1352          to him.  */
1353       int fd = dup (fileno (fp));
1354       if (fd < 0)
1355         {
1356           /* Jeez!  What a bloody system, we can't dup() anymore.  */
1357           perror ("dup");
1358           exit (1);
1359         }
1360       /* XXX we don't close this yet anywhere.  */
1361       data->fp = fdopen (fd, "r");
1362       if (!data->fp)
1363         {
1364           /* My God!  What happened now?  */
1365           perror ("fdopen");
1366           exit (1);
1367         }
1368     }
1369 }