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