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)
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 Stringpool *cmp_spool;
111
112 static int
113 needid_cmp_need_s(const void *ap, const void *bp)
114 {
115   const NeedId *a = ap;
116   const NeedId *b = bp;
117   int r;
118   r = b->need - a->need;
119   if (r)
120     return r;
121   const char *as = cmp_spool->stringspace + cmp_spool->strings[a->map];
122   const char *bs = cmp_spool->stringspace + cmp_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)
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     qsort(sids, i, sizeof (Id), cmp_ids);
307   if ((len - i) > 2)
308     qsort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids);
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     qsort(sids, i, sizeof (Id), cmp_ids);
482   if ((len - i) > 2)
483     qsort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids);
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_create(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   cmp_spool = spool;
1513 #if 0
1514   qsort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s);
1515 #else
1516   /* make first entry '' */
1517   needid[1].need = 1;
1518   qsort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s);
1519 #endif
1520   qsort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need);
1521
1522   sizeid = 0;
1523   for (i = 1; i < reloff; i++)
1524     {
1525       if (!needid[i].need)
1526         break;
1527       needid[i].need = 0;
1528       sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1529     }
1530
1531   nstrings = i;
1532   for (i = 1; i < nstrings; i++)
1533     needid[needid[i].map].need = i;
1534
1535   for (i = 0; i < pool->nrels; i++)
1536     {
1537       if (!needid[reloff + i].need)
1538         break;
1539       else
1540         needid[reloff + i].need = 0;
1541     }
1542
1543   nrels = i;
1544   for (i = 0; i < nrels; i++)
1545     needid[needid[reloff + i].map].need = nstrings + i;
1546
1547
1548 /********************************************************************/
1549
1550   /* create dir map */
1551   ndirmap = 0;
1552   dirmap = 0;
1553   if (dirpool)
1554     {
1555       if (cbdata.dirused && !cbdata.dirused[1])
1556         cbdata.dirused[1] = 1;  /* always want / entry */
1557       dirmap = sat_calloc(dirpool->ndirs, sizeof(Id));
1558       dirmap[0] = 0;
1559       ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1560       if (!cbdata.dirused)
1561         cbdata.dirused = sat_malloc2(dirpool->ndirs, sizeof(Id));
1562       memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1563       for (i = 1; i < ndirmap; i++)
1564         {
1565           if (dirmap[i] <= 0)
1566             continue;
1567           cbdata.dirused[dirmap[i]] = i;
1568           id = dirpool->dirs[dirmap[i]];
1569           if (cbdata.ownspool && dirpooldata && id > 1)
1570             id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1571           dirmap[i] = needid[id].need;
1572         }
1573     }
1574
1575 /********************************************************************/
1576   cbdata.extdata = sat_calloc(cbdata.nmykeys, sizeof(struct extdata));
1577
1578   xd = cbdata.extdata;
1579   cbdata.current_sub = 0;
1580   /* write main schema */
1581   cbdata.lastlen = 0;
1582   data_addid(xd, mainschema);
1583
1584 #if 1
1585   for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1586     repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1587 #endif
1588
1589   if (xd->len - cbdata.lastlen > cbdata.maxdata)
1590     cbdata.maxdata = xd->len - cbdata.lastlen;
1591   cbdata.lastlen = xd->len;
1592
1593   if (repo->nsolvables)
1594     data_addid(xd, repo->nsolvables);   /* FLEXARRAY nentries */
1595   cbdata.doingsolvables = 1;
1596   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1597     {
1598       if (s->repo != repo)
1599         continue;
1600       data_addid(xd, cbdata.solvschemata[n]);
1601       if (cbdata.keymap[SOLVABLE_NAME])
1602         data_addid(xd, needid[s->name].need);
1603       if (cbdata.keymap[SOLVABLE_ARCH])
1604         data_addid(xd, needid[s->arch].need);
1605       if (cbdata.keymap[SOLVABLE_EVR])
1606         data_addid(xd, needid[s->evr].need);
1607       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1608         data_addid(xd, needid[s->vendor].need);
1609       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1610         data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1611       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1612         data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1613       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1614         data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1615       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1616         data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1617       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1618         data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1619       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1620         data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1621       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1622         data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1623       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1624         data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1625       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1626         data_addu32(xd, repo->rpmdbid[i - repo->start]);
1627       if (anyrepodataused)
1628         {
1629           cbdata.vstart = -1;
1630           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1631             {
1632               if (!repodataused[j])
1633                 continue;
1634               if (i < data->start || i >= data->end)
1635                 continue;
1636               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1637             }
1638         }
1639       if (xd->len - cbdata.lastlen > cbdata.maxdata)
1640         cbdata.maxdata = xd->len - cbdata.lastlen;
1641       cbdata.lastlen = xd->len;
1642       n++;
1643     }
1644   cbdata.doingsolvables = 0;
1645
1646   assert(cbdata.current_sub == cbdata.nsubschemata);
1647   if (cbdata.subschemata)
1648     {
1649       cbdata.subschemata = sat_free(cbdata.subschemata);
1650       cbdata.nsubschemata = 0;
1651     }
1652
1653 #if 0
1654   if (repo->nextra && anyrepodataused)
1655     for (i = -1; i >= -repo->nextra; i--)
1656       {
1657         Dataiterator di;
1658         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1659         entrysize = xd->len;
1660         data_addid(xd, cbdata.extraschemata[-1 - i]);
1661         cbdata.vstart = -1;
1662         while (dataiterator_step(&di))
1663           repo_write_adddata(&cbdata, di.data, di.key, &di.kv);
1664         entrysize = xd->len - entrysize;
1665         if (entrysize > maxentrysize)
1666           maxentrysize = entrysize;
1667       }
1668 #endif
1669
1670 /********************************************************************/
1671
1672   /* write header */
1673
1674   /* write file header */
1675   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1676   write_u32(fp, SOLV_VERSION_8);
1677
1678
1679   /* write counts */
1680   write_u32(fp, nstrings);
1681   write_u32(fp, nrels);
1682   write_u32(fp, ndirmap);
1683   write_u32(fp, repo->nsolvables);
1684   write_u32(fp, cbdata.nmykeys);
1685   write_u32(fp, cbdata.nmyschemata);
1686   solv_flags = 0;
1687   solv_flags |= SOLV_FLAG_PREFIX_POOL;
1688   write_u32(fp, solv_flags);
1689
1690   /* Build the prefix-encoding of the string pool.  We need to know
1691      the size of that before writing it to the file, so we have to
1692      build a separate buffer for that.  As it's temporarily possible
1693      that this actually is an expansion we can't easily reuse the 
1694      stringspace for this.  The max expansion per string is 1 byte,
1695      so it will fit into sizeid+nstrings bytes.  */
1696   char *prefix = sat_malloc(sizeid + nstrings);
1697   char *pp = prefix;
1698   char *old_str = "";
1699   for (i = 1; i < nstrings; i++)
1700     {
1701       char *str = spool->stringspace + spool->strings[needid[i].map];
1702       int same;
1703       size_t len;
1704       for (same = 0; same < 255; same++)
1705         if (!old_str[same] || !str[same] || old_str[same] != str[same])
1706           break;
1707       *pp++ = same;
1708       len = strlen(str + same) + 1;
1709       memcpy (pp, str + same, len);
1710       pp += len;
1711       old_str = str;
1712     }
1713
1714   /*
1715    * write strings
1716    */
1717   write_u32(fp, sizeid);
1718   write_u32(fp, pp - prefix);
1719   if (pp != prefix)
1720     {
1721       if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1722         {
1723           perror("write error prefix");
1724           exit(1);
1725         }
1726     }
1727   sat_free(prefix);
1728
1729   /*
1730    * write RelDeps
1731    */
1732   for (i = 0; i < nrels; i++)
1733     {
1734       ran = pool->rels + (needid[reloff + i].map - reloff);
1735       write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1736       write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1737       write_u8(fp, ran->flags);
1738     }
1739
1740   /*
1741    * write dirs (skip both root and / entry)
1742    */
1743   for (i = 2; i < ndirmap; i++)
1744     {
1745       if (dirmap[i] > 0)
1746         write_id(fp, dirmap[i]);
1747       else
1748         write_id(fp, nstrings - dirmap[i]);
1749     }
1750   sat_free(dirmap);
1751
1752   /*
1753    * write keys
1754    */
1755   if (keyarrayp)
1756     *keyarrayp = sat_calloc(2 * cbdata.nmykeys + 1, sizeof(Id));
1757   for (i = 1; i < cbdata.nmykeys; i++)
1758     {
1759       write_id(fp, needid[cbdata.mykeys[i].name].need);
1760       write_id(fp, needid[cbdata.mykeys[i].type].need);
1761       if (cbdata.mykeys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1762         {
1763           if (cbdata.mykeys[i].type == type_constantid)
1764             write_id(fp, needid[cbdata.mykeys[i].size].need);
1765           else
1766             write_id(fp, cbdata.mykeys[i].size);
1767         }
1768       else
1769         write_id(fp, cbdata.extdata[i].len);
1770       write_id(fp, cbdata.mykeys[i].storage);
1771       if (keyarrayp)
1772         {
1773           (*keyarrayp)[2 * i - 2] = cbdata.mykeys[i].name;
1774           (*keyarrayp)[2 * i - 1] = cbdata.mykeys[i].type;
1775         }
1776     }
1777
1778   /*
1779    * write schemata
1780    */
1781   write_id(fp, cbdata.myschemadatalen);
1782   if (cbdata.nmyschemata)
1783     {
1784       for (i = 1; i < cbdata.nmyschemata; i++)
1785         write_idarray(fp, pool, 0, cbdata.myschemadata + cbdata.myschemata[i]);
1786     }
1787
1788 #if 0
1789   /*
1790    * write info block
1791    */
1792   if (nsubfiles)
1793     {
1794       struct extdata xd;
1795       xd.buf = 0;
1796       xd.len = 0;
1797       int max = 0;
1798       int cur;
1799
1800       for (i = 0; i < nsubfiles; i++)
1801         {
1802           int j;
1803
1804           cur = xd.len;
1805           data_addid(&xd, repodataschemata[i]);
1806           if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1807             {
1808               if (fileinfo[i].addedfileprovides)
1809                 data_addidarray_sort(&xd, pool, needid, fileinfo[i].addedfileprovides, 0);
1810               if (fileinfo[i].rpmdbcookie)
1811                 data_addblob(&xd, fileinfo[i].rpmdbcookie, 32);
1812             }
1813           else
1814             {
1815               /* key,type array + location, write idarray */
1816               for (j = 1; j < fileinfo[i].nkeys; j++)
1817                 {
1818                   data_addideof(&xd, needid[fileinfo[i].keys[j].name].need, 0);
1819                   data_addideof(&xd, needid[fileinfo[i].keys[j].type].need, j == fileinfo[i].nkeys - 1);
1820                 }
1821               if (fileinfo[i].location)
1822                 data_addblob(&xd, (unsigned char *)fileinfo[i].location, strlen(fileinfo[i].location) + 1);
1823             }
1824           cur = xd.len - cur;
1825           if (cur > max)
1826             max = cur;
1827         }
1828       write_id(fp, max);
1829       write_id(fp, xd.len);
1830       write_blob(fp, xd.buf, xd.len);
1831       sat_free(xd.buf);
1832     }
1833 #endif
1834
1835 /********************************************************************/
1836
1837   write_id(fp, cbdata.maxdata);
1838   write_id(fp, cbdata.extdata[0].len);
1839   if (cbdata.extdata[0].len)
1840     write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1841   sat_free(cbdata.extdata[0].buf);
1842
1843 #if 0
1844   /*
1845    * write Solvable data
1846    */
1847   if (repo->nsolvables || repo->nextra)
1848     {
1849       write_id(fp, maxentrysize);
1850       write_id(fp, cbdata.extdata[0].len);
1851       write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1852     }
1853   sat_free(cbdata.extdata[0].buf);
1854 #endif
1855
1856   /* write vertical data */
1857   for (i = 1; i < cbdata.nmykeys; i++)
1858     if (cbdata.extdata[i].len)
1859       break;
1860   if (i < cbdata.nmykeys)
1861     {
1862       unsigned char *dp, vpage[BLOB_PAGESIZE];
1863       int l, ll, lpage = 0;
1864
1865       write_u32(fp, BLOB_PAGESIZE);
1866       for (i = 1; i < cbdata.nmykeys; i++)
1867         {
1868           if (!cbdata.extdata[i].len)
1869             continue;
1870           l = cbdata.extdata[i].len;
1871           dp = cbdata.extdata[i].buf;
1872           while (l)
1873             {
1874               ll = BLOB_PAGESIZE - lpage;
1875               if (l < ll)
1876                 ll = l;
1877               memcpy(vpage + lpage, dp, ll);
1878               dp += ll;
1879               lpage += ll;
1880               l -= ll;
1881               if (lpage == BLOB_PAGESIZE)
1882                 {
1883                   write_compressed_page(fp, vpage, lpage);
1884                   lpage = 0;
1885                 }
1886             }
1887         }
1888       if (lpage)
1889         write_compressed_page(fp, vpage, lpage);
1890     }
1891
1892 #if 0
1893   /* write vertical_offset entries */
1894   write_u32(fp, 0);     /* no paging */
1895   for (i = 1; i < cbdata.nmykeys; i++)
1896     if (cbdata.extdata[i].len)
1897       write_blob(fp, cbdata.extdata[i].buf, cbdata.extdata[i].len);
1898
1899   /* Fill fileinfo for our caller.  */
1900   if (setfileinfo)
1901     {
1902       fileinfo->checksum = 0;
1903       fileinfo->nchecksum = 0;
1904       fileinfo->checksumtype = 0;
1905       fileinfo->location = 0;
1906     }
1907 #endif
1908
1909   for (i = 1; i < cbdata.nmykeys; i++)
1910     sat_free(cbdata.extdata[i].buf);
1911   sat_free(cbdata.extdata);
1912
1913   if (cbdata.ownspool)
1914     {
1915       sat_free(cbdata.ownspool->strings);
1916       sat_free(cbdata.ownspool->stringspace);
1917       sat_free(cbdata.ownspool->stringhashtbl);
1918     }
1919   if (cbdata.owndirpool)
1920     {
1921       sat_free(cbdata.owndirpool->dirs);
1922       sat_free(cbdata.owndirpool->dirtraverse);
1923     }
1924   sat_free(needid);
1925   sat_free(cbdata.extraschemata);
1926   sat_free(cbdata.solvschemata);
1927   sat_free(cbdata.myschemadata);
1928   sat_free(cbdata.myschemata);
1929   sat_free(cbdata.schema);
1930
1931   sat_free(cbdata.mykeys);
1932   sat_free(cbdata.keymap);
1933   sat_free(cbdata.keymapstart);
1934   sat_free(cbdata.dirused);
1935   sat_free(repodataused);
1936   sat_free(repodataschemata);
1937 }