- put SOLVABLE_PKGID, SOLVABLE_HDRID, SOLVABLE_LEADSIGID in the vertical section...
[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   unsigned char *prefixcomp;
1064   unsigned int compsum;
1065   char *old_str;
1066
1067
1068   memset(&cbdata, 0, sizeof(cbdata));
1069   cbdata.repo = repo;
1070   cbdata.target = &target;
1071
1072   repodata_initdata(&target, repo, 1);
1073
1074   /* go through all repodata and find the keys we need */
1075   /* also unify keys */
1076   /*          keymapstart - maps repo number to keymap offset */
1077   /*          keymap      - maps repo key to my key, 0 -> not used */
1078
1079   /* start with all KEY_STORAGE_SOLVABLE ids */
1080
1081   n = ID_NUM_INTERNAL;
1082   FOR_REPODATAS(repo, i, data)
1083     n += data->nkeys;
1084   cbdata.keymap = solv_calloc(n, sizeof(Id));
1085   cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1086   repodataused = solv_calloc(repo->nrepodata, 1);
1087
1088   clonepool = 0;
1089   poolusage = 0;
1090
1091   /* add keys for STORAGE_SOLVABLE */
1092   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1093     {
1094       Repokey keyd;
1095       keyd.name = i;
1096       if (i < SOLVABLE_PROVIDES)
1097         keyd.type = REPOKEY_TYPE_ID;
1098       else if (i < RPM_RPMDBID)
1099         keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1100       else
1101         keyd.type = REPOKEY_TYPE_NUM;
1102       keyd.size = 0;
1103       keyd.storage = KEY_STORAGE_SOLVABLE;
1104       if (keyfilter)
1105         {
1106           keyd.storage = keyfilter(repo, &keyd, kfdata);
1107           if (keyd.storage == KEY_STORAGE_DROPPED)
1108             continue;
1109           keyd.storage = KEY_STORAGE_SOLVABLE;
1110         }
1111       poolusage = 1;
1112       clonepool = 1;
1113       cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1114     }
1115
1116   if (repo->nsolvables)
1117     {
1118       Repokey keyd;
1119       keyd.name = REPOSITORY_SOLVABLES;
1120       keyd.type = REPOKEY_TYPE_FLEXARRAY;
1121       keyd.size = 0;
1122       keyd.storage = KEY_STORAGE_INCORE;
1123       cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1124     }
1125
1126   dirpoolusage = 0;
1127
1128   spool = 0;
1129   dirpool = 0;
1130   dirpooldata = 0;
1131   n = ID_NUM_INTERNAL;
1132   FOR_REPODATAS(repo, i, data)
1133     {
1134       cbdata.keymapstart[i] = n;
1135       cbdata.keymap[n++] = 0;   /* key 0 */
1136       idused = 0;
1137       dirused = 0;
1138       if (keyfilter)
1139         {
1140           Repokey keyd;
1141           /* check if we want this repodata */
1142           memset(&keyd, 0, sizeof(keyd));
1143           keyd.name = 1;
1144           keyd.type = 1;
1145           keyd.size = i;
1146           if (keyfilter(repo, &keyd, kfdata) == -1)
1147             continue;
1148         }
1149       for (j = 1; j < data->nkeys; j++, n++)
1150         {
1151           key = data->keys + j;
1152           if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1153             {
1154               cbdata.keymap[n] = cbdata.keymap[key->name];
1155               continue;
1156             }
1157           if (key->type == REPOKEY_TYPE_DELETED)
1158             {
1159               cbdata.keymap[n] = 0;
1160               continue;
1161             }
1162           if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1163             {
1164               Repokey keyd = *key;
1165               keyd.size = repodata_globalize_id(data, key->size, 1);
1166               id = repodata_key2id(&target, &keyd, 0);
1167             }
1168           else
1169             id = repodata_key2id(&target, key, 0);
1170           if (!id)
1171             {
1172               Repokey keyd = *key;
1173               keyd.storage = KEY_STORAGE_INCORE;
1174               if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1175                 keyd.size = repodata_globalize_id(data, key->size, 1);
1176               else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1177                 keyd.size = 0;
1178               if (keyfilter)
1179                 {
1180                   keyd.storage = keyfilter(repo, &keyd, kfdata);
1181                   if (keyd.storage == KEY_STORAGE_DROPPED)
1182                     {
1183                       cbdata.keymap[n] = 0;
1184                       continue;
1185                     }
1186                 }
1187               id = repodata_key2id(&target, &keyd, 1);
1188             }
1189           cbdata.keymap[n] = id;
1190           /* load repodata if not already loaded */
1191           if (data->state == REPODATA_STUB)
1192             {
1193               if (data->loadcallback)
1194                 data->loadcallback(data);
1195               else
1196                 data->state = REPODATA_ERROR;
1197               if (data->state != REPODATA_ERROR)
1198                 {
1199                   /* redo this repodata! */
1200                   j = 0;
1201                   n = cbdata.keymapstart[i];
1202                   continue;
1203                 }
1204             }
1205           if (data->state == REPODATA_ERROR)
1206             {
1207               /* too bad! */
1208               cbdata.keymap[n] = 0;
1209               continue;
1210             }
1211
1212           repodataused[i] = 1;
1213           anyrepodataused = 1;
1214           if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1215               key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1216             idused = 1;
1217           else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1218             {
1219               idused = 1;       /* dirs also use ids */
1220               dirused = 1;
1221             }
1222         }
1223       if (idused)
1224         {
1225           if (data->localpool)
1226             {
1227               if (poolusage)
1228                 poolusage = 3;  /* need own pool */
1229               else
1230                 {
1231                   poolusage = 2;
1232                   spool = &data->spool;
1233                 }
1234             }
1235           else
1236             {
1237               if (poolusage == 0)
1238                 poolusage = 1;
1239               else if (poolusage != 1)
1240                 poolusage = 3;  /* need own pool */
1241             }
1242         }
1243       if (dirused)
1244         {
1245           if (dirpoolusage)
1246             dirpoolusage = 3;   /* need own dirpool */
1247           else
1248             {
1249               dirpoolusage = 2;
1250               dirpool = &data->dirpool;
1251               dirpooldata = data;
1252             }
1253         }
1254     }
1255   cbdata.nkeymap = n;
1256
1257   /* 0: no pool needed at all */
1258   /* 1: use global pool */
1259   /* 2: use repodata local pool */
1260   /* 3: need own pool */
1261   if (poolusage == 3)
1262     {
1263       spool = &target.spool;
1264       /* hack: reuse global pool data so we don't have to map pool ids */
1265       if (clonepool)
1266         {
1267           stringpool_free(spool);
1268           stringpool_clone(spool, &pool->ss);
1269         }
1270       cbdata.ownspool = spool;
1271     }
1272   else if (poolusage == 0 || poolusage == 1)
1273     {
1274       poolusage = 1;
1275       spool = &pool->ss;
1276     }
1277
1278   if (dirpoolusage == 3)
1279     {
1280       dirpool = &target.dirpool;
1281       dirpooldata = 0;
1282       cbdata.owndirpool = dirpool;
1283     }
1284   else if (dirpool)
1285     cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1286
1287
1288 /********************************************************************/
1289 #if 0
1290 fprintf(stderr, "poolusage: %d\n", poolusage);
1291 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1292 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1293 for (i = 1; i < target.nkeys; i++)
1294   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);
1295 #endif
1296
1297   /* copy keys if requested */
1298   if (keyq)
1299     {
1300       queue_empty(keyq);
1301       for (i = 1; i < target.nkeys; i++)
1302         queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1303     }
1304
1305   if (poolusage > 1)
1306     {
1307       /* put all the keys we need in our string pool */
1308       /* put mapped ids right into target.keys */
1309       for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1310         {
1311           key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1312           if (key->type == REPOKEY_TYPE_CONSTANTID)
1313             {
1314               key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1315               type_constantid = key->type;
1316               key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1317             }
1318           else
1319             key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1320         }
1321       if (poolusage == 2)
1322         stringpool_freehash(spool);     /* free some mem */
1323     }
1324
1325
1326 /********************************************************************/
1327
1328   /* set needed count of all strings and rels,
1329    * find which keys are used in the solvables
1330    * put all strings in own spool
1331    */
1332
1333   reloff = spool->nstrings;
1334   if (poolusage == 3)
1335     reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1336
1337   needid = calloc(reloff + pool->nrels, sizeof(*needid));
1338   needid[0].map = reloff;
1339
1340   cbdata.needid = needid;
1341   cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1342   cbdata.sp = cbdata.schema;
1343   cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1344
1345   /* create main schema */
1346   cbdata.sp = cbdata.schema;
1347   /* collect all other data from all repodatas */
1348   /* XXX: merge arrays of equal keys? */
1349   FOR_REPODATAS(repo, j, data)
1350     {
1351       if (!repodataused[j])
1352         continue;
1353       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1354     }
1355   sp = cbdata.sp;
1356   /* add solvables if needed (may revert later) */
1357   if (repo->nsolvables)
1358     {
1359       *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1360       target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1361     }
1362   *sp = 0;
1363   mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1364
1365   idarraydata = repo->idarraydata;
1366
1367   anysolvableused = 0;
1368   cbdata.doingsolvables = 1;
1369   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1370     {
1371       if (s->repo != repo)
1372         continue;
1373
1374       /* set schema info, keep in sync with further down */
1375       sp = cbdata.schema;
1376       if (cbdata.keymap[SOLVABLE_NAME])
1377         {
1378           *sp++ = cbdata.keymap[SOLVABLE_NAME];
1379           needid[s->name].need++;
1380         }
1381       if (cbdata.keymap[SOLVABLE_ARCH])
1382         {
1383           *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1384           needid[s->arch].need++;
1385         }
1386       if (cbdata.keymap[SOLVABLE_EVR])
1387         {
1388           *sp++ = cbdata.keymap[SOLVABLE_EVR];
1389           needid[s->evr].need++;
1390         }
1391       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1392         {
1393           *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1394           needid[s->vendor].need++;
1395         }
1396       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1397         {
1398           *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1399           target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1400         }
1401       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1402         {
1403           *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1404           target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1405         }
1406       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1407         {
1408           *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1409           target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1410         }
1411       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1412         {
1413           *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1414           target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1415         }
1416       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1417         {
1418           *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1419           target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1420         }
1421       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1422         {
1423           *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1424           target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1425         }
1426       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1427         {
1428           *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1429           target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1430         }
1431       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1432         {
1433           *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1434           target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1435         }
1436       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1437         {
1438           *sp++ = cbdata.keymap[RPM_RPMDBID];
1439           target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1440         }
1441       cbdata.sp = sp;
1442
1443       if (anyrepodataused)
1444         {
1445           FOR_REPODATAS(repo, j, data)
1446             {
1447               if (!repodataused[j])
1448                 continue;
1449               if (i < data->start || i >= data->end)
1450                 continue;
1451               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1452               needid = cbdata.needid;
1453             }
1454         }
1455       *cbdata.sp = 0;
1456       cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1457       if (cbdata.solvschemata[n])
1458         anysolvableused = 1;
1459       n++;
1460     }
1461   cbdata.doingsolvables = 0;
1462   assert(n == repo->nsolvables);
1463
1464   if (repo->nsolvables && !anysolvableused)
1465     {
1466       /* strip off solvable from the main schema */
1467       target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1468       sp = cbdata.schema;
1469       for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1470         {
1471           *sp = target.schemadata[target.schemata[mainschema] + i];
1472           if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1473             sp++;
1474         }
1475       assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1476       *sp = 0;
1477       target.schemadatalen = target.schemata[mainschema];
1478       target.nschemata--;
1479       repodata_free_schemahash(&target);
1480       mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1481     }
1482
1483 /********************************************************************/
1484
1485   /* remove unused keys */
1486   keyused = solv_calloc(target.nkeys, sizeof(Id));
1487   for (i = 1; i < target.schemadatalen; i++)
1488     keyused[target.schemadata[i]] = 1;
1489   keyused[0] = 0;
1490   for (n = i = 1; i < target.nkeys; i++)
1491     {
1492       if (!keyused[i])
1493         continue;
1494       keyused[i] = n;
1495       if (i != n)
1496         {
1497           target.keys[n] = target.keys[i];
1498           if (keyq)
1499             {
1500               keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1501               keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1502             }
1503         }
1504       n++;
1505     }
1506   target.nkeys = n;
1507   if (keyq)
1508     queue_truncate(keyq, 2 * n - 2);
1509
1510   /* update schema data to the new key ids */
1511   for (i = 1; i < target.schemadatalen; i++)
1512     target.schemadata[i] = keyused[target.schemadata[i]];
1513   /* update keymap to the new key ids */
1514   for (i = 0; i < cbdata.nkeymap; i++)
1515     cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1516   keyused = solv_free(keyused);
1517
1518   /* increment needid of the used keys, they are already mapped to
1519    * the correct string pool  */
1520   for (i = 1; i < target.nkeys; i++)
1521     {
1522       if (target.keys[i].type == type_constantid)
1523         needid[target.keys[i].size].need++;
1524       needid[target.keys[i].name].need++;
1525       needid[target.keys[i].type].need++;
1526     }
1527
1528 /********************************************************************/
1529
1530   if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1531     {
1532       /* no dirs used at all */
1533       cbdata.dirused = solv_free(cbdata.dirused);
1534       dirpool = 0;
1535     }
1536
1537   /* increment need id for used dir components */
1538   if (dirpool)
1539     {
1540       /* if we have own dirpool, all entries in it are used.
1541          also, all comp ids are already mapped by putinowndirpool(),
1542          so we can simply increment needid.
1543          (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1544       /* else we re-use a dirpool of repodata "dirpooldata".
1545          dirused tells us which of the ids are used.
1546          we need to map comp ids if we generate a new pool.
1547          (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1548       for (i = 1; i < dirpool->ndirs; i++)
1549         {
1550 #if 0
1551 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1552 #endif
1553           if (cbdata.dirused && !cbdata.dirused[i])
1554             continue;
1555           id = dirpool->dirs[i];
1556           if (id <= 0)
1557             continue;
1558           if (dirpooldata && cbdata.ownspool && id > 1)
1559             {
1560               id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1561               needid = cbdata.needid;
1562             }
1563           needid[id].need++;
1564         }
1565     }
1566
1567
1568 /********************************************************************/
1569
1570   /*
1571    * create mapping table, new keys are sorted by needid[].need
1572    *
1573    * needid[key].need : old key -> new key
1574    * needid[key].map  : new key -> old key
1575    */
1576
1577   /* zero out id 0 and rel 0 just in case */
1578   reloff = needid[0].map;
1579   needid[0].need = 0;
1580   needid[reloff].need = 0;
1581
1582   for (i = 1; i < reloff + pool->nrels; i++)
1583     needid[i].map = i;
1584
1585 #if 0
1586   solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1587 #else
1588   /* make first entry '' */
1589   needid[1].need = 1;
1590   solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1591 #endif
1592   solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1593   /* now needid is in new order, needid[newid].map -> oldid */
1594
1595   /* calculate string space size, also zero out needid[].need */
1596   sizeid = 0;
1597   for (i = 1; i < reloff; i++)
1598     {
1599       if (!needid[i].need)
1600         break;  /* as we have sorted, every entry after this also has need == 0 */
1601       needid[i].need = 0;
1602       sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1603     }
1604   nstrings = i; /* our new string id end */
1605
1606   /* make needid[oldid].need point to newid */
1607   for (i = 1; i < nstrings; i++)
1608     needid[needid[i].map].need = i;
1609
1610   /* same as above for relations */
1611   for (i = 0; i < pool->nrels; i++)
1612     {
1613       if (!needid[reloff + i].need)
1614         break;
1615       needid[reloff + i].need = 0;
1616     }
1617   nrels = i;    /* our new rel id end */
1618
1619   for (i = 0; i < nrels; i++)
1620     needid[needid[reloff + i].map].need = nstrings + i;
1621
1622   /* now we have: needid[oldid].need -> newid
1623                   needid[newid].map  -> oldid
1624      both for strings and relations  */
1625
1626
1627 /********************************************************************/
1628
1629   ndirmap = 0;
1630   dirmap = 0;
1631   if (dirpool)
1632     {
1633       /* create our new target directory structure by traversing through all
1634        * used dirs. This will concatenate blocks with the same parent
1635        * directory into single blocks.
1636        * Instead of components, traverse_dirs stores the old dirids,
1637        * we will change this in the second step below */
1638       /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1639       if (cbdata.dirused && !cbdata.dirused[1])
1640         cbdata.dirused[1] = 1;  /* always want / entry */
1641       dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1642       dirmap[0] = 0;
1643       ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1644
1645       /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1646       /* change dirmap so that it maps from "new dirid" to "new compid" */
1647       if (!cbdata.dirused)
1648         cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1649       memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1650       for (i = 1; i < ndirmap; i++)
1651         {
1652           if (dirmap[i] <= 0)
1653             continue;
1654           cbdata.dirused[dirmap[i]] = i;
1655           id = dirpool->dirs[dirmap[i]];
1656           if (dirpooldata && cbdata.ownspool && id > 1)
1657             id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1658           dirmap[i] = needid[id].need;
1659         }
1660       /* now the new target directory structure is complete (dirmap), and we have
1661        * dirused[olddirid] -> newdirid */
1662     }
1663
1664 /********************************************************************/
1665
1666   /* collect all data
1667    * we use extdata[0] for incore data and extdata[keyid] for vertical data
1668    */
1669
1670   cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1671
1672   xd = cbdata.extdata;
1673   cbdata.current_sub = 0;
1674   /* add main schema */
1675   cbdata.lastlen = 0;
1676   data_addid(xd, mainschema);
1677
1678 #if 1
1679   FOR_REPODATAS(repo, j, data)
1680     {
1681       if (!repodataused[j])
1682         continue;
1683       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1684     }
1685 #endif
1686
1687   if (xd->len - cbdata.lastlen > cbdata.maxdata)
1688     cbdata.maxdata = xd->len - cbdata.lastlen;
1689   cbdata.lastlen = xd->len;
1690
1691   if (anysolvableused)
1692     {
1693       data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1694       cbdata.doingsolvables = 1;
1695       for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1696         {
1697           if (s->repo != repo)
1698             continue;
1699           data_addid(xd, cbdata.solvschemata[n]);
1700           if (cbdata.keymap[SOLVABLE_NAME])
1701             data_addid(xd, needid[s->name].need);
1702           if (cbdata.keymap[SOLVABLE_ARCH])
1703             data_addid(xd, needid[s->arch].need);
1704           if (cbdata.keymap[SOLVABLE_EVR])
1705             data_addid(xd, needid[s->evr].need);
1706           if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1707             data_addid(xd, needid[s->vendor].need);
1708           if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1709             data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1710           if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1711             data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1712           if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1713             data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1714           if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1715             data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1716           if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1717             data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1718           if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1719             data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1720           if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1721             data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1722           if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1723             data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1724           if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1725             data_addid(xd, repo->rpmdbid[i - repo->start]);
1726           if (anyrepodataused)
1727             {
1728               cbdata.vstart = -1;
1729               FOR_REPODATAS(repo, j, data)
1730                 {
1731                   if (!repodataused[j])
1732                     continue;
1733                   if (i < data->start || i >= data->end)
1734                     continue;
1735                   repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1736                 }
1737             }
1738           if (xd->len - cbdata.lastlen > cbdata.maxdata)
1739             cbdata.maxdata = xd->len - cbdata.lastlen;
1740           cbdata.lastlen = xd->len;
1741           n++;
1742         }
1743       cbdata.doingsolvables = 0;
1744     }
1745
1746   assert(cbdata.current_sub == cbdata.nsubschemata);
1747   if (cbdata.subschemata)
1748     {
1749       cbdata.subschemata = solv_free(cbdata.subschemata);
1750       cbdata.nsubschemata = 0;
1751     }
1752
1753 /********************************************************************/
1754
1755   target.fp = fp;
1756
1757   /* write header */
1758
1759   /* write file header */
1760   write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1761   write_u32(&target, SOLV_VERSION_8);
1762
1763
1764   /* write counts */
1765   write_u32(&target, nstrings);
1766   write_u32(&target, nrels);
1767   write_u32(&target, ndirmap);
1768   write_u32(&target, anysolvableused ? repo->nsolvables : 0);
1769   write_u32(&target, target.nkeys);
1770   write_u32(&target, target.nschemata);
1771   solv_flags = 0;
1772   solv_flags |= SOLV_FLAG_PREFIX_POOL;
1773   solv_flags |= SOLV_FLAG_SIZE_BYTES;
1774   write_u32(&target, solv_flags);
1775
1776   /*
1777    * calculate prefix encoding of the strings
1778    */
1779   prefixcomp = solv_malloc(nstrings);
1780   compsum = 0;
1781   old_str = "";
1782
1783   prefixcomp[0] = 0;
1784   for (i = 1; i < nstrings; i++)
1785     {
1786       char *str = spool->stringspace + spool->strings[needid[i].map];
1787       int same;
1788       for (same = 0; same < 255; same++)
1789         if (!old_str[same] || old_str[same] != str[same])
1790           break;
1791       prefixcomp[i] = same;
1792       compsum += same;
1793       old_str = str;
1794     }
1795
1796   /*
1797    * write strings
1798    */
1799   write_u32(&target, sizeid);
1800   /* we save compsum bytes but need 1 extra byte for every string */
1801   write_u32(&target, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1802   if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1803     {
1804       for (i = 1; i < nstrings; i++)
1805         {
1806           char *str = spool->stringspace + spool->strings[needid[i].map];
1807           write_u8(&target, prefixcomp[i]);
1808           write_str(&target, str + prefixcomp[i]);
1809         }
1810     }
1811   solv_free(prefixcomp);
1812
1813 #if 0
1814   /* Build the prefix-encoding of the string pool.  We need to know
1815      the size of that before writing it to the file, so we have to
1816      build a separate buffer for that.  As it's temporarily possible
1817      that this actually is an expansion we can't easily reuse the
1818      stringspace for this.  The max expansion per string is 1 byte,
1819      so it will fit into sizeid+nstrings bytes.  */
1820   char *prefix = solv_malloc(sizeid + nstrings);
1821   char *pp = prefix;
1822   char *old_str = "";
1823   for (i = 1; i < nstrings; i++)
1824     {
1825       char *str = spool->stringspace + spool->strings[needid[i].map];
1826       int same;
1827       size_t len;
1828       for (same = 0; same < 255; same++)
1829         if (!old_str[same] || !str[same] || old_str[same] != str[same])
1830           break;
1831       *pp++ = same;
1832       len = strlen(str + same) + 1;
1833       memcpy(pp, str + same, len);
1834       pp += len;
1835       old_str = str;
1836     }
1837
1838   /*
1839    * write strings
1840    */
1841   write_u32(&target, sizeid);
1842   write_u32(&target, pp - prefix);
1843   write_blob(&target, prefix, pp - prefix);
1844   solv_free(prefix);
1845 #endif
1846
1847   /*
1848    * write RelDeps
1849    */
1850   for (i = 0; i < nrels; i++)
1851     {
1852       ran = pool->rels + (needid[reloff + i].map - reloff);
1853       write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1854       write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1855       write_u8(&target, ran->flags);
1856     }
1857
1858   /*
1859    * write dirs (skip both root and / entry)
1860    */
1861   for (i = 2; i < ndirmap; i++)
1862     {
1863       if (dirmap[i] > 0)
1864         write_id(&target, dirmap[i]);
1865       else
1866         write_id(&target, nstrings - dirmap[i]);
1867     }
1868   solv_free(dirmap);
1869
1870   /*
1871    * write keys
1872    */
1873   for (i = 1; i < target.nkeys; i++)
1874     {
1875       write_id(&target, needid[target.keys[i].name].need);
1876       write_id(&target, needid[target.keys[i].type].need);
1877       if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1878         {
1879           if (target.keys[i].type == type_constantid)
1880             write_id(&target, needid[target.keys[i].size].need);
1881           else
1882             write_id(&target, target.keys[i].size);
1883         }
1884       else
1885         write_id(&target, cbdata.extdata[i].len);
1886       write_id(&target, target.keys[i].storage);
1887     }
1888
1889   /*
1890    * write schemata
1891    */
1892   write_id(&target, target.schemadatalen);      /* XXX -1? */
1893   for (i = 1; i < target.nschemata; i++)
1894     write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
1895
1896 /********************************************************************/
1897
1898   write_id(&target, cbdata.maxdata);
1899   write_id(&target, cbdata.extdata[0].len);
1900   if (cbdata.extdata[0].len)
1901     write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1902   solv_free(cbdata.extdata[0].buf);
1903
1904   /* do we have vertical data? */
1905   for (i = 1; i < target.nkeys; i++)
1906     if (cbdata.extdata[i].len)
1907       break;
1908   if (i < target.nkeys)
1909     {
1910       /* yes, write it in pages */
1911       unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
1912       int l, ll, lpage = 0;
1913
1914       write_u32(&target, REPOPAGE_BLOBSIZE);
1915       for (i = 1; i < target.nkeys; i++)
1916         {
1917           if (!cbdata.extdata[i].len)
1918             continue;
1919           l = cbdata.extdata[i].len;
1920           dp = cbdata.extdata[i].buf;
1921           while (l)
1922             {
1923               ll = REPOPAGE_BLOBSIZE - lpage;
1924               if (l < ll)
1925                 ll = l;
1926               memcpy(vpage + lpage, dp, ll);
1927               dp += ll;
1928               lpage += ll;
1929               l -= ll;
1930               if (lpage == REPOPAGE_BLOBSIZE)
1931                 {
1932                   write_compressed_page(&target, vpage, lpage);
1933                   lpage = 0;
1934                 }
1935             }
1936         }
1937       if (lpage)
1938         write_compressed_page(&target, vpage, lpage);
1939     }
1940
1941   for (i = 1; i < target.nkeys; i++)
1942     solv_free(cbdata.extdata[i].buf);
1943   solv_free(cbdata.extdata);
1944
1945   target.fp = 0;
1946   repodata_freedata(&target);
1947
1948   solv_free(needid);
1949   solv_free(cbdata.solvschemata);
1950   solv_free(cbdata.schema);
1951
1952   solv_free(cbdata.keymap);
1953   solv_free(cbdata.keymapstart);
1954   solv_free(cbdata.dirused);
1955   solv_free(repodataused);
1956   return target.error;
1957 }
1958
1959 struct repodata_write_data {
1960   int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1961   void *kfdata;
1962   int repodataid;
1963 };
1964
1965 static int
1966 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1967 {
1968   struct repodata_write_data *wd = kfdata;
1969
1970   /* XXX: special repodata selection hack */
1971   if (key->name == 1 && key->size != wd->repodataid)
1972     return -1;
1973   if (key->storage == KEY_STORAGE_SOLVABLE)
1974     return KEY_STORAGE_DROPPED; /* not part of this repodata */
1975   if (wd->keyfilter)
1976     return (*wd->keyfilter)(repo, key, wd->kfdata);
1977   return key->storage;
1978 }
1979
1980 int
1981 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1982 {
1983   struct repodata_write_data wd;
1984
1985   wd.keyfilter = keyfilter;
1986   wd.kfdata = kfdata;
1987   wd.repodataid = data->repodataid;
1988   return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
1989 }
1990
1991 int
1992 repodata_write(Repodata *data, FILE *fp)
1993 {
1994   return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
1995 }
1996
1997 int
1998 repo_write(Repo *repo, FILE *fp)
1999 {
2000   return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);
2001 }