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