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