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