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