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