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