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