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