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