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