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