- really support DIRSTRARRAY type
[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 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
29                                   unsigned char *out, unsigned int out_len);
30 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
31                                               unsigned int in_len,
32                                               unsigned char *out,
33                                               unsigned int out_len);
34
35 static unsigned char *
36 data_read_id(unsigned char *dp, Id *idp)
37 {
38   Id x = 0;
39   unsigned char c;
40   for (;;)
41     {
42       c = *dp++;
43       if (!(c & 0x80))
44         {
45           *idp = (x << 7) ^ c;
46           return dp;
47         }
48       x = (x << 7) ^ c ^ 128;
49     }
50 }
51
52 static unsigned char *
53 data_read_ideof(unsigned char *dp, Id *idp, int *eof)
54 {
55   Id x = 0;
56   unsigned char c;
57   for (;;)
58     {
59       c = *dp++;
60       if (!(c & 0x80))
61         {
62           if (c & 0x40)
63             {
64               c ^= 0x40;
65               *eof = 0;
66             }
67           else
68             *eof = 1;
69           *idp = (x << 6) ^ c;
70           return dp;
71         }
72       x = (x << 7) ^ c ^ 128;
73     }
74 }
75
76 static unsigned char *
77 data_skip(unsigned char *dp, int type)
78 {
79   unsigned char x;
80   switch (type)
81     {
82     case TYPE_VOID:
83     case TYPE_CONSTANT:
84       return dp;
85     case TYPE_ID:
86     case TYPE_NUM:
87     case TYPE_DIR:
88       while ((*dp & 0x80) != 0)
89         dp++;
90       return dp + 1;
91     case TYPE_IDARRAY:
92       while ((*dp & 0xc0) != 0)
93         dp++;
94       return dp + 1;
95     case TYPE_STR:
96       while ((*dp) != 0)
97         dp++;
98       return dp + 1;
99     case TYPE_DIRSTRARRAY:
100       for (;;)
101         {
102           while ((*dp & 0x80) != 0)
103             dp++;
104           x = *dp++;
105           while ((*dp) != 0)
106             dp++;
107           dp++;
108           if (!(x & 0x40))
109             return dp;
110         }
111     case TYPE_DIRNUMNUMARRAY:
112       for (;;)
113         {
114           while ((*dp & 0x80) != 0)
115             dp++;
116           dp++;
117           while ((*dp & 0x80) != 0)
118             dp++;
119           dp++;
120           while ((*dp & 0x80) != 0)
121             dp++;
122           if (!(*dp & 0x40))
123             return dp + 1;
124           dp++;
125         }
126     default:
127       fprintf(stderr, "unknown type in data_skip\n");
128       exit(1);
129     }
130 }
131
132 static unsigned char *
133 data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
134 {
135   kv->eof = 1;
136   if (!dp)
137     return 0;
138   switch (key->type)
139     {
140     case TYPE_VOID:
141       return dp;
142     case TYPE_CONSTANT:
143       kv->num = key->size;
144       return dp;
145     case TYPE_STR:
146       kv->str = (const char *)dp;
147       return dp + strlen(kv->str) + 1;
148     case TYPE_ID:
149       return data_read_id(dp, &kv->id);
150     case TYPE_NUM:
151       return data_read_id(dp, &kv->num);
152     case TYPE_IDARRAY:
153       return data_read_ideof(dp, &kv->id, &kv->eof);
154     case TYPE_DIR:
155       return data_read_id(dp, &kv->id);
156     case TYPE_DIRSTRARRAY:
157       dp = data_read_ideof(dp, &kv->id, &kv->eof);
158       kv->str = (const char *)dp;
159       return dp + strlen(kv->str) + 1;
160     case TYPE_DIRNUMNUMARRAY:
161       dp = data_read_id(dp, &kv->id);
162       dp = data_read_id(dp, &kv->num);
163       return data_read_ideof(dp, &kv->num2, &kv->eof);
164     default:
165       return 0;
166     }
167 }
168
169 static unsigned char *
170 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
171 {
172   Id k, *keyp;
173
174   keyp = data->schemadata + data->schemata[schemaid];
175   while ((k = *keyp++) != 0)
176     {
177       if (k == key)
178         return dp;
179       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
180         {
181           dp = data_skip(dp, TYPE_ID);  /* skip that offset */
182           dp = data_skip(dp, TYPE_ID);  /* skip that length */
183           continue;
184         }
185       if (data->keys[k].storage != KEY_STORAGE_INCORE)
186         continue;
187       dp = data_skip(dp, data->keys[k].type);
188     }
189   return 0;
190 }
191
192 #define BLOB_PAGEBITS 15
193 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
194
195 static unsigned char *
196 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
197 {
198 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
199    and are consecutive.  Return a pointer to the mapping of PSTART.  */
200   unsigned char buf[BLOB_PAGESIZE];
201   unsigned int i;
202
203   /* Quick check in case all pages are there already and consecutive.  */
204   for (i = pstart; i <= pend; i++)
205     if (data->pages[i].mapped_at == -1
206         || (i > pstart
207             && data->pages[i].mapped_at
208                != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
209       break;
210   if (i > pend)
211     return data->blob_store + data->pages[pstart].mapped_at;
212
213   /* Ensure that we can map the numbers of pages we need at all.  */
214   if (pend - pstart + 1 > data->ncanmap)
215     {
216       unsigned int oldcan = data->ncanmap;
217       data->ncanmap = pend - pstart + 1;
218       if (data->ncanmap < 4)
219         data->ncanmap = 4;
220       data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
221       memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
222       data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
223 #ifdef DEBUG_PAGING
224       fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
225 #endif
226     }
227
228   /* Now search for "cheap" space in our store.  Space is cheap if it's either
229      free (very cheap) or contains pages we search for anyway.  */
230
231   /* Setup cost array.  */
232   unsigned int cost[data->ncanmap];
233   for (i = 0; i < data->ncanmap; i++)
234     {
235       unsigned int pnum = data->mapped[i];
236       if (pnum == 0)
237         cost[i] = 0;
238       else
239         {
240           pnum--;
241           Attrblobpage *p = data->pages + pnum;
242           assert (p->mapped_at != -1);
243           if (pnum >= pstart && pnum <= pend)
244             cost[i] = 1;
245           else
246             cost[i] = 3;
247         }
248     }
249
250   /* And search for cheapest space.  */
251   unsigned int best_cost = -1;
252   unsigned int best = 0;
253   unsigned int same_cost = 0;
254   for (i = 0; i + pend - pstart < data->ncanmap; i++)
255     {
256       unsigned int c = cost[i];
257       unsigned int j;
258       for (j = 0; j < pend - pstart + 1; j++)
259         c += cost[i+j];
260       if (c < best_cost)
261         best_cost = c, best = i;
262       else if (c == best_cost)
263         same_cost++;
264       /* A null cost won't become better.  */
265       if (c == 0)
266         break;
267     }
268   /* If all places have the same cost we would thrash on slot 0.  Avoid
269      this by doing a round-robin strategy in this case.  */
270   if (same_cost == data->ncanmap - pend + pstart - 1)
271     best = data->rr_counter++ % (data->ncanmap - pend + pstart);
272
273   /* So we want to map our pages from [best] to [best+pend-pstart].
274      Use a very simple strategy, which doesn't make the best use of
275      our resources, but works.  Throw away all pages in that range
276      (even ours) then copy around ours (in case they were outside the 
277      range) or read them in.  */
278   for (i = best; i < best + pend - pstart + 1; i++)
279     {
280       unsigned int pnum = data->mapped[i];
281       if (pnum--
282           /* If this page is exactly at the right place already,
283              no need to evict it.  */
284           && pnum != pstart + i - best)
285         {
286           /* Evict this page.  */
287 #ifdef DEBUG_PAGING
288           fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
289 #endif
290           cost[i] = 0;
291           data->mapped[i] = 0;
292           data->pages[pnum].mapped_at = -1;
293         }
294     }
295
296   /* Everything is free now.  Read in the pages we want.  */
297   for (i = pstart; i <= pend; i++)
298     {
299       Attrblobpage *p = data->pages + i;
300       unsigned int pnum = i - pstart + best;
301       void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
302       if (p->mapped_at != -1)
303         {
304           if (p->mapped_at != pnum * BLOB_PAGESIZE)
305             {
306 #ifdef DEBUG_PAGING
307               fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
308 #endif
309               /* Still mapped somewhere else, so just copy it from there.  */
310               memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
311               data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
312             }
313         }
314       else
315         {
316           unsigned int in_len = p->file_size;
317           unsigned int compressed = in_len & 1;
318           in_len >>= 1;
319 #ifdef DEBUG_PAGING
320           fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
321 #endif
322           /* Not mapped, so read in this page.  */
323           if (fseek(data->fp, p->file_offset, SEEK_SET) < 0)
324             {
325               perror ("mapping fseek");
326               exit (1);
327             }
328           if (fread(compressed ? buf : dest, in_len, 1, data->fp) != 1)
329             {
330               perror ("mapping fread");
331               exit (1);
332             }
333           if (compressed)
334             {
335               unsigned int out_len;
336               out_len = unchecked_decompress_buf(buf, in_len,
337                                                   dest, BLOB_PAGESIZE);
338               if (out_len != BLOB_PAGESIZE
339                   && i < data->num_pages - 1)
340                 {
341                   fprintf (stderr, "can't decompress\n");
342                   exit (1);
343                 }
344 #ifdef DEBUG_PAGING
345               fprintf (stderr, " (expand %d to %d)", in_len, out_len);
346 #endif
347             }
348 #ifdef DEBUG_PAGING
349           fprintf (stderr, "\n");
350 #endif
351         }
352       p->mapped_at = pnum * BLOB_PAGESIZE;
353       data->mapped[pnum] = i + 1;
354     }
355   return data->blob_store + best * BLOB_PAGESIZE;
356 }
357
358 static unsigned char *
359 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
360 {
361   unsigned char *dp;
362   if (key->type == TYPE_VOID)
363     return 0;
364   if (off >= data->lastverticaloffset)
365     {
366       off -= data->lastverticaloffset;
367       if (off + len > data->vincorelen)
368         return 0;
369       return data->vincore + off;
370     }
371   if (!data->fp)
372     return 0;
373   if (off + len > key->size)
374     return 0;
375   /* we now have the offset, go into vertical */
376   off += data->verticaloffset[key - data->keys];
377   dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
378   if (dp)
379     dp += off % BLOB_PAGESIZE;
380   return dp;
381 }
382
383 static inline unsigned char *
384 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
385 {
386   unsigned char *dp = *dpp;
387
388   if (!dp)
389     return 0;
390   if (key->storage == KEY_STORAGE_INCORE)
391     {
392       /* hmm, this is a bit expensive */
393       *dpp = data_skip(dp, key->type);
394       return dp;
395     }
396   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
397     {
398       Id off, len;
399       dp = data_read_id(dp, &off);
400       dp = data_read_id(dp, &len);
401       *dpp = dp;
402       return make_vertical_available(data, key, off, len);
403     }
404   return 0;
405 }
406
407 static inline int
408 maybe_load_repodata(Repodata *data)
409 {
410   if (data->state == REPODATA_STUB)
411     {
412       if (data->loadcallback)
413         data->loadcallback(data);
414       else
415         data->state = REPODATA_ERROR;
416     }
417   if (data->state == REPODATA_AVAILABLE)
418     return 1;
419   data->state = REPODATA_ERROR;
420   return 0;
421 }
422
423 const char *
424 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
425 {
426   Id schema;
427   Repokey *key;
428   Id id, *keyp;
429   unsigned char *dp;
430
431   if (!maybe_load_repodata (data))
432     return 0;
433
434   dp = data->incoredata + data->incoreoffset[entry];
435   dp = data_read_id(dp, &schema);
436   /* make sure the schema of this solvable contains the key */
437   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
438     if (!*keyp)
439       return 0;
440   dp = forward_to_key(data, keyid, schema, dp);
441   key = data->keys + keyid;
442   dp = get_data(data, key, &dp);
443   if (!dp)
444     return 0;
445   if (key->type == TYPE_STR)
446     return (const char *)dp;
447   if (key->type != TYPE_ID)
448     return 0;
449   /* id type, must either use global or local string store*/
450   dp = data_read_id(dp, &id);
451   if (data->localpool)
452     return data->spool.stringspace + data->spool.strings[id];
453   return id2str(data->repo->pool, id);
454 }
455
456 int
457 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
458 {
459   Id schema;
460   Repokey *key;
461   Id *keyp;
462   KeyValue kv;
463   unsigned char *dp;
464
465   *value = 0;
466
467   if (!maybe_load_repodata (data))
468     return 0;
469
470   dp = data->incoredata + data->incoreoffset[entry];
471   dp = data_read_id(dp, &schema);
472   /* make sure the schema of this solvable contains the key */
473   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
474     if (!*keyp)
475       return 0;
476   dp = forward_to_key(data, keyid, schema, dp);
477   key = data->keys + keyid;
478   dp = get_data(data, key, &dp);
479   if (!dp)
480     return 0;
481   if (key->type == TYPE_NUM
482       || key->type == TYPE_U32
483       || key->type == TYPE_CONSTANT)
484     {
485       dp = data_fetch(dp, &kv, key);
486       *value = kv.num;
487       return 1;
488     }
489   return 0;
490 }
491
492 void
493 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
494 {
495   Id schema;
496   Repokey *key;
497   Id k, keyid, *kp, *keyp;
498   unsigned char *dp, *ddp;
499   int onekey = 0;
500   int stop;
501   KeyValue kv;
502
503   if (!maybe_load_repodata (data))
504     return;
505
506   dp = data->incoredata + data->incoreoffset[entry];
507   dp = data_read_id(dp, &schema);
508   keyp = data->schemadata + data->schemata[schema];
509   if (keyname)
510     {
511       /* search in a specific key */
512       for (kp = keyp; (k = *kp++) != 0; )
513         if (data->keys[k].name == keyname)
514           break;
515       if (k == 0)
516         return;
517       dp = forward_to_key(data, k, schema, dp);
518       if (!dp)
519         return;
520       keyp = kp - 1;
521       onekey = 1;
522     }
523   while ((keyid = *keyp++) != 0)
524     {
525       stop = 0;
526       key = data->keys + keyid;
527       ddp = get_data(data, key, &dp);
528       do
529         {
530           ddp = data_fetch(ddp, &kv, key);
531           if (!ddp)
532             break;
533           stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
534         }
535       while (!kv.eof && !stop);
536       if (onekey || stop > SEARCH_NEXT_KEY)
537         return;
538     }
539 }
540
541
542 /* extend repodata so that it includes solvables p */
543 void
544 repodata_extend(Repodata *data, Id p)
545 {
546   if (data->start == data->end)
547     data->start = data->end = p;
548   if (p >= data->end)
549     {
550       int old = data->end - data->start;
551       int new = p - data->end + 1;
552       if (data->attrs)
553         {
554           data->attrs = sat_realloc2(data->attrs, old + new, sizeof(Id *));
555           memset(data->attrs + old, 0, new * sizeof(Id *));
556         }
557       data->incoreoffset = sat_realloc2(data->incoreoffset, old + new, sizeof(Id));
558       memset(data->incoreoffset + old, 0, new * sizeof(Id));
559       data->end = p + 1;
560     }
561   if (p < data->start)
562     {
563       int old = data->end - data->start;
564       int new = data->start - p;
565       if (data->attrs)
566         {
567           data->attrs = sat_realloc2(data->attrs, old + new, sizeof(Id *));
568           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
569           memset(data->attrs, 0, new * sizeof(Id *));
570         }
571       data->incoreoffset = sat_realloc2(data->incoreoffset, old + new, sizeof(Id));
572       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
573       memset(data->incoreoffset, 0, new * sizeof(Id));
574       data->start = p;
575     }
576 }
577
578 static void
579 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
580 {
581   Id *pp;
582   int i;
583   if (!data->attrs)
584     data->attrs = sat_calloc(data->end - data->start + 1, sizeof(Id *));
585   i = 0;
586   if (data->attrs[entry])
587     {
588       for (pp = data->attrs[entry]; *pp; pp += 2)
589         /* Determine equality based on the name only, allows us to change
590            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
591         if (data->keys[*pp].name == data->keys[keyid].name)
592           break;
593       if (*pp)
594         {
595           if (overwrite)
596             {
597               pp[0] = keyid;
598               pp[1] = val;
599             }
600           return;
601         }
602       i = pp - data->attrs[entry];
603     }
604   data->attrs[entry] = sat_realloc2(data->attrs[entry], i + 3, sizeof(Id));
605   pp = data->attrs[entry] + i;
606   *pp++ = keyid;
607   *pp++ = val;
608   *pp = 0;
609 }
610
611 void
612 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
613 {
614   Id keyid;
615
616   /* find key in keys */
617   for (keyid = 1; keyid < data->nkeys; keyid++)
618     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
619       {
620         if (key->type == TYPE_CONSTANT && key->size != data->keys[keyid].size)
621           continue;
622         break;
623       }
624   if (keyid == data->nkeys)
625     {
626       /* allocate new key */
627       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
628       data->keys[data->nkeys++] = *key;
629       if (data->verticaloffset)
630         {
631           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
632           data->verticaloffset[data->nkeys - 1] = 0;
633         }
634     }
635   repodata_insert_keyid(data, entry, keyid, val, 1);
636 }
637
638 void
639 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
640 {
641   Repokey key;
642   key.name = keyname;
643   key.type = TYPE_ID;
644   key.size = 0;
645   key.storage = KEY_STORAGE_INCORE;
646   repodata_set(data, entry, &key, id);
647 }
648
649 void
650 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
651 {
652   Repokey key;
653   key.name = keyname;
654   key.type = TYPE_NUM;
655   key.size = 0;
656   key.storage = KEY_STORAGE_INCORE;
657   repodata_set(data, entry, &key, num);
658 }
659
660 void
661 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
662 {
663   Repokey key;
664   Id id;
665   if (data->localpool)
666     id = stringpool_str2id(&data->spool, str, 1);
667   else
668     id = str2id(data->repo->pool, str, 1);
669   key.name = keyname;
670   key.type = TYPE_ID;
671   key.size = 0;
672   key.storage = KEY_STORAGE_INCORE;
673   repodata_set(data, entry, &key, id);
674 }
675
676 void
677 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
678 {
679   Repokey key;
680   key.name = keyname;
681   key.type = TYPE_CONSTANT;
682   key.size = constant;
683   key.storage = KEY_STORAGE_INCORE;
684   repodata_set(data, entry, &key, 0);
685 }
686
687 void
688 repodata_set_void(Repodata *data, Id entry, Id keyname)
689 {
690   Repokey key;
691   key.name = keyname;
692   key.type = TYPE_VOID;
693   key.size = 0;
694   key.storage = KEY_STORAGE_INCORE;
695   repodata_set(data, entry, &key, 0);
696 }
697
698 void
699 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
700 {
701   Repokey key;
702   int l;
703
704   l = strlen(str) + 1;
705   key.name = keyname;
706   key.type = TYPE_STR;
707   key.size = 0;
708   key.storage = KEY_STORAGE_INCORE;
709   data->attrdata = sat_realloc(data->attrdata, data->attrdatalen + l);
710   memcpy(data->attrdata + data->attrdatalen, str, l);
711   repodata_set(data, entry, &key, data->attrdatalen);
712   data->attrdatalen += l;
713 }
714
715 void
716 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
717 {
718   Id *ida, *pp;
719   Repokey key;
720
721 #if 0
722 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
723 #endif
724   if (data->attrs && data->attrs[entry])
725     {
726       for (pp = data->attrs[entry]; *pp; pp += 2)
727         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRNUMNUMARRAY)
728           break;
729       if (*pp)
730         {
731           int oldsize = 0;
732           for (ida = data->attriddata + pp[1]; *ida; ida += 3)
733             oldsize += 3;
734           if (ida + 1 == data->attriddata + data->attriddatalen)
735             {
736               /* this was the last entry, just append it */
737               data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 3, sizeof(Id));
738               data->attriddatalen--;    /* overwrite terminating 0  */
739             }
740           else
741             {
742               /* too bad. move to back. */
743               data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + oldsize + 4, sizeof(Id));
744               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
745               pp[1] = data->attriddatalen;
746               data->attriddatalen += oldsize;
747             }
748           data->attriddata[data->attriddatalen++] = dir;
749           data->attriddata[data->attriddatalen++] = num;
750           data->attriddata[data->attriddatalen++] = num2;
751           data->attriddata[data->attriddatalen++] = 0;
752           return;
753         }
754     }
755   key.name = keyname;
756   key.type = TYPE_DIRNUMNUMARRAY;
757   key.size = 0;
758   key.storage = KEY_STORAGE_INCORE;
759   data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 4, sizeof(Id));
760   repodata_set(data, entry, &key, data->attriddatalen);
761   data->attriddata[data->attriddatalen++] = dir;
762   data->attriddata[data->attriddatalen++] = num;
763   data->attriddata[data->attriddatalen++] = num2;
764   data->attriddata[data->attriddatalen++] = 0;
765 }
766
767 void
768 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
769 {
770   Id *ida, *pp, stroff;
771   Repokey key;
772   int l;
773
774   l = strlen(str) + 1;
775   data->attrdata = sat_realloc(data->attrdata, data->attrdatalen + l);
776   memcpy(data->attrdata + data->attrdatalen, str, l);
777   stroff = data->attrdatalen;
778   data->attrdatalen += l;
779
780 #if 0
781 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
782 #endif
783   if (data->attrs && data->attrs[entry])
784     {
785       for (pp = data->attrs[entry]; *pp; pp += 2)
786         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRSTRARRAY)
787           break;
788       if (*pp)
789         {
790           int oldsize = 0;
791           for (ida = data->attriddata + pp[1]; *ida; ida += 2)
792             oldsize += 2;
793           if (ida + 1 == data->attriddata + data->attriddatalen)
794             {
795               /* this was the last entry, just append it */
796               data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 2, sizeof(Id));
797               data->attriddatalen--;    /* overwrite terminating 0  */
798             }
799           else
800             {
801               /* too bad. move to back. */
802               data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + oldsize + 3, sizeof(Id));
803               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
804               pp[1] = data->attriddatalen;
805               data->attriddatalen += oldsize;
806             }
807           data->attriddata[data->attriddatalen++] = dir;
808           data->attriddata[data->attriddatalen++] = stroff;
809           data->attriddata[data->attriddatalen++] = 0;
810           return;
811         }
812     }
813   key.name = keyname;
814   key.type = TYPE_DIRSTRARRAY;
815   key.size = 0;
816   key.storage = KEY_STORAGE_INCORE;
817   data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 3, sizeof(Id));
818   repodata_set(data, entry, &key, data->attriddatalen);
819   data->attriddata[data->attriddatalen++] = dir;
820   data->attriddata[data->attriddatalen++] = stroff;
821   data->attriddata[data->attriddatalen++] = 0;
822 }
823
824 void
825 repodata_merge_attrs (Repodata *data, Id dest, Id src)
826 {
827   Id *keyp;
828   for (keyp = data->attrs[src]; *keyp; keyp += 2)
829     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
830 }
831
832 /*********************************/
833
834 /* unify with repo_write! */
835
836 #define EXTDATA_BLOCK 1023
837 #define SCHEMATA_BLOCK 31
838 #define SCHEMATADATA_BLOCK 255
839
840 struct extdata {
841   unsigned char *buf;
842   int len;
843 };
844
845 static void
846 data_addid(struct extdata *xd, Id x)
847 {
848   unsigned char *dp;
849   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
850   dp = xd->buf + xd->len;
851
852   if (x >= (1 << 14))
853     {
854       if (x >= (1 << 28))
855         *dp++ = (x >> 28) | 128;
856       if (x >= (1 << 21))
857         *dp++ = (x >> 21) | 128;
858       *dp++ = (x >> 14) | 128;
859     }
860   if (x >= (1 << 7))
861     *dp++ = (x >> 7) | 128;
862   *dp++ = x & 127;
863   xd->len = dp - xd->buf;
864 }
865
866 static void
867 data_addideof(struct extdata *xd, Id x, int eof)
868 {
869   if (x >= 64)
870     x = (x & 63) | ((x & ~63) << 1);
871   data_addid(xd, (eof ? x: x | 64));
872 }
873
874 static void
875 data_addblob(struct extdata *xd, unsigned char *blob, int len)
876 {
877   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
878   memcpy(xd->buf + xd->len, blob, len);
879   xd->len += len;
880 }
881
882 /*********************************/
883
884 static void
885 addschema_prepare(Repodata *data, Id *schematacache)
886 {
887   int h, len, i;
888   Id *sp;
889
890   memset(schematacache, 0, 256 * sizeof(Id));
891   for (i = 0; i < data->nschemata; i++)
892     {
893       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
894         h = h * 7 + *sp++;
895       h &= 255;
896       schematacache[h] = i + 1;
897     }
898   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
899   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
900 }
901
902 static Id
903 addschema(Repodata *data, Id *schema, Id *schematacache)
904 {
905   int h, len; 
906   Id *sp, cid; 
907
908   for (sp = schema, len = 0, h = 0; *sp; len++)
909     h = h * 7 + *sp++;
910   h &= 255; 
911   len++;
912
913   cid = schematacache[h];
914   if (cid)
915     {    
916       cid--;
917       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
918         return cid;
919       /* cache conflict */
920       for (cid = 0; cid < data->nschemata; cid++)
921         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
922           return cid;
923     }
924   /* a new one. make room. */
925   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
926   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
927   /* add schema */
928   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
929   data->schemata[data->nschemata] = data->schemadatalen;
930   data->schemadatalen += len;
931   schematacache[h] = data->nschemata + 1;
932 #if 0
933 fprintf(stderr, "addschema: new schema\n");
934 #endif
935   return data->nschemata++; 
936 }
937
938
939 void
940 repodata_internalize(Repodata *data)
941 {
942   Repokey *key;
943   Id id, entry, nentry, *ida;
944   Id schematacache[256];
945   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
946   unsigned char *dp, *ndp;
947   int newschema, oldcount;
948   struct extdata newincore;
949   struct extdata newvincore;
950
951   if (!data->attrs)
952     return;
953
954   newvincore.buf = data->vincore;
955   newvincore.len = data->vincorelen;
956
957   schema = sat_malloc2(data->nkeys, sizeof(Id));
958   seen = sat_malloc2(data->nkeys, sizeof(Id));
959
960   /* Merge the data already existing (in data->schemata, ->incoredata and
961      friends) with the new attributes in data->attrs[].  */
962   nentry = data->end - data->start;
963   addschema_prepare(data, schematacache);
964   memset(&newincore, 0, sizeof(newincore));
965   for (entry = 0; entry < nentry; entry++)
966     {
967       memset(seen, 0, data->nkeys * sizeof(Id));
968       sp = schema;
969       dp = data->incoredata + data->incoreoffset[entry];
970       if (data->incoredata)
971         dp = data_read_id(dp, &oldschema);
972       else
973         oldschema = 0;
974 #if 0
975 fprintf(stderr, "oldschema %d\n", oldschema);
976 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
977 fprintf(stderr, "schemadata %p\n", data->schemadata);
978 #endif
979       /* seen: -1: old data  0: skipped  >0: id + 1 */
980       newschema = 0;
981       oldcount = 0;
982       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
983         {
984           if (seen[*keyp])
985             {
986               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
987               exit(1);
988             }
989           seen[*keyp] = -1;
990           *sp++ = *keyp;
991           oldcount++;
992         }
993       if (data->attrs[entry])
994         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
995           {
996             if (!seen[*keyp])
997               {
998                 newschema = 1;
999                 *sp++ = *keyp;
1000               }
1001             seen[*keyp] = keyp[1] + 1;
1002           }
1003       *sp++ = 0;
1004       if (newschema)
1005         /* Ideally we'd like to sort the new schema here, to ensure
1006            schema equality independend of the ordering.  We can't do that
1007            yet.  For once see below (old ids need to come before new ids).
1008            An additional difficulty is that we also need to move
1009            the values with the keys.  */
1010         schemaid = addschema(data, schema, schematacache);
1011       else
1012         schemaid = oldschema;
1013
1014
1015       /* Now create data blob.  We walk through the (possibly new) schema
1016          and either copy over old data, or insert the new.  */
1017       /* XXX Here we rely on the fact that the (new) schema has the form
1018          o1 o2 o3 o4 ... | n1 n2 n3 ...
1019          (oX being the old keyids (possibly overwritten), and nX being
1020           the new keyids).  This rules out sorting the keyids in order
1021          to ensure a small schema count.  */
1022       data->incoreoffset[entry] = newincore.len;
1023       data_addid(&newincore, schemaid);
1024       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1025         {
1026           key = data->keys + *keyp;
1027           ndp = dp;
1028           if (oldcount)
1029             {
1030               /* Skip the data associated with this old key.  */
1031               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1032                 {
1033                   ndp = data_skip(dp, TYPE_ID);
1034                   ndp = data_skip(ndp, TYPE_ID);
1035                 }
1036               else if (key->storage == KEY_STORAGE_INCORE)
1037                 ndp = data_skip(dp, key->type);
1038               oldcount--;
1039             }
1040           if (seen[*keyp] == -1)
1041             {
1042               /* If this key was an old one _and_ was not overwritten with
1043                  a different value copy over the old value (we skipped it
1044                  above).  */
1045               if (dp != ndp)
1046                 data_addblob(&newincore, dp, ndp - dp);
1047               seen[*keyp] = 0;
1048             }
1049           else if (seen[*keyp])
1050             {
1051               /* Otherwise we have a new value.  Parse it into the internal
1052                  form.  */
1053               struct extdata *xd;
1054               unsigned int oldvincorelen = 0;
1055
1056               xd = &newincore;
1057               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1058                 {
1059                   xd = &newvincore;
1060                   oldvincorelen = xd->len;
1061                 }
1062               id = seen[*keyp] - 1;
1063               switch (key->type)
1064                 {
1065                 case TYPE_VOID:
1066                 case TYPE_CONSTANT:
1067                   break;
1068                 case TYPE_STR:
1069                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1070                   break;
1071                 case TYPE_ID:
1072                 case TYPE_NUM:
1073                 case TYPE_DIR:
1074                   data_addid(xd, id);
1075                   break;
1076                 case TYPE_DIRNUMNUMARRAY:
1077                   for (ida = data->attriddata + id; *ida; ida += 3)
1078                     {
1079                       data_addid(xd, ida[0]);
1080                       data_addid(xd, ida[1]);
1081                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1082                     }
1083                   break;
1084                 case TYPE_DIRSTRARRAY:
1085                   for (ida = data->attriddata + id; *ida; ida += 2)
1086                     {
1087                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1088                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1089                     }
1090                   break;
1091                 default:
1092                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1093                   exit(1);
1094                 }
1095               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1096                 {
1097                   /* put offset/len in incore */
1098                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1099                   oldvincorelen = xd->len - oldvincorelen;
1100                   data_addid(&newincore, oldvincorelen);
1101                 }
1102             }
1103           dp = ndp;
1104         }
1105     }
1106   data->incoredata = newincore.buf;
1107   data->incoredatalen = newincore.len;
1108   data->incoredatafree = 0;
1109   
1110   data->vincore = newvincore.buf;
1111   data->vincorelen = newvincore.len;
1112
1113   data->attrs = sat_free(data->attrs);
1114   data->attrdata = sat_free(data->attrdata);
1115   data->attrdatalen = 0;
1116 }
1117
1118 Id
1119 repodata_str2dir(Repodata *data, const char *dir, int create)
1120 {
1121   Id id, parent;
1122   const char *dire;
1123
1124   parent = 0;
1125   while (*dir == '/' && dir[1] == '/')
1126     dir++;
1127   while (*dir)
1128     {
1129       dire = strchrnul(dir, '/');
1130       if (data->localpool)
1131         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1132       else
1133         id = strn2id(data->repo->pool, dir, dire - dir, create);
1134       if (!id)
1135         return 0;
1136       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1137       if (!parent)
1138         return 0;
1139       if (!*dire)
1140         break;
1141       dir = dire + 1;
1142       while (*dir == '/')
1143         dir++;
1144     }
1145   return parent;
1146 }
1147
1148 unsigned int
1149 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1150 {
1151   return compress_buf(page, len, cpage, max);
1152 }
1153
1154 #define SOLV_ERROR_EOF              3
1155
1156 static inline unsigned int
1157 read_u32(FILE *fp)
1158 {
1159   int c, i;
1160   unsigned int x = 0; 
1161
1162   for (i = 0; i < 4; i++) 
1163     {    
1164       c = getc(fp);
1165       if (c == EOF) 
1166         return 0;
1167       x = (x << 8) | c; 
1168     }    
1169   return x;
1170 }
1171
1172 /* Try to either setup on-demand paging (using FP as backing
1173    file), or in case that doesn't work (FP not seekable) slurps in
1174    all pages and deactivates paging.  */
1175
1176 void
1177 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1178 {
1179   FILE *fp = data->fp;
1180   unsigned int npages;
1181   unsigned int i;
1182   unsigned int can_seek;
1183   long cur_file_ofs;
1184   unsigned char buf[BLOB_PAGESIZE];
1185   if (pagesz != BLOB_PAGESIZE)
1186     {
1187       /* We could handle this by slurping in everything.  */
1188       fprintf (stderr, "non matching page size\n");
1189       exit (1);
1190     }
1191   can_seek = 1;
1192   if ((cur_file_ofs = ftell(fp)) < 0)
1193     can_seek = 0;
1194   clearerr (fp);
1195 #ifdef DEBUG_PAGING
1196   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1197 #endif
1198   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1199
1200   data->num_pages = npages;
1201   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1202
1203   /* If we can't seek on our input we have to slurp in everything.  */
1204   if (!can_seek)
1205     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1206   for (i = 0; i < npages; i++)
1207     {
1208       unsigned int in_len = read_u32(fp);
1209       unsigned int compressed = in_len & 1;
1210       Attrblobpage *p = data->pages + i;
1211       in_len >>= 1;
1212 #ifdef DEBUG_PAGING
1213       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1214                i, in_len, compressed ? "" : "not ");
1215 #endif
1216       if (can_seek)
1217         {
1218           cur_file_ofs += 4;
1219           p->mapped_at = -1;
1220           p->file_offset = cur_file_ofs;
1221           p->file_size = in_len * 2 + compressed;
1222           if (fseek(fp, in_len, SEEK_CUR) < 0)
1223             {
1224               perror ("fseek");
1225               fprintf (stderr, "can't seek after we thought we can\n");
1226               /* We can't fall back to non-seeking behaviour as we already
1227                  read over some data pages without storing them away.  */
1228               exit (1);
1229             }
1230           cur_file_ofs += in_len;
1231         }
1232       else
1233         {
1234           unsigned int out_len;
1235           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1236           p->mapped_at = i * BLOB_PAGESIZE;
1237           p->file_offset = 0;
1238           p->file_size = 0;
1239           /* We can't seek, so suck everything in.  */
1240           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1241             {
1242               perror ("fread");
1243               exit (1);
1244             }
1245           if (compressed)
1246             {
1247               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1248               if (out_len != BLOB_PAGESIZE
1249                   && i < npages - 1)
1250                 {
1251                   fprintf (stderr, "can't decompress\n");
1252                   exit (1);
1253                 }
1254             }
1255         }
1256     }
1257
1258   if (can_seek)
1259     {
1260       /* If we are here we were able to seek to all page
1261          positions, so activate paging by copying FP into our structure.
1262          We dup() the file, so that our callers can fclose() it and we
1263          still have it open.  But this means that we share file positions
1264          with the input filedesc.  So in case our caller reads it after us,
1265          and calls back into us we might change the file position unexpectedly
1266          to him.  */
1267       int fd = dup (fileno (fp));
1268       if (fd < 0)
1269         {
1270           /* Jeez!  What a bloody system, we can't dup() anymore.  */
1271           perror ("dup");
1272           exit (1);
1273         }
1274       /* XXX we don't close this yet anywhere.  */
1275       data->fp = fdopen (fd, "r");
1276       if (!data->fp)
1277         {
1278           /* My God!  What happened now?  */
1279           perror ("fdopen");
1280           exit (1);
1281         }
1282     }
1283 }