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