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