- start support for 64bit nums
[platform/upstream/libsolv.git] / src / repo_write.c
1 /*
2  * Copyright (c) 2007-2011, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_write.c
10  * 
11  * Write Repo data out to a file in solv format
12  * 
13  * See doc/README.format for a description 
14  * of the binary file format
15  * 
16  */
17
18 #include <sys/types.h>
19 #include <limits.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25
26 #include "pool.h"
27 #include "util.h"
28 #include "repo_write.h"
29 #include "repopage.h"
30
31 /*------------------------------------------------------------------*/
32 /* Id map optimizations */
33
34 typedef struct needid {
35   Id need;
36   Id map;
37 } NeedId;
38
39
40 #define RELOFF(id) (needid[0].map + GETRELID(id))
41
42 /*
43  * increment need Id
44  * idarray: array of Ids, ID_NULL terminated
45  * needid: array of Id->NeedId
46  * 
47  * return size of array (including trailing zero)
48  * 
49  */
50
51 static void
52 incneedid(Pool *pool, Id id, NeedId *needid)
53 {
54   while (ISRELDEP(id))
55     {
56       Reldep *rd = GETRELDEP(pool, id);
57       needid[RELOFF(id)].need++;
58       if (ISRELDEP(rd->evr))
59         incneedid(pool, rd->evr, needid);
60       else
61         needid[rd->evr].need++;
62       id = rd->name;
63     }
64   needid[id].need++;
65 }
66
67 static int
68 incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
69 {
70   Id id;
71   int n = 0;
72
73   if (!idarray)
74     return 0;
75   while ((id = *idarray++) != 0)
76     {
77       n++;
78       while (ISRELDEP(id))
79         {
80           Reldep *rd = GETRELDEP(pool, id);
81           needid[RELOFF(id)].need++;
82           if (ISRELDEP(rd->evr))
83             incneedid(pool, rd->evr, needid);
84           else
85             needid[rd->evr].need++;
86           id = rd->name;
87         }
88       needid[id].need++;
89     }
90   return n + 1;
91 }
92
93
94 /*
95  * 
96  */
97
98 static int
99 needid_cmp_need(const void *ap, const void *bp, void *dp)
100 {
101   const NeedId *a = ap;
102   const NeedId *b = bp;
103   int r;
104   r = b->need - a->need;
105   if (r)
106     return r;
107   return a->map - b->map;
108 }
109
110 static int
111 needid_cmp_need_s(const void *ap, const void *bp, void *dp)
112 {
113   const NeedId *a = ap;
114   const NeedId *b = bp;
115   Stringpool *spool = dp;
116   const char *as;
117   const char *bs;
118
119   int r;
120   r = b->need - a->need;
121   if (r)
122     return r;
123   as = spool->stringspace + spool->strings[a->map];
124   bs = spool->stringspace + spool->strings[b->map];
125   return strcmp(as, bs);
126 }
127
128
129 /*------------------------------------------------------------------*/
130 /* output helper routines, used for writing the header */
131 /* (the data itself is accumulated in memory and written with
132  * write_blob) */
133
134 /*
135  * unsigned 32-bit
136  */
137
138 static void
139 write_u32(FILE *fp, unsigned int x)
140 {
141   if (putc(x >> 24, fp) == EOF ||
142       putc(x >> 16, fp) == EOF ||
143       putc(x >> 8, fp) == EOF ||
144       putc(x, fp) == EOF)
145     {
146       perror("write error u32");
147       exit(1);
148     }
149 }
150
151
152 /*
153  * unsigned 8-bit
154  */
155
156 static void
157 write_u8(FILE *fp, unsigned int x)
158 {
159   if (putc(x, fp) == EOF)
160     {
161       perror("write error u8");
162       exit(1);
163     }
164 }
165
166 /*
167  * data blob
168  */
169
170 static void
171 write_blob(FILE *fp, void *data, int len)
172 {
173   if (len && fwrite(data, len, 1, fp) != 1)
174     {
175       perror("write error blob");
176       exit(1);
177     }
178 }
179
180 /*
181  * Id
182  */
183
184 static void
185 write_id(FILE *fp, Id x)
186 {
187   if (x >= (1 << 14))
188     {
189       if (x >= (1 << 28))
190         putc((x >> 28) | 128, fp);
191       if (x >= (1 << 21))
192         putc((x >> 21) | 128, fp);
193       putc((x >> 14) | 128, fp);
194     }
195   if (x >= (1 << 7))
196     putc((x >> 7) | 128, fp);
197   if (putc(x & 127, fp) == EOF)
198     {
199       perror("write error id");
200       exit(1);
201     }
202 }
203
204 static inline void
205 write_id_eof(FILE *fp, Id x, int eof)
206 {
207   if (x >= 64)
208     x = (x & 63) | ((x & ~63) << 1);
209   write_id(fp, x | (eof ? 0 : 64));
210 }
211
212
213
214 static inline void
215 write_str(FILE *fp, const char *str)
216 {
217   if (fputs(str, fp) == EOF || putc(0, fp) == EOF)
218     {
219       perror("write error str");
220       exit(1);
221     }
222 }
223
224 /*
225  * Array of Ids
226  */
227
228 static void
229 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
230 {
231   Id id;
232   if (!ids)
233     return;
234   if (!*ids)
235     {
236       write_u8(fp, 0);
237       return;
238     }
239   for (;;)
240     {
241       id = *ids++;
242       if (needid)
243         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
244       if (id >= 64)
245         id = (id & 63) | ((id & ~63) << 1);
246       if (!*ids)
247         {
248           write_id(fp, id);
249           return;
250         }
251       write_id(fp, id | 64);
252     }
253 }
254
255 static int
256 cmp_ids(const void *pa, const void *pb, void *dp)
257 {
258   Id a = *(Id *)pa;
259   Id b = *(Id *)pb;
260   return a - b;
261 }
262
263 #if 0
264 static void
265 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
266 {
267   int len, i;
268   Id lids[64], *sids;
269
270   if (!ids)
271     return;
272   if (!*ids)
273     {
274       write_u8(fp, 0);
275       return;
276     }
277   for (len = 0; len < 64 && ids[len]; len++)
278     {
279       Id id = ids[len];
280       if (needid)
281         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
282       lids[len] = id;
283     }
284   if (ids[len])
285     {
286       for (i = len + 1; ids[i]; i++)
287         ;
288       sids = solv_malloc2(i, sizeof(Id));
289       memcpy(sids, lids, 64 * sizeof(Id));
290       for (; ids[len]; len++)
291         {
292           Id id = ids[len];
293           if (needid)
294             id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
295           sids[len] = id;
296         }
297     }
298   else
299     sids = lids;
300
301   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
302   if (needid)
303     marker = needid[marker].need;
304   for (i = 0; i < len; i++)
305     if (sids[i] == marker)
306       break;
307   if (i > 1)
308     solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
309   if ((len - i) > 2)
310     solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
311
312   Id id, old = 0;
313
314   /* The differencing above produces many runs of ones and twos.  I tried
315      fairly elaborate schemes to RLE those, but they give only very mediocre
316      improvements in compression, as coding the escapes costs quite some
317      space.  Even if they are coded only as bits in IDs.  The best improvement
318      was about 2.7% for the whole .solv file.  It's probably better to
319      invest some complexity into sharing idarrays, than RLEing.  */
320   for (i = 0; i < len - 1; i++)
321     {
322       id = sids[i];
323     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
324        hence all real differences are offsetted by 1.  Otherwise we would
325        have to handle negative differences, which would cost code space for
326        the encoding of the sign.  We loose the exact mapping of prereq here,
327        but we know the result, so we can recover from that in the reader.  */
328       if (id == marker)
329         id = old = 0;
330       else
331         {
332           id = id - old + 1;
333           old = sids[i];
334         }
335       /* XXX If difference is zero we have multiple equal elements,
336          we might want to skip writing them out.  */
337       if (id >= 64)
338         id = (id & 63) | ((id & ~63) << 1);
339       write_id(fp, id | 64);
340     }
341   id = sids[i];
342   if (id == marker)
343     id = 0;
344   else
345     id = id - old + 1;
346   if (id >= 64)
347     id = (id & 63) | ((id & ~63) << 1);
348   write_id(fp, id);
349   if (sids != lids)
350     solv_free(sids);
351 }
352 #endif
353
354
355 struct extdata {
356   unsigned char *buf;
357   int len;
358 };
359
360 struct cbdata {
361   Repo *repo;
362   Repodata *target;
363
364   Stringpool *ownspool;
365   Dirpool *owndirpool;
366
367   Id *keymap;
368   int nkeymap;
369   Id *keymapstart;
370
371   NeedId *needid;
372
373   Id *schema;           /* schema construction space */
374   Id *sp;               /* pointer in above */
375   Id *oldschema, *oldsp;
376
377   Id *solvschemata;
378   Id *subschemata;
379   int nsubschemata;
380   int current_sub;
381
382   struct extdata *extdata;
383
384   Id *dirused;
385
386   Id vstart;
387
388   Id maxdata;
389   Id lastlen;
390
391   int doingsolvables;   /* working on solvables data */
392 };
393
394 #define NEEDED_BLOCK 1023
395 #define SCHEMATA_BLOCK 31
396 #define SCHEMATADATA_BLOCK 255
397 #define EXTDATA_BLOCK 4095
398
399 static inline void
400 data_addid(struct extdata *xd, Id sx)
401 {
402   unsigned int x = (unsigned int)sx;
403   unsigned char *dp;
404   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
405   dp = xd->buf + xd->len;
406
407   if (x >= (1 << 14))
408     {
409       if (x >= (1 << 28))
410         *dp++ = (x >> 28) | 128;
411       if (x >= (1 << 21))
412         *dp++ = (x >> 21) | 128;
413       *dp++ = (x >> 14) | 128;
414     }
415   if (x >= (1 << 7))
416     *dp++ = (x >> 7) | 128;
417   *dp++ = x & 127;
418   xd->len = dp - xd->buf;
419 }
420
421 static inline void
422 data_addideof(struct extdata *xd, Id x, int eof)
423 {
424   if (x >= 64)
425     x = (x & 63) | ((x & ~63) << 1);
426   data_addid(xd, (eof ? x: x | 64));
427 }
428
429 static void
430 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
431 {
432   int len, i;
433   Id lids[64], *sids;
434   Id id, old;
435
436   if (!ids)
437     return;
438   if (!*ids)
439     {
440       data_addid(xd, 0);
441       return;
442     }
443   for (len = 0; len < 64 && ids[len]; len++)
444     {
445       Id id = ids[len];
446       if (needid)
447         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
448       lids[len] = id;
449     }
450   if (ids[len])
451     {
452       for (i = len + 1; ids[i]; i++)
453         ;
454       sids = solv_malloc2(i, sizeof(Id));
455       memcpy(sids, lids, 64 * sizeof(Id));
456       for (; ids[len]; len++)
457         {
458           Id id = ids[len];
459           if (needid)
460             id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
461           sids[len] = id;
462         }
463     }
464   else
465     sids = lids;
466
467   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
468   if (needid)
469     marker = needid[marker].need;
470   for (i = 0; i < len; i++)
471     if (sids[i] == marker)
472       break;
473   if (i > 1)
474     solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
475   if ((len - i) > 2)
476     solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
477
478   old = 0;
479
480   /* The differencing above produces many runs of ones and twos.  I tried
481      fairly elaborate schemes to RLE those, but they give only very mediocre
482      improvements in compression, as coding the escapes costs quite some
483      space.  Even if they are coded only as bits in IDs.  The best improvement
484      was about 2.7% for the whole .solv file.  It's probably better to
485      invest some complexity into sharing idarrays, than RLEing.  */
486   for (i = 0; i < len - 1; i++)
487     {
488       id = sids[i];
489     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
490        hence all real differences are offsetted by 1.  Otherwise we would
491        have to handle negative differences, which would cost code space for
492        the encoding of the sign.  We loose the exact mapping of prereq here,
493        but we know the result, so we can recover from that in the reader.  */
494       if (id == marker)
495         id = old = 0;
496       else
497         {
498           id = id - old + 1;
499           old = sids[i];
500         }
501       /* XXX If difference is zero we have multiple equal elements,
502          we might want to skip writing them out.  */
503       if (id >= 64)
504         id = (id & 63) | ((id & ~63) << 1);
505       data_addid(xd, id | 64);
506     }
507   id = sids[i];
508   if (id == marker)
509     id = 0;
510   else
511     id = id - old + 1;
512   if (id >= 64)
513     id = (id & 63) | ((id & ~63) << 1);
514   data_addid(xd, id);
515   if (sids != lids)
516     solv_free(sids);
517 }
518
519 static inline void
520 data_addblob(struct extdata *xd, unsigned char *blob, int len)
521 {
522   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
523   memcpy(xd->buf + xd->len, blob, len);
524   xd->len += len;
525 }
526
527 static inline void
528 data_addu32(struct extdata *xd, unsigned int num)
529 {
530   unsigned char d[4];
531   d[0] = num >> 24;
532   d[1] = num >> 16;
533   d[2] = num >> 8;
534   d[3] = num;
535   data_addblob(xd, d, 4);
536 }
537
538 static Id
539 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
540 {
541   const char *str = stringpool_id2str(ss, id);
542   id = stringpool_str2id(cbdata->ownspool, str, 1);
543   if (id >= cbdata->needid[0].map)
544     {
545       int oldoff = cbdata->needid[0].map;
546       int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
547       int nrels = cbdata->repo->pool->nrels;
548       cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
549       if (nrels)
550         memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
551       memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
552       cbdata->needid[0].map = newoff;
553     }
554   return id;
555 }
556
557 static Id
558 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
559 {
560   Id compid, parent;
561
562   parent = dirpool_parent(dp, dir);
563   if (parent)
564     parent = putinowndirpool(cbdata, data, dp, parent);
565   compid = dp->dirs[dir];
566   if (cbdata->ownspool && compid > 1)
567     compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
568   return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
569 }
570
571 /*
572  * collect usage information about the dirs
573  * 1: dir used, no child of dir used
574  * 2: dir used as parent of another used dir
575  */
576 static inline void
577 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
578 {
579   if (cbdata->dirused[dir])
580     return;
581   cbdata->dirused[dir] = 1;
582   while ((dir = dirpool_parent(dp, dir)) != 0)
583     {
584       if (cbdata->dirused[dir] == 2)
585         return;
586       if (cbdata->dirused[dir])
587         {
588           cbdata->dirused[dir] = 2;
589           return;
590         }
591       cbdata->dirused[dir] = 2;
592     }
593   cbdata->dirused[0] = 2;
594 }
595
596 /*
597  * pass 1 callback:
598  * collect key/id/dirid usage information, create needed schemas
599  */
600 static int
601 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
602 {
603   Id id;
604   int rm;
605
606   if (key->name == REPOSITORY_SOLVABLES)
607     return SEARCH_NEXT_KEY;     /* we do not want this one */
608
609   /* hack: ignore some keys, see BUGS */
610   if (data->repodataid != data->repo->nrepodata - 1)
611     if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
612       return SEARCH_NEXT_KEY;
613
614   rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
615   if (!rm)
616     return SEARCH_NEXT_KEY;     /* we do not want this one */
617
618   /* record key in schema */
619   if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
620       && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
621     *cbdata->sp++ = rm;
622
623   switch(key->type)
624     {
625       case REPOKEY_TYPE_ID:
626       case REPOKEY_TYPE_IDARRAY:
627         id = kv->id;
628         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
629           id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
630         incneedid(repo->pool, id, cbdata->needid);
631         break;
632       case REPOKEY_TYPE_DIR:
633       case REPOKEY_TYPE_DIRNUMNUMARRAY:
634       case REPOKEY_TYPE_DIRSTRARRAY:
635         id = kv->id;
636         if (cbdata->owndirpool)
637           putinowndirpool(cbdata, data, &data->dirpool, id);
638         else
639           setdirused(cbdata, &data->dirpool, id);
640         break;
641       case REPOKEY_TYPE_FIXARRAY:
642         if (kv->eof == 0)
643           {
644             if (cbdata->oldschema)
645               {
646                 fprintf(stderr, "nested structs not yet implemented\n");
647                 exit(1);
648               }
649             cbdata->oldschema = cbdata->schema;
650             cbdata->oldsp = cbdata->sp;
651             cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
652             cbdata->sp = cbdata->schema;
653           }
654         else if (kv->eof == 1)
655           {
656             cbdata->current_sub++;
657             *cbdata->sp = 0;
658             cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
659             cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
660 #if 0
661             fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
662 #endif
663             cbdata->sp = cbdata->schema;
664           }
665         else
666           {
667             solv_free(cbdata->schema);
668             cbdata->schema = cbdata->oldschema;
669             cbdata->sp = cbdata->oldsp;
670             cbdata->oldsp = cbdata->oldschema = 0;
671           }
672         break;
673       case REPOKEY_TYPE_FLEXARRAY:
674         if (kv->entry == 0)
675           {
676             if (kv->eof != 2)
677               *cbdata->sp++ = 0;        /* mark start */
678           }
679         else
680           {
681             /* just finished a schema, rewind */
682             Id *sp = cbdata->sp - 1;
683             *sp = 0;
684             while (sp[-1])
685               sp--;
686             cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
687             cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
688             cbdata->sp = kv->eof == 2 ? sp - 1: sp;
689           }
690         break;
691       default:
692         break;
693     }
694   return 0;
695 }
696
697 static int
698 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
699 {
700   struct cbdata *cbdata = vcbdata;
701   Repo *repo = data->repo;
702
703 #if 0
704   if (s)
705     fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? s - repo->pool->solvables : 0, s ? pool_id2str(repo->pool, s->name) : "", key->name, pool_id2str(repo->pool, key->name), key->type);
706 #endif
707   return repo_write_collect_needed(cbdata, repo, data, key, kv);
708 }
709
710
711 /*
712  * pass 2 callback:
713  * encode all of the data into the correct buffers
714  */
715
716 static int
717 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
718 {
719   int rm;
720   Id id;
721   unsigned int u32;
722   unsigned char v[4];
723   struct extdata *xd;
724   NeedId *needid;
725
726   if (key->name == REPOSITORY_SOLVABLES)
727     return SEARCH_NEXT_KEY;
728
729   /* hack: ignore some keys, see BUGS */
730   if (data->repodataid != data->repo->nrepodata - 1)
731     if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
732       return SEARCH_NEXT_KEY;
733
734   rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
735   if (!rm)
736     return SEARCH_NEXT_KEY;     /* we do not want this one */
737   
738   if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
739     {
740       xd = cbdata->extdata + rm;        /* vertical buffer */
741       if (cbdata->vstart == -1)
742         cbdata->vstart = xd->len;
743     }
744   else
745     xd = cbdata->extdata + 0;           /* incore buffer */
746   switch(key->type)
747     {
748       case REPOKEY_TYPE_VOID:
749       case REPOKEY_TYPE_CONSTANT:
750       case REPOKEY_TYPE_CONSTANTID:
751         break;
752       case REPOKEY_TYPE_ID:
753         id = kv->id;
754         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
755           id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
756         needid = cbdata->needid;
757         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
758         data_addid(xd, id);
759         break;
760       case REPOKEY_TYPE_IDARRAY:
761         id = kv->id;
762         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
763           id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
764         needid = cbdata->needid;
765         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
766         data_addideof(xd, id, kv->eof);
767         break;
768       case REPOKEY_TYPE_STR:
769         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
770         break;
771       case REPOKEY_TYPE_MD5:
772         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
773         break;
774       case REPOKEY_TYPE_SHA1:
775         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
776         break;
777       case REPOKEY_TYPE_SHA256:
778         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
779         break;
780       case REPOKEY_TYPE_U32:
781         u32 = kv->num;
782         v[0] = u32 >> 24;
783         v[1] = u32 >> 16;
784         v[2] = u32 >> 8;
785         v[3] = u32;
786         data_addblob(xd, v, 4);
787         break;
788       case REPOKEY_TYPE_NUM:
789         data_addid(xd, kv->num);
790         break;
791       case REPOKEY_TYPE_DIR:
792         id = kv->id;
793         if (cbdata->owndirpool)
794           id = putinowndirpool(cbdata, data, &data->dirpool, id);
795         id = cbdata->dirused[id];
796         data_addid(xd, id);
797         break;
798       case REPOKEY_TYPE_BINARY:
799         data_addid(xd, kv->num);
800         if (kv->num)
801           data_addblob(xd, (unsigned char *)kv->str, kv->num);
802         break;
803       case REPOKEY_TYPE_DIRNUMNUMARRAY:
804         id = kv->id;
805         if (cbdata->owndirpool)
806           id = putinowndirpool(cbdata, data, &data->dirpool, id);
807         id = cbdata->dirused[id];
808         data_addid(xd, id);
809         data_addid(xd, kv->num);
810         data_addideof(xd, kv->num2, kv->eof);
811         break;
812       case REPOKEY_TYPE_DIRSTRARRAY:
813         id = kv->id;
814         if (cbdata->owndirpool)
815           id = putinowndirpool(cbdata, data, &data->dirpool, id);
816         id = cbdata->dirused[id];
817         data_addideof(xd, id, kv->eof);
818         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
819         break;
820       case REPOKEY_TYPE_FIXARRAY:
821         if (kv->eof == 0)
822           {
823             if (kv->num)
824               {
825                 data_addid(xd, kv->num);
826                 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
827 #if 0
828                 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
829 #endif
830               }
831           }
832         else if (kv->eof == 1)
833           {
834             cbdata->current_sub++;
835           }
836         break;
837       case REPOKEY_TYPE_FLEXARRAY:
838         if (!kv->entry)
839           data_addid(xd, kv->num);
840         if (kv->eof != 2)
841           data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
842         if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
843           {
844             if (xd->len - cbdata->lastlen > cbdata->maxdata)
845               cbdata->maxdata = xd->len - cbdata->lastlen;
846             cbdata->lastlen = xd->len;
847           }
848         break;
849       default:
850         fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
851         exit(1);
852     }
853   if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
854     {
855       /* we can re-use old data in the blob here! */
856       data_addid(cbdata->extdata + 0, cbdata->vstart);                  /* add offset into incore data */
857       data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart);        /* add length into incore data */
858       cbdata->vstart = -1;
859     }
860   return 0;
861 }
862
863 static int
864 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
865 {
866   struct cbdata *cbdata = vcbdata;
867   return repo_write_adddata(cbdata, data, key, kv);
868 }
869
870 /* traverse through directory with first child "dir" */
871 static int
872 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
873 {
874   Id sib, child;
875   Id parent, lastn;
876
877   parent = n;
878   /* special case for '/', which has to come first */
879   if (parent == 1)
880     dirmap[n++] = 1;
881   for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
882     {
883       if (used && !used[sib])
884         continue;
885       if (sib == 1 && parent == 1)
886         continue;       /* already did that one above */
887       dirmap[n++] = sib;
888     }
889
890   /* now go through all the siblings we just added and
891    * do recursive calls on them */
892   lastn = n;
893   for (; parent < lastn; parent++)
894     {
895       sib = dirmap[parent];
896       if (used && used[sib] != 2)       /* 2: used as parent */
897         continue;
898       child = dirpool_child(dp, sib);
899       if (child)
900         {
901           dirmap[n++] = -parent;        /* start new block */
902           n = traverse_dirs(dp, dirmap, n, child, used);
903         }
904     }
905   return n;
906 }
907
908 static void
909 write_compressed_page(FILE *fp, unsigned char *page, int len)
910 {
911   int clen;
912   unsigned char cpage[REPOPAGE_BLOBSIZE];
913
914   clen = repopagestore_compress_page(page, len, cpage, len - 1);
915   if (!clen)
916     {
917       write_u32(fp, len * 2);
918       write_blob(fp, page, len);
919     }
920   else
921     {
922       write_u32(fp, clen * 2 + 1);
923       write_blob(fp, cpage, clen);
924     }
925 }
926
927 static Id verticals[] = {
928   SOLVABLE_AUTHORS,
929   SOLVABLE_DESCRIPTION,
930   SOLVABLE_MESSAGEDEL,
931   SOLVABLE_MESSAGEINS,
932   SOLVABLE_EULA,
933   SOLVABLE_DISKUSAGE,
934   SOLVABLE_FILELIST,
935   0
936 };
937
938 static char *languagetags[] = {
939   "solvable:summary:",
940   "solvable:description:",
941   "solvable:messageins:",
942   "solvable:messagedel:",
943   "solvable:eula:",
944   0
945 };
946
947 int
948 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
949 {
950   const char *keyname;
951   int i;
952
953   for (i = 0; verticals[i]; i++)
954     if (key->name == verticals[i])
955       return KEY_STORAGE_VERTICAL_OFFSET;
956   keyname = pool_id2str(repo->pool, key->name);
957   for (i = 0; languagetags[i] != 0; i++)
958     if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
959       return KEY_STORAGE_VERTICAL_OFFSET;
960   return KEY_STORAGE_INCORE;
961 }
962
963 /*
964  * Repo
965  */
966
967 /*
968  * the code works the following way:
969  *
970  * 1) find which keys should be written
971  * 2) collect usage information for keys/ids/dirids, create schema
972  *    data
973  * 3) use usage information to create mapping tables, so that often
974  *    used ids get a lower number
975  * 4) encode data into buffers using the mapping tables
976  * 5) write everything to disk
977  */
978 int
979 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
980 {
981   Pool *pool = repo->pool;
982   int i, j, n;
983   Solvable *s;
984   NeedId *needid;
985   int nstrings, nrels;
986   unsigned int sizeid;
987   unsigned int solv_flags;
988   Reldep *ran;
989   Id *idarraydata;
990
991   Id id, *sp;
992
993   Id *dirmap;
994   int ndirmap;
995   Id *keyused;
996   unsigned char *repodataused;
997   int anyrepodataused = 0;
998   int anysolvableused = 0;
999   
1000   struct cbdata cbdata;
1001   int clonepool;
1002   Repokey *key;
1003   int poolusage, dirpoolusage, idused, dirused;
1004   int reloff;
1005
1006   Repodata *data, *dirpooldata;
1007
1008   Repodata target;
1009
1010   Stringpool *spool;
1011   Dirpool *dirpool;
1012
1013   Id mainschema;
1014
1015   struct extdata *xd;
1016
1017   Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1018
1019   unsigned char *prefixcomp;
1020   unsigned int compsum;
1021   char *old_str;
1022
1023
1024   memset(&cbdata, 0, sizeof(cbdata));
1025   cbdata.repo = repo;
1026   cbdata.target = &target;
1027
1028   repodata_initdata(&target, repo, 1);
1029
1030   /* go through all repodata and find the keys we need */
1031   /* also unify keys */
1032   /*          keymapstart - maps repo number to keymap offset */
1033   /*          keymap      - maps repo key to my key, 0 -> not used */
1034
1035   /* start with all KEY_STORAGE_SOLVABLE ids */
1036
1037   n = ID_NUM_INTERNAL;
1038   FOR_REPODATAS(repo, i, data)
1039     n += data->nkeys;
1040   cbdata.keymap = solv_calloc(n, sizeof(Id));
1041   cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1042   repodataused = solv_calloc(repo->nrepodata, 1);
1043
1044   clonepool = 0;
1045   poolusage = 0;
1046
1047   /* add keys for STORAGE_SOLVABLE */
1048   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1049     {
1050       Repokey keyd;
1051       keyd.name = i;
1052       if (i < SOLVABLE_PROVIDES)
1053         keyd.type = REPOKEY_TYPE_ID;
1054       else if (i < RPM_RPMDBID)
1055         keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1056       else
1057         keyd.type = REPOKEY_TYPE_U32;
1058       keyd.size = 0;
1059       keyd.storage = KEY_STORAGE_SOLVABLE;
1060       if (keyfilter)
1061         {
1062           keyd.storage = keyfilter(repo, &keyd, kfdata);
1063           if (keyd.storage == KEY_STORAGE_DROPPED)
1064             continue;
1065           keyd.storage = KEY_STORAGE_SOLVABLE;
1066         }
1067       poolusage = 1;
1068       clonepool = 1;
1069       cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1070     }
1071
1072   if (repo->nsolvables)
1073     {
1074       Repokey keyd;
1075       keyd.name = REPOSITORY_SOLVABLES;
1076       keyd.type = REPOKEY_TYPE_FLEXARRAY;
1077       keyd.size = 0;
1078       keyd.storage = KEY_STORAGE_INCORE;
1079       cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1080     }
1081
1082   dirpoolusage = 0;
1083
1084   spool = 0;
1085   dirpool = 0;
1086   dirpooldata = 0;
1087   n = ID_NUM_INTERNAL;
1088   FOR_REPODATAS(repo, i, data)
1089     {
1090       cbdata.keymapstart[i] = n;
1091       cbdata.keymap[n++] = 0;   /* key 0 */
1092       idused = 0;
1093       dirused = 0;
1094       if (keyfilter)
1095         {
1096           Repokey keyd;
1097           /* check if we want this repodata */
1098           memset(&keyd, 0, sizeof(keyd));
1099           keyd.name = 1;
1100           keyd.type = 1;
1101           keyd.size = i;
1102           if (keyfilter(repo, &keyd, kfdata) == -1)
1103             continue;
1104         }
1105       for (j = 1; j < data->nkeys; j++, n++)
1106         {
1107           key = data->keys + j;
1108           if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1109             {
1110               cbdata.keymap[n] = cbdata.keymap[key->name];
1111               continue;
1112             }
1113           if (key->type == REPOKEY_TYPE_DELETED)
1114             {
1115               cbdata.keymap[n] = 0;
1116               continue;
1117             }
1118           if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1119             {
1120               Repokey keyd = *key;
1121               keyd.size = repodata_globalize_id(data, key->size, 1);
1122               id = repodata_key2id(&target, &keyd, 0);
1123             }
1124           else
1125             id = repodata_key2id(&target, key, 0);
1126           if (!id)
1127             {
1128               Repokey keyd = *key;
1129               keyd.storage = KEY_STORAGE_INCORE;
1130               if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1131                 keyd.size = repodata_globalize_id(data, key->size, 1);
1132               else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1133                 keyd.size = 0;
1134               if (keyfilter)
1135                 {
1136                   keyd.storage = keyfilter(repo, &keyd, kfdata);
1137                   if (keyd.storage == KEY_STORAGE_DROPPED)
1138                     {
1139                       cbdata.keymap[n] = 0;
1140                       continue;
1141                     }
1142                 }
1143               id = repodata_key2id(&target, &keyd, 1);
1144             }
1145           cbdata.keymap[n] = id;
1146           /* load repodata if not already loaded */
1147           if (data->state == REPODATA_STUB)
1148             {
1149               if (data->loadcallback)
1150                 data->loadcallback(data);
1151               else
1152                 data->state = REPODATA_ERROR;
1153               if (data->state != REPODATA_ERROR)
1154                 {
1155                   /* redo this repodata! */
1156                   j = 0;
1157                   n = cbdata.keymapstart[i];
1158                   continue;
1159                 }
1160             }
1161           if (data->state == REPODATA_ERROR)
1162             {
1163               /* too bad! */
1164               cbdata.keymap[n] = 0;
1165               continue;
1166             }
1167
1168           repodataused[i] = 1;
1169           anyrepodataused = 1;
1170           if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1171               key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1172             idused = 1;
1173           else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1174             {
1175               idused = 1;       /* dirs also use ids */
1176               dirused = 1;
1177             }
1178         }
1179       if (idused)
1180         {
1181           if (data->localpool)
1182             {
1183               if (poolusage)
1184                 poolusage = 3;  /* need own pool */
1185               else
1186                 {
1187                   poolusage = 2;
1188                   spool = &data->spool;
1189                 }
1190             }
1191           else
1192             {
1193               if (poolusage == 0)
1194                 poolusage = 1;
1195               else if (poolusage != 1)
1196                 poolusage = 3;  /* need own pool */
1197             }
1198         }
1199       if (dirused)
1200         {
1201           if (dirpoolusage)
1202             dirpoolusage = 3;   /* need own dirpool */
1203           else
1204             {
1205               dirpoolusage = 2;
1206               dirpool = &data->dirpool;
1207               dirpooldata = data;
1208             }
1209         }
1210     }
1211   cbdata.nkeymap = n;
1212
1213   /* 0: no pool needed at all */
1214   /* 1: use global pool */
1215   /* 2: use repodata local pool */
1216   /* 3: need own pool */
1217   if (poolusage == 3)
1218     {
1219       spool = &target.spool;
1220       /* hack: reuse global pool data so we don't have to map pool ids */
1221       if (clonepool)
1222         {
1223           stringpool_free(spool);
1224           stringpool_clone(spool, &pool->ss);
1225         }
1226       cbdata.ownspool = spool;
1227     }
1228   else if (poolusage == 0 || poolusage == 1)
1229     {
1230       poolusage = 1;
1231       spool = &pool->ss;
1232     }
1233
1234   if (dirpoolusage == 3)
1235     {
1236       dirpool = &target.dirpool;
1237       dirpooldata = 0;
1238       cbdata.owndirpool = dirpool;
1239     }
1240   else if (dirpool)
1241     cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1242
1243
1244 /********************************************************************/
1245 #if 0
1246 fprintf(stderr, "poolusage: %d\n", poolusage);
1247 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1248 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1249 for (i = 1; i < target.nkeys; i++)
1250   fprintf(stderr, "  %2d: %s[%d] %d %d %d\n", i, pool_id2str(pool, target.keys[i].name), target.keys[i].name, target.keys[i].type, target.keys[i].size, target.keys[i].storage);
1251 #endif
1252
1253   /* copy keys if requested */
1254   if (keyq)
1255     {
1256       queue_empty(keyq);
1257       for (i = 1; i < target.nkeys; i++)
1258         queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1259     }
1260
1261   if (poolusage > 1)
1262     {
1263       /* put all the keys we need in our string pool */
1264       /* put mapped ids right into target.keys */
1265       for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1266         {
1267           key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1268           if (key->type == REPOKEY_TYPE_CONSTANTID)
1269             {
1270               key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1271               type_constantid = key->type;
1272               key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1273             }
1274           else
1275             key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1276         }
1277       if (poolusage == 2)
1278         stringpool_freehash(spool);     /* free some mem */
1279     }
1280
1281
1282 /********************************************************************/
1283
1284   /* set needed count of all strings and rels,
1285    * find which keys are used in the solvables
1286    * put all strings in own spool
1287    */
1288
1289   reloff = spool->nstrings;
1290   if (poolusage == 3)
1291     reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1292
1293   needid = calloc(reloff + pool->nrels, sizeof(*needid));
1294   needid[0].map = reloff;
1295
1296   cbdata.needid = needid;
1297   cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1298   cbdata.sp = cbdata.schema;
1299   cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1300
1301   /* create main schema */
1302   cbdata.sp = cbdata.schema;
1303   /* collect all other data from all repodatas */
1304   /* XXX: merge arrays of equal keys? */
1305   FOR_REPODATAS(repo, j, data)
1306     {
1307       if (!repodataused[j])
1308         continue;
1309       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1310     }
1311   sp = cbdata.sp;
1312   /* add solvables if needed (may revert later) */
1313   if (repo->nsolvables)
1314     {
1315       *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1316       target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1317     }
1318   *sp = 0;
1319   mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1320
1321   idarraydata = repo->idarraydata;
1322
1323   anysolvableused = 0;
1324   cbdata.doingsolvables = 1;
1325   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1326     {
1327       if (s->repo != repo)
1328         continue;
1329
1330       /* set schema info, keep in sync with further down */
1331       sp = cbdata.schema;
1332       if (cbdata.keymap[SOLVABLE_NAME])
1333         {
1334           *sp++ = cbdata.keymap[SOLVABLE_NAME];
1335           needid[s->name].need++;
1336         }
1337       if (cbdata.keymap[SOLVABLE_ARCH])
1338         {
1339           *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1340           needid[s->arch].need++;
1341         }
1342       if (cbdata.keymap[SOLVABLE_EVR])
1343         {
1344           *sp++ = cbdata.keymap[SOLVABLE_EVR];
1345           needid[s->evr].need++;
1346         }
1347       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1348         {
1349           *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1350           needid[s->vendor].need++;
1351         }
1352       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1353         {
1354           *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1355           target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1356         }
1357       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1358         {
1359           *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1360           target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1361         }
1362       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1363         {
1364           *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1365           target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1366         }
1367       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1368         {
1369           *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1370           target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1371         }
1372       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1373         {
1374           *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1375           target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1376         }
1377       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1378         {
1379           *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1380           target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1381         }
1382       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1383         {
1384           *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1385           target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1386         }
1387       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1388         {
1389           *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1390           target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1391         }
1392       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1393         {
1394           *sp++ = cbdata.keymap[RPM_RPMDBID];
1395           target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1396         }
1397       cbdata.sp = sp;
1398
1399       if (anyrepodataused)
1400         {
1401           FOR_REPODATAS(repo, j, data)
1402             {
1403               if (!repodataused[j])
1404                 continue;
1405               if (i < data->start || i >= data->end)
1406                 continue;
1407               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1408               needid = cbdata.needid;
1409             }
1410         }
1411       *cbdata.sp = 0;
1412       cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1413       if (cbdata.solvschemata[n])
1414         anysolvableused = 1;
1415       n++;
1416     }
1417   cbdata.doingsolvables = 0;
1418   assert(n == repo->nsolvables);
1419
1420   if (repo->nsolvables && !anysolvableused)
1421     {
1422       /* strip off solvable from the main schema */
1423       target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1424       sp = cbdata.schema;
1425       for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1426         {
1427           *sp = target.schemadata[target.schemata[mainschema] + i];
1428           if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1429             sp++;
1430         }
1431       assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1432       *sp = 0;
1433       target.schemadatalen = target.schemata[mainschema];
1434       target.nschemata--;
1435       repodata_free_schemahash(&target);
1436       mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1437     }
1438
1439 /********************************************************************/
1440
1441   /* remove unused keys */
1442   keyused = solv_calloc(target.nkeys, sizeof(Id));
1443   for (i = 1; i < target.schemadatalen; i++)
1444     keyused[target.schemadata[i]] = 1;
1445   keyused[0] = 0;
1446   for (n = i = 1; i < target.nkeys; i++)
1447     {
1448       if (!keyused[i])
1449         continue;
1450       keyused[i] = n;
1451       if (i != n)
1452         {
1453           target.keys[n] = target.keys[i];
1454           if (keyq)
1455             {
1456               keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1457               keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1458             }
1459         }
1460       n++;
1461     }
1462   target.nkeys = n;
1463   if (keyq)
1464     queue_truncate(keyq, 2 * n - 2);
1465
1466   /* update schema data to the new key ids */
1467   for (i = 1; i < target.schemadatalen; i++)
1468     target.schemadata[i] = keyused[target.schemadata[i]];
1469   /* update keymap to the new key ids */
1470   for (i = 0; i < cbdata.nkeymap; i++)
1471     cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1472   keyused = solv_free(keyused);
1473
1474   /* increment needid of the used keys, they are already mapped to
1475    * the correct string pool  */
1476   for (i = 1; i < target.nkeys; i++)
1477     {
1478       if (target.keys[i].type == type_constantid)
1479         needid[target.keys[i].size].need++;
1480       needid[target.keys[i].name].need++;
1481       needid[target.keys[i].type].need++;
1482     }
1483
1484 /********************************************************************/
1485
1486   if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1487     {
1488       /* no dirs used at all */
1489       cbdata.dirused = solv_free(cbdata.dirused);
1490       dirpool = 0;
1491     }
1492
1493   /* increment need id for used dir components */
1494   if (dirpool)
1495     {
1496       /* if we have own dirpool, all entries in it are used.
1497          also, all comp ids are already mapped by putinowndirpool(),
1498          so we can simply increment needid.
1499          (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1500       /* else we re-use a dirpool of repodata "dirpooldata".
1501          dirused tells us which of the ids are used.
1502          we need to map comp ids if we generate a new pool.
1503          (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1504       for (i = 1; i < dirpool->ndirs; i++)
1505         {
1506 #if 0
1507 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1508 #endif
1509           if (cbdata.dirused && !cbdata.dirused[i])
1510             continue;
1511           id = dirpool->dirs[i];
1512           if (id <= 0)
1513             continue;
1514           if (dirpooldata && cbdata.ownspool && id > 1)
1515             {
1516               id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1517               needid = cbdata.needid;
1518             }
1519           needid[id].need++;
1520         }
1521     }
1522
1523
1524 /********************************************************************/
1525
1526   /*
1527    * create mapping table, new keys are sorted by needid[].need
1528    *
1529    * needid[key].need : old key -> new key
1530    * needid[key].map  : new key -> old key
1531    */
1532
1533   /* zero out id 0 and rel 0 just in case */
1534   reloff = needid[0].map;
1535   needid[0].need = 0;
1536   needid[reloff].need = 0;
1537
1538   for (i = 1; i < reloff + pool->nrels; i++)
1539     needid[i].map = i;
1540
1541 #if 0
1542   solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1543 #else
1544   /* make first entry '' */
1545   needid[1].need = 1;
1546   solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1547 #endif
1548   solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1549   /* now needid is in new order, needid[newid].map -> oldid */
1550
1551   /* calculate string space size, also zero out needid[].need */
1552   sizeid = 0;
1553   for (i = 1; i < reloff; i++)
1554     {
1555       if (!needid[i].need)
1556         break;  /* as we have sorted, every entry after this also has need == 0 */
1557       needid[i].need = 0;
1558       sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1559     }
1560   nstrings = i; /* our new string id end */
1561
1562   /* make needid[oldid].need point to newid */
1563   for (i = 1; i < nstrings; i++)
1564     needid[needid[i].map].need = i;
1565
1566   /* same as above for relations */
1567   for (i = 0; i < pool->nrels; i++)
1568     {
1569       if (!needid[reloff + i].need)
1570         break;
1571       needid[reloff + i].need = 0;
1572     }
1573   nrels = i;    /* our new rel id end */
1574
1575   for (i = 0; i < nrels; i++)
1576     needid[needid[reloff + i].map].need = nstrings + i;
1577
1578   /* now we have: needid[oldid].need -> newid
1579                   needid[newid].map  -> oldid
1580      both for strings and relations  */
1581
1582
1583 /********************************************************************/
1584
1585   ndirmap = 0;
1586   dirmap = 0;
1587   if (dirpool)
1588     {
1589       /* create our new target directory structure by traversing through all
1590        * used dirs. This will concatenate blocks with the same parent
1591        * directory into single blocks.
1592        * Instead of components, traverse_dirs stores the old dirids,
1593        * we will change this in the second step below */
1594       /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1595       if (cbdata.dirused && !cbdata.dirused[1])
1596         cbdata.dirused[1] = 1;  /* always want / entry */
1597       dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1598       dirmap[0] = 0;
1599       ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1600
1601       /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1602       /* change dirmap so that it maps from "new dirid" to "new compid" */
1603       if (!cbdata.dirused)
1604         cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1605       memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1606       for (i = 1; i < ndirmap; i++)
1607         {
1608           if (dirmap[i] <= 0)
1609             continue;
1610           cbdata.dirused[dirmap[i]] = i;
1611           id = dirpool->dirs[dirmap[i]];
1612           if (dirpooldata && cbdata.ownspool && id > 1)
1613             id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1614           dirmap[i] = needid[id].need;
1615         }
1616       /* now the new target directory structure is complete (dirmap), and we have
1617        * dirused[olddirid] -> newdirid */
1618     }
1619
1620 /********************************************************************/
1621
1622   /* collect all data
1623    * we use extdata[0] for incore data and extdata[keyid] for vertical data
1624    */
1625
1626   cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1627
1628   xd = cbdata.extdata;
1629   cbdata.current_sub = 0;
1630   /* add main schema */
1631   cbdata.lastlen = 0;
1632   data_addid(xd, mainschema);
1633
1634 #if 1
1635   FOR_REPODATAS(repo, j, data)
1636     {
1637       if (!repodataused[j])
1638         continue;
1639       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1640     }
1641 #endif
1642
1643   if (xd->len - cbdata.lastlen > cbdata.maxdata)
1644     cbdata.maxdata = xd->len - cbdata.lastlen;
1645   cbdata.lastlen = xd->len;
1646
1647   if (anysolvableused)
1648     {
1649       data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1650       cbdata.doingsolvables = 1;
1651       for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1652         {
1653           if (s->repo != repo)
1654             continue;
1655           data_addid(xd, cbdata.solvschemata[n]);
1656           if (cbdata.keymap[SOLVABLE_NAME])
1657             data_addid(xd, needid[s->name].need);
1658           if (cbdata.keymap[SOLVABLE_ARCH])
1659             data_addid(xd, needid[s->arch].need);
1660           if (cbdata.keymap[SOLVABLE_EVR])
1661             data_addid(xd, needid[s->evr].need);
1662           if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1663             data_addid(xd, needid[s->vendor].need);
1664           if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1665             data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1666           if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1667             data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1668           if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1669             data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1670           if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1671             data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1672           if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1673             data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1674           if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1675             data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1676           if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1677             data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1678           if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1679             data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1680           if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1681             data_addu32(xd, repo->rpmdbid[i - repo->start]);
1682           if (anyrepodataused)
1683             {
1684               cbdata.vstart = -1;
1685               FOR_REPODATAS(repo, j, data)
1686                 {
1687                   if (!repodataused[j])
1688                     continue;
1689                   if (i < data->start || i >= data->end)
1690                     continue;
1691                   repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1692                 }
1693             }
1694           if (xd->len - cbdata.lastlen > cbdata.maxdata)
1695             cbdata.maxdata = xd->len - cbdata.lastlen;
1696           cbdata.lastlen = xd->len;
1697           n++;
1698         }
1699       cbdata.doingsolvables = 0;
1700     }
1701
1702   assert(cbdata.current_sub == cbdata.nsubschemata);
1703   if (cbdata.subschemata)
1704     {
1705       cbdata.subschemata = solv_free(cbdata.subschemata);
1706       cbdata.nsubschemata = 0;
1707     }
1708
1709 /********************************************************************/
1710
1711   /* write header */
1712
1713   /* write file header */
1714   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1715   write_u32(fp, SOLV_VERSION_8);
1716
1717
1718   /* write counts */
1719   write_u32(fp, nstrings);
1720   write_u32(fp, nrels);
1721   write_u32(fp, ndirmap);
1722   write_u32(fp, anysolvableused ? repo->nsolvables : 0);
1723   write_u32(fp, target.nkeys);
1724   write_u32(fp, target.nschemata);
1725   solv_flags = 0;
1726   solv_flags |= SOLV_FLAG_PREFIX_POOL;
1727   write_u32(fp, solv_flags);
1728
1729   /*
1730    * calculate prefix encoding of the strings
1731    */
1732   prefixcomp = solv_malloc(nstrings);
1733   compsum = 0;
1734   old_str = "";
1735   
1736   prefixcomp[0] = 0;
1737   for (i = 1; i < nstrings; i++)
1738     {
1739       char *str = spool->stringspace + spool->strings[needid[i].map];
1740       int same;
1741       for (same = 0; same < 255; same++)
1742         if (!old_str[same] || old_str[same] != str[same])
1743           break;
1744       prefixcomp[i] = same;
1745       compsum += same;
1746       old_str = str;
1747     }
1748
1749   /*
1750    * write strings
1751    */
1752   write_u32(fp, sizeid);
1753   /* we save compsum bytes but need 1 extra byte for every string */
1754   write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1755   if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1756     {
1757       for (i = 1; i < nstrings; i++)
1758         {
1759           char *str = spool->stringspace + spool->strings[needid[i].map];
1760           write_u8(fp, prefixcomp[i]);
1761           write_str(fp, str + prefixcomp[i]);
1762         }
1763     }
1764   solv_free(prefixcomp);
1765
1766 #if 0
1767   /* Build the prefix-encoding of the string pool.  We need to know
1768      the size of that before writing it to the file, so we have to
1769      build a separate buffer for that.  As it's temporarily possible
1770      that this actually is an expansion we can't easily reuse the 
1771      stringspace for this.  The max expansion per string is 1 byte,
1772      so it will fit into sizeid+nstrings bytes.  */
1773   char *prefix = solv_malloc(sizeid + nstrings);
1774   char *pp = prefix;
1775   char *old_str = "";
1776   for (i = 1; i < nstrings; i++)
1777     {
1778       char *str = spool->stringspace + spool->strings[needid[i].map];
1779       int same;
1780       size_t len;
1781       for (same = 0; same < 255; same++)
1782         if (!old_str[same] || !str[same] || old_str[same] != str[same])
1783           break;
1784       *pp++ = same;
1785       len = strlen(str + same) + 1;
1786       memcpy(pp, str + same, len);
1787       pp += len;
1788       old_str = str;
1789     }
1790
1791   /*
1792    * write strings
1793    */
1794   write_u32(fp, sizeid);
1795   write_u32(fp, pp - prefix);
1796   if (pp != prefix)
1797     {
1798       if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1799         {
1800           perror("write error prefix");
1801           exit(1);
1802         }
1803     }
1804   solv_free(prefix);
1805 #endif
1806
1807   /*
1808    * write RelDeps
1809    */
1810   for (i = 0; i < nrels; i++)
1811     {
1812       ran = pool->rels + (needid[reloff + i].map - reloff);
1813       write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1814       write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1815       write_u8(fp, ran->flags);
1816     }
1817
1818   /*
1819    * write dirs (skip both root and / entry)
1820    */
1821   for (i = 2; i < ndirmap; i++)
1822     {
1823       if (dirmap[i] > 0)
1824         write_id(fp, dirmap[i]);
1825       else
1826         write_id(fp, nstrings - dirmap[i]);
1827     }
1828   solv_free(dirmap);
1829
1830   /*
1831    * write keys
1832    */
1833   for (i = 1; i < target.nkeys; i++)
1834     {
1835       write_id(fp, needid[target.keys[i].name].need);
1836       write_id(fp, needid[target.keys[i].type].need);
1837       if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1838         {
1839           if (target.keys[i].type == type_constantid)
1840             write_id(fp, needid[target.keys[i].size].need);
1841           else
1842             write_id(fp, target.keys[i].size);
1843         }
1844       else
1845         write_id(fp, cbdata.extdata[i].len);
1846       write_id(fp, target.keys[i].storage);
1847     }
1848
1849   /*
1850    * write schemata
1851    */
1852   write_id(fp, target.schemadatalen);   /* XXX -1? */
1853   for (i = 1; i < target.nschemata; i++)
1854     write_idarray(fp, pool, 0, repodata_id2schema(&target, i));
1855
1856 /********************************************************************/
1857
1858   write_id(fp, cbdata.maxdata);
1859   write_id(fp, cbdata.extdata[0].len);
1860   if (cbdata.extdata[0].len)
1861     write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1862   solv_free(cbdata.extdata[0].buf);
1863
1864   /* do we have vertical data? */
1865   for (i = 1; i < target.nkeys; i++)
1866     if (cbdata.extdata[i].len)
1867       break;
1868   if (i < target.nkeys)
1869     {
1870       /* yes, write it in pages */
1871       unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
1872       int l, ll, lpage = 0;
1873
1874       write_u32(fp, REPOPAGE_BLOBSIZE);
1875       for (i = 1; i < target.nkeys; i++)
1876         {
1877           if (!cbdata.extdata[i].len)
1878             continue;
1879           l = cbdata.extdata[i].len;
1880           dp = cbdata.extdata[i].buf;
1881           while (l)
1882             {
1883               ll = REPOPAGE_BLOBSIZE - lpage;
1884               if (l < ll)
1885                 ll = l;
1886               memcpy(vpage + lpage, dp, ll);
1887               dp += ll;
1888               lpage += ll;
1889               l -= ll;
1890               if (lpage == REPOPAGE_BLOBSIZE)
1891                 {
1892                   write_compressed_page(fp, vpage, lpage);
1893                   lpage = 0;
1894                 }
1895             }
1896         }
1897       if (lpage)
1898         write_compressed_page(fp, vpage, lpage);
1899     }
1900
1901   for (i = 1; i < target.nkeys; i++)
1902     solv_free(cbdata.extdata[i].buf);
1903   solv_free(cbdata.extdata);
1904
1905   repodata_freedata(&target);
1906
1907   solv_free(needid);
1908   solv_free(cbdata.solvschemata);
1909   solv_free(cbdata.schema);
1910
1911   solv_free(cbdata.keymap);
1912   solv_free(cbdata.keymapstart);
1913   solv_free(cbdata.dirused);
1914   solv_free(repodataused);
1915   return 0;
1916 }
1917
1918 struct repodata_write_data {
1919   int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1920   void *kfdata;
1921   int repodataid;
1922 };
1923
1924 static int
1925 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1926 {
1927   struct repodata_write_data *wd = kfdata;
1928
1929   /* XXX: special repodata selection hack */
1930   if (key->name == 1 && key->size != wd->repodataid)
1931     return -1;
1932   if (key->storage == KEY_STORAGE_SOLVABLE)
1933     return KEY_STORAGE_DROPPED; /* not part of this repodata */
1934   if (wd->keyfilter)
1935     return (*wd->keyfilter)(repo, key, wd->kfdata);
1936   return key->storage;
1937 }
1938
1939 int
1940 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1941 {
1942   struct repodata_write_data wd;
1943
1944   wd.keyfilter = keyfilter;
1945   wd.kfdata = kfdata;
1946   wd.repodataid = data->repodataid;
1947   return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
1948 }
1949
1950 int
1951 repodata_write(Repodata *data, FILE *fp)
1952 {
1953   return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
1954 }
1955
1956 int
1957 repo_write(Repo *repo, FILE *fp)
1958 {
1959   return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);
1960 }