Support for generating separate sub files and bugfixes in the reader
[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 static inline int
403 maybe_load_repodata(Repodata *data)
404 {
405   if (data->state == REPODATA_STUB)
406     {
407       if (data->loadcallback)
408         data->loadcallback(data);
409       else
410         data->state = REPODATA_ERROR;
411     }
412   if (data->state == REPODATA_AVAILABLE)
413     return 1;
414   data->state = REPODATA_ERROR;
415   return 0;
416 }
417
418 const char *
419 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
420 {
421   Id schema;
422   Repokey *key;
423   Id id, *keyp;
424   unsigned char *dp;
425
426   if (!maybe_load_repodata (data))
427     return 0;
428
429   dp = data->incoredata + data->incoreoffset[entry];
430   dp = data_read_id(dp, &schema);
431   /* make sure the schema of this solvable contains the key */
432   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
433     if (!*keyp)
434       return 0;
435   dp = forward_to_key(data, keyid, schema, dp);
436   key = data->keys + keyid;
437   dp = get_data(data, key, &dp);
438   if (!dp)
439     return 0;
440   if (key->type == TYPE_STR)
441     return (const char *)dp;
442   if (key->type != TYPE_ID)
443     return 0;
444   /* id type, must either use global or local string store*/
445   dp = data_read_id(dp, &id);
446   if (data->localpool)
447     return data->spool.stringspace + data->spool.strings[id];
448   return id2str(data->repo->pool, id);
449 }
450
451 int
452 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
453 {
454   Id schema;
455   Repokey *key;
456   Id *keyp;
457   KeyValue kv;
458   unsigned char *dp;
459
460   *value = 0;
461
462   if (!maybe_load_repodata (data))
463     return 0;
464
465   dp = data->incoredata + data->incoreoffset[entry];
466   dp = data_read_id(dp, &schema);
467   /* make sure the schema of this solvable contains the key */
468   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
469     if (!*keyp)
470       return 0;
471   dp = forward_to_key(data, keyid, schema, dp);
472   key = data->keys + keyid;
473   dp = get_data(data, key, &dp);
474   if (!dp)
475     return 0;
476   if (key->type == TYPE_NUM
477       || key->type == TYPE_U32
478       || key->type == TYPE_CONSTANT)
479     {
480       dp = data_fetch(dp, &kv, key);
481       *value = kv.num;
482       return 1;
483     }
484   return 0;
485 }
486
487 void
488 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
489 {
490   Id schema;
491   Repokey *key;
492   Id k, keyid, *kp, *keyp;
493   unsigned char *dp, *ddp;
494   int onekey = 0;
495   int stop;
496   KeyValue kv;
497
498   if (!maybe_load_repodata (data))
499     return;
500
501   dp = data->incoredata + data->incoreoffset[entry];
502   dp = data_read_id(dp, &schema);
503   keyp = data->schemadata + data->schemata[schema];
504   if (keyname)
505     {
506       /* search in a specific key */
507       for (kp = keyp; (k = *kp++) != 0; )
508         if (data->keys[k].name == keyname)
509           break;
510       if (k == 0)
511         return;
512       dp = forward_to_key(data, k, schema, dp);
513       if (!dp)
514         return;
515       keyp = kp - 1;
516       onekey = 1;
517     }
518   while ((keyid = *keyp++) != 0)
519     {
520       stop = 0;
521       key = data->keys + keyid;
522       ddp = get_data(data, key, &dp);
523       do
524         {
525           ddp = data_fetch(ddp, &kv, key);
526           if (!ddp)
527             break;
528           stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
529         }
530       while (!kv.eof && !stop);
531       if (onekey || stop > SEARCH_NEXT_KEY)
532         return;
533     }
534 }
535
536
537 /* extend repodata so that it includes solvables p */
538 void
539 repodata_extend(Repodata *data, Id p)
540 {
541   if (data->start == data->end)
542     data->start = data->end = p;
543   if (p >= data->end)
544     {
545       int old = data->end - data->start;
546       int new = p - data->end + 1;
547       if (data->attrs)
548         {
549           data->attrs = sat_realloc2(data->attrs, old + new, sizeof(Id *));
550           memset(data->attrs + old, 0, new * sizeof(Id *));
551         }
552       data->incoreoffset = sat_realloc2(data->incoreoffset, old + new, sizeof(Id));
553       memset(data->incoreoffset + old, 0, new * sizeof(Id));
554       data->end = p + 1;
555     }
556   if (p < data->start)
557     {
558       int old = data->end - data->start;
559       int new = data->start - p;
560       if (data->attrs)
561         {
562           data->attrs = sat_realloc2(data->attrs, old + new, sizeof(Id *));
563           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
564           memset(data->attrs, 0, new * sizeof(Id *));
565         }
566       data->incoreoffset = sat_realloc2(data->incoreoffset, old + new, sizeof(Id));
567       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
568       memset(data->incoreoffset, 0, new * sizeof(Id));
569       data->start = p;
570     }
571 }
572
573 static void
574 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
575 {
576   Id *pp;
577   int i;
578   if (!data->attrs)
579     data->attrs = sat_calloc(data->end - data->start + 1, sizeof(Id *));
580   i = 0;
581   if (data->attrs[entry])
582     {
583       for (pp = data->attrs[entry]; *pp; pp += 2)
584         /* Determine equality based on the name only, allows us to change
585            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
586         if (data->keys[*pp].name == data->keys[keyid].name)
587           break;
588       if (*pp)
589         {
590           if (overwrite)
591             {
592               pp[0] = keyid;
593               pp[1] = val;
594             }
595           return;
596         }
597       i = pp - data->attrs[entry];
598     }
599   data->attrs[entry] = sat_realloc2(data->attrs[entry], i + 3, sizeof(Id));
600   pp = data->attrs[entry] + i;
601   *pp++ = keyid;
602   *pp++ = val;
603   *pp = 0;
604 }
605
606 void
607 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
608 {
609   Id keyid;
610
611   /* find key in keys */
612   for (keyid = 1; keyid < data->nkeys; keyid++)
613     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
614       {
615         if (key->type == TYPE_CONSTANT && key->size != data->keys[keyid].size)
616           continue;
617         break;
618       }
619   if (keyid == data->nkeys)
620     {
621       /* allocate new key */
622       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
623       data->keys[data->nkeys++] = *key;
624       if (data->verticaloffset)
625         {
626           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
627           data->verticaloffset[data->nkeys - 1] = 0;
628         }
629     }
630   repodata_insert_keyid(data, entry, keyid, val, 1);
631 }
632
633 void
634 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
635 {
636   Repokey key;
637   key.name = keyname;
638   key.type = TYPE_ID;
639   key.size = 0;
640   key.storage = KEY_STORAGE_INCORE;
641   repodata_set(data, entry, &key, id);
642 }
643
644 void
645 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
646 {
647   Repokey key;
648   key.name = keyname;
649   key.type = TYPE_NUM;
650   key.size = 0;
651   key.storage = KEY_STORAGE_INCORE;
652   repodata_set(data, entry, &key, num);
653 }
654
655 void
656 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
657 {
658   Repokey key;
659   Id id;
660   if (data->localpool)
661     id = stringpool_str2id(&data->spool, str, 1);
662   else
663     id = str2id(data->repo->pool, str, 1);
664   key.name = keyname;
665   key.type = TYPE_ID;
666   key.size = 0;
667   key.storage = KEY_STORAGE_INCORE;
668   repodata_set(data, entry, &key, id);
669 }
670
671 void
672 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
673 {
674   Repokey key;
675   key.name = keyname;
676   key.type = TYPE_CONSTANT;
677   key.size = constant;
678   key.storage = KEY_STORAGE_INCORE;
679   repodata_set(data, entry, &key, 0);
680 }
681
682 void
683 repodata_set_void(Repodata *data, Id entry, Id keyname)
684 {
685   Repokey key;
686   key.name = keyname;
687   key.type = TYPE_VOID;
688   key.size = 0;
689   key.storage = KEY_STORAGE_INCORE;
690   repodata_set(data, entry, &key, 0);
691 }
692
693 void
694 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
695 {
696   Repokey key;
697   int l;
698
699   l = strlen(str) + 1;
700   key.name = keyname;
701   key.type = TYPE_STR;
702   key.size = 0;
703   key.storage = KEY_STORAGE_INCORE;
704   data->attrdata = sat_realloc(data->attrdata, data->attrdatalen + l);
705   memcpy(data->attrdata + data->attrdatalen, str, l);
706   repodata_set(data, entry, &key, data->attrdatalen);
707   data->attrdatalen += l;
708 }
709
710 void
711 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
712 {
713   Id *ida, *pp;
714   Repokey key;
715
716 #if 0
717 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
718 #endif
719   if (data->attrs && data->attrs[entry])
720     {
721       for (pp = data->attrs[entry]; *pp; pp += 2)
722         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRNUMNUMARRAY)
723           break;
724       if (*pp)
725         {
726           int oldsize = 0;
727           for (ida = data->attriddata + pp[1]; *ida; ida += 3)
728             oldsize += 3;
729           if (ida + 1 == data->attriddata + data->attriddatalen)
730             {
731               /* this was the last entry, just append it */
732               data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 3, sizeof(Id));
733               data->attriddatalen--;    /* overwrite terminating 0  */
734             }
735           else
736             {
737               /* too bad. move to back. */
738               data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + oldsize + 4, sizeof(Id));
739               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
740               pp[1] = data->attriddatalen;
741               data->attriddatalen += oldsize;
742             }
743           data->attriddata[data->attriddatalen++] = dir;
744           data->attriddata[data->attriddatalen++] = num;
745           data->attriddata[data->attriddatalen++] = num2;
746           data->attriddata[data->attriddatalen++] = 0;
747           return;
748         }
749     }
750   key.name = keyname;
751   key.type = TYPE_DIRNUMNUMARRAY;
752   key.size = 0;
753   key.storage = KEY_STORAGE_INCORE;
754   data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 4, sizeof(Id));
755   repodata_set(data, entry, &key, data->attriddatalen);
756   data->attriddata[data->attriddatalen++] = dir;
757   data->attriddata[data->attriddatalen++] = num;
758   data->attriddata[data->attriddatalen++] = num2;
759   data->attriddata[data->attriddatalen++] = 0;
760 }
761
762 void
763 repodata_merge_attrs (Repodata *data, Id dest, Id src)
764 {
765   Id *keyp;
766   for (keyp = data->attrs[src]; *keyp; keyp += 2)
767     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
768 }
769
770 /*********************************/
771
772 /* unify with repo_write! */
773
774 #define EXTDATA_BLOCK 1023
775 #define SCHEMATA_BLOCK 31
776 #define SCHEMATADATA_BLOCK 255
777
778 struct extdata {
779   unsigned char *buf;
780   int len;
781 };
782
783 static void
784 data_addid(struct extdata *xd, Id x)
785 {
786   unsigned char *dp;
787   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
788   dp = xd->buf + xd->len;
789
790   if (x >= (1 << 14))
791     {
792       if (x >= (1 << 28))
793         *dp++ = (x >> 28) | 128;
794       if (x >= (1 << 21))
795         *dp++ = (x >> 21) | 128;
796       *dp++ = (x >> 14) | 128;
797     }
798   if (x >= (1 << 7))
799     *dp++ = (x >> 7) | 128;
800   *dp++ = x & 127;
801   xd->len = dp - xd->buf;
802 }
803
804 static void
805 data_addideof(struct extdata *xd, Id x, int eof)
806 {
807   if (x >= 64)
808     x = (x & 63) | ((x & ~63) << 1);
809   data_addid(xd, (eof ? x: x | 64));
810 }
811
812 static void
813 data_addblob(struct extdata *xd, unsigned char *blob, int len)
814 {
815   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
816   memcpy(xd->buf + xd->len, blob, len);
817   xd->len += len;
818 }
819
820 /*********************************/
821
822 static void
823 addschema_prepare(Repodata *data, Id *schematacache)
824 {
825   int h, len, i;
826   Id *sp;
827
828   memset(schematacache, 0, 256 * sizeof(Id));
829   for (i = 0; i < data->nschemata; i++)
830     {
831       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
832         h = h * 7 + *sp++;
833       h &= 255;
834       schematacache[h] = i + 1;
835     }
836   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
837   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
838 }
839
840 static Id
841 addschema(Repodata *data, Id *schema, Id *schematacache)
842 {
843   int h, len; 
844   Id *sp, cid; 
845
846   for (sp = schema, len = 0, h = 0; *sp; len++)
847     h = h * 7 + *sp++;
848   h &= 255; 
849   len++;
850
851   cid = schematacache[h];
852   if (cid)
853     {    
854       cid--;
855       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
856         return cid;
857       /* cache conflict */
858       for (cid = 0; cid < data->nschemata; cid++)
859         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
860           return cid;
861     }
862   /* a new one. make room. */
863   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
864   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
865   /* add schema */
866   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
867   data->schemata[data->nschemata] = data->schemadatalen;
868   data->schemadatalen += len;
869   schematacache[h] = data->nschemata + 1;
870 #if 0
871 fprintf(stderr, "addschema: new schema\n");
872 #endif
873   return data->nschemata++; 
874 }
875
876
877 void
878 repodata_internalize(Repodata *data)
879 {
880   Repokey *key;
881   Id id, entry, nentry, *ida;
882   Id schematacache[256];
883   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
884   unsigned char *dp, *ndp;
885   int newschema, oldcount;
886   struct extdata newincore;
887   struct extdata newvincore;
888
889   if (!data->attrs)
890     return;
891
892   newvincore.buf = data->vincore;
893   newvincore.len = data->vincorelen;
894
895   schema = sat_malloc2(data->nkeys, sizeof(Id));
896   seen = sat_malloc2(data->nkeys, sizeof(Id));
897
898   /* Merge the data already existing (in data->schemata, ->incoredata and
899      friends) with the new attributes in data->attrs[].  */
900   nentry = data->end - data->start;
901   addschema_prepare(data, schematacache);
902   memset(&newincore, 0, sizeof(newincore));
903   for (entry = 0; entry < nentry; entry++)
904     {
905       memset(seen, 0, data->nkeys * sizeof(Id));
906       sp = schema;
907       dp = data->incoredata + data->incoreoffset[entry];
908       if (data->incoredata)
909         dp = data_read_id(dp, &oldschema);
910       else
911         oldschema = 0;
912 #if 0
913 fprintf(stderr, "oldschema %d\n", oldschema);
914 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
915 fprintf(stderr, "schemadata %p\n", data->schemadata);
916 #endif
917       /* seen: -1: old data  0: skipped  >0: id + 1 */
918       newschema = 0;
919       oldcount = 0;
920       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
921         {
922           if (seen[*keyp])
923             {
924               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
925               exit(1);
926             }
927           seen[*keyp] = -1;
928           *sp++ = *keyp;
929           oldcount++;
930         }
931       if (data->attrs[entry])
932         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
933           {
934             if (!seen[*keyp])
935               {
936                 newschema = 1;
937                 *sp++ = *keyp;
938               }
939             seen[*keyp] = keyp[1] + 1;
940           }
941       *sp++ = 0;
942       if (newschema)
943         /* Ideally we'd like to sort the new schema here, to ensure
944            schema equality independend of the ordering.  We can't do that
945            yet.  For once see below (old ids need to come before new ids).
946            An additional difficulty is that we also need to move
947            the values with the keys.  */
948         schemaid = addschema(data, schema, schematacache);
949       else
950         schemaid = oldschema;
951
952
953       /* Now create data blob.  We walk through the (possibly new) schema
954          and either copy over old data, or insert the new.  */
955       /* XXX Here we rely on the fact that the (new) schema has the form
956          o1 o2 o3 o4 ... | n1 n2 n3 ...
957          (oX being the old keyids (possibly overwritten), and nX being
958           the new keyids).  This rules out sorting the keyids in order
959          to ensure a small schema count.  */
960       data->incoreoffset[entry] = newincore.len;
961       data_addid(&newincore, schemaid);
962       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
963         {
964           key = data->keys + *keyp;
965           ndp = dp;
966           if (oldcount)
967             {
968               /* Skip the data associated with this old key.  */
969               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
970                 {
971                   ndp = data_skip(dp, TYPE_ID);
972                   ndp = data_skip(ndp, TYPE_ID);
973                 }
974               else if (key->storage == KEY_STORAGE_INCORE)
975                 ndp = data_skip(dp, key->type);
976               oldcount--;
977             }
978           if (seen[*keyp] == -1)
979             {
980               /* If this key was an old one _and_ was not overwritten with
981                  a different value copy over the old value (we skipped it
982                  above).  */
983               if (dp != ndp)
984                 data_addblob(&newincore, dp, ndp - dp);
985               seen[*keyp] = 0;
986             }
987           else if (seen[*keyp])
988             {
989               /* Otherwise we have a new value.  Parse it into the internal
990                  form.  */
991               struct extdata *xd;
992               unsigned int oldvincorelen = 0;
993
994               xd = &newincore;
995               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
996                 {
997                   xd = &newvincore;
998                   oldvincorelen = xd->len;
999                 }
1000               id = seen[*keyp] - 1;
1001               switch (key->type)
1002                 {
1003                 case TYPE_VOID:
1004                 case TYPE_CONSTANT:
1005                   break;
1006                 case TYPE_STR:
1007                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1008                   break;
1009                 case TYPE_ID:
1010                 case TYPE_NUM:
1011                 case TYPE_DIR:
1012                   data_addid(xd, id);
1013                   break;
1014                 case TYPE_DIRNUMNUMARRAY:
1015                   for (ida = data->attriddata + id; *ida; ida += 3)
1016                     {
1017                       data_addid(xd, ida[0]);
1018                       data_addid(xd, ida[1]);
1019                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1020                     }
1021                   break;
1022                 default:
1023                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1024                   exit(1);
1025                 }
1026               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1027                 {
1028                   /* put offset/len in incore */
1029                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1030                   oldvincorelen = xd->len - oldvincorelen;
1031                   data_addid(&newincore, oldvincorelen);
1032                 }
1033             }
1034           dp = ndp;
1035         }
1036     }
1037   data->incoredata = newincore.buf;
1038   data->incoredatalen = newincore.len;
1039   data->incoredatafree = 0;
1040   
1041   data->vincore = newvincore.buf;
1042   data->vincorelen = newvincore.len;
1043
1044   data->attrs = sat_free(data->attrs);
1045   data->attrdata = sat_free(data->attrdata);
1046   data->attrdatalen = 0;
1047 }
1048
1049 Id
1050 repodata_str2dir(Repodata *data, const char *dir, int create)
1051 {
1052   Id id, parent;
1053   const char *dire;
1054
1055   parent = 0;
1056   while (*dir == '/' && dir[1] == '/')
1057     dir++;
1058   while (*dir)
1059     {
1060       dire = strchrnul(dir, '/');
1061       if (data->localpool)
1062         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1063       else
1064         id = strn2id(data->repo->pool, dir, dire - dir, create);
1065       if (!id)
1066         return 0;
1067       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1068       if (!parent)
1069         return 0;
1070       if (!*dire)
1071         break;
1072       dir = dire + 1;
1073       while (*dir == '/')
1074         dir++;
1075     }
1076   return parent;
1077 }
1078
1079 unsigned int
1080 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1081 {
1082   return compress_buf(page, len, cpage, max);
1083 }
1084
1085 #define SOLV_ERROR_EOF              3
1086
1087 static inline unsigned int
1088 read_u32(FILE *fp)
1089 {
1090   int c, i;
1091   unsigned int x = 0; 
1092
1093   for (i = 0; i < 4; i++) 
1094     {    
1095       c = getc(fp);
1096       if (c == EOF) 
1097         return 0;
1098       x = (x << 8) | c; 
1099     }    
1100   return x;
1101 }
1102
1103 /* Try to either setup on-demand paging (using FP as backing
1104    file), or in case that doesn't work (FP not seekable) slurps in
1105    all pages and deactivates paging.  */
1106
1107 void
1108 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1109 {
1110   FILE *fp = data->fp;
1111   unsigned int npages;
1112   unsigned int i;
1113   unsigned int can_seek;
1114   long cur_file_ofs;
1115   unsigned char buf[BLOB_PAGESIZE];
1116   if (pagesz != BLOB_PAGESIZE)
1117     {
1118       /* We could handle this by slurping in everything.  */
1119       fprintf (stderr, "non matching page size\n");
1120       exit (1);
1121     }
1122   can_seek = 1;
1123   if ((cur_file_ofs = ftell(fp)) < 0)
1124     can_seek = 0;
1125   clearerr (fp);
1126 #ifdef DEBUG_PAGING
1127   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1128 #endif
1129   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1130
1131   data->num_pages = npages;
1132   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1133
1134   /* If we can't seek on our input we have to slurp in everything.  */
1135   if (!can_seek)
1136     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1137   for (i = 0; i < npages; i++)
1138     {
1139       unsigned int in_len = read_u32(fp);
1140       unsigned int compressed = in_len & 1;
1141       Attrblobpage *p = data->pages + i;
1142       in_len >>= 1;
1143 #ifdef DEBUG_PAGING
1144       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1145                i, in_len, compressed ? "" : "not ");
1146 #endif
1147       if (can_seek)
1148         {
1149           cur_file_ofs += 4;
1150           p->mapped_at = -1;
1151           p->file_offset = cur_file_ofs;
1152           p->file_size = in_len * 2 + compressed;
1153           if (fseek(fp, in_len, SEEK_CUR) < 0)
1154             {
1155               perror ("fseek");
1156               fprintf (stderr, "can't seek after we thought we can\n");
1157               /* We can't fall back to non-seeking behaviour as we already
1158                  read over some data pages without storing them away.  */
1159               exit (1);
1160             }
1161           cur_file_ofs += in_len;
1162         }
1163       else
1164         {
1165           unsigned int out_len;
1166           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1167           p->mapped_at = i * BLOB_PAGESIZE;
1168           p->file_offset = 0;
1169           p->file_size = 0;
1170           /* We can't seek, so suck everything in.  */
1171           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1172             {
1173               perror ("fread");
1174               exit (1);
1175             }
1176           if (compressed)
1177             {
1178               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1179               if (out_len != BLOB_PAGESIZE
1180                   && i < npages - 1)
1181                 {
1182                   fprintf (stderr, "can't decompress\n");
1183                   exit (1);
1184                 }
1185             }
1186         }
1187     }
1188
1189   if (can_seek)
1190     {
1191       /* If we are here we were able to seek to all page
1192          positions, so activate paging by copying FP into our structure.
1193          We dup() the file, so that our callers can fclose() it and we
1194          still have it open.  But this means that we share file positions
1195          with the input filedesc.  So in case our caller reads it after us,
1196          and calls back into us we might change the file position unexpectedly
1197          to him.  */
1198       int fd = dup (fileno (fp));
1199       if (fd < 0)
1200         {
1201           /* Jeez!  What a bloody system, we can't dup() anymore.  */
1202           perror ("dup");
1203           exit (1);
1204         }
1205       /* XXX we don't close this yet anywhere.  */
1206       data->fp = fdopen (fd, "r");
1207       if (!data->fp)
1208         {
1209           /* My God!  What happened now?  */
1210           perror ("fdopen");
1211           exit (1);
1212         }
1213     }
1214 }