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