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