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