2 * Copyright (c) 2007-2011, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * Write Repo data out to a file in solv format
13 * See doc/README.format for a description
14 * of the binary file format
18 #include <sys/types.h>
28 #include "repo_write.h"
31 /*------------------------------------------------------------------*/
32 /* Id map optimizations */
34 typedef struct needid {
40 #define RELOFF(id) (needid[0].map + GETRELID(id))
44 * idarray: array of Ids, ID_NULL terminated
45 * needid: array of Id->NeedId
47 * return size of array (including trailing zero)
52 incneedid(Pool *pool, Id id, NeedId *needid)
56 Reldep *rd = GETRELDEP(pool, id);
57 needid[RELOFF(id)].need++;
58 if (ISRELDEP(rd->evr))
59 incneedid(pool, rd->evr, needid);
61 needid[rd->evr].need++;
68 incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
75 while ((id = *idarray++) != 0)
80 Reldep *rd = GETRELDEP(pool, id);
81 needid[RELOFF(id)].need++;
82 if (ISRELDEP(rd->evr))
83 incneedid(pool, rd->evr, needid);
85 needid[rd->evr].need++;
99 needid_cmp_need(const void *ap, const void *bp, void *dp)
101 const NeedId *a = ap;
102 const NeedId *b = bp;
104 r = b->need - a->need;
107 return a->map - b->map;
111 needid_cmp_need_s(const void *ap, const void *bp, void *dp)
113 const NeedId *a = ap;
114 const NeedId *b = bp;
115 Stringpool *spool = dp;
120 r = b->need - a->need;
123 as = spool->stringspace + spool->strings[a->map];
124 bs = spool->stringspace + spool->strings[b->map];
125 return strcmp(as, bs);
129 /*------------------------------------------------------------------*/
130 /* output helper routines, used for writing the header */
131 /* (the data itself is accumulated in memory and written with
139 write_u32(FILE *fp, unsigned int x)
141 if (putc(x >> 24, fp) == EOF ||
142 putc(x >> 16, fp) == EOF ||
143 putc(x >> 8, fp) == EOF ||
146 perror("write error u32");
157 write_u8(FILE *fp, unsigned int x)
159 if (putc(x, fp) == EOF)
161 perror("write error u8");
171 write_blob(FILE *fp, void *data, int len)
173 if (len && fwrite(data, len, 1, fp) != 1)
175 perror("write error blob");
185 write_id(FILE *fp, Id x)
190 putc((x >> 28) | 128, fp);
192 putc((x >> 21) | 128, fp);
193 putc((x >> 14) | 128, fp);
196 putc((x >> 7) | 128, fp);
197 if (putc(x & 127, fp) == EOF)
199 perror("write error id");
205 write_id_eof(FILE *fp, Id x, int eof)
208 x = (x & 63) | ((x & ~63) << 1);
209 write_id(fp, x | (eof ? 0 : 64));
215 write_str(FILE *fp, const char *str)
217 if (fputs(str, fp) == EOF || putc(0, fp) == EOF)
219 perror("write error str");
229 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
243 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
245 id = (id & 63) | ((id & ~63) << 1);
251 write_id(fp, id | 64);
256 cmp_ids(const void *pa, const void *pb, void *dp)
265 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
277 for (len = 0; len < 64 && ids[len]; len++)
281 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
286 for (i = len + 1; ids[i]; i++)
288 sids = solv_malloc2(i, sizeof(Id));
289 memcpy(sids, lids, 64 * sizeof(Id));
290 for (; ids[len]; len++)
294 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
301 /* That bloody solvable:prereqmarker needs to stay in position :-( */
303 marker = needid[marker].need;
304 for (i = 0; i < len; i++)
305 if (sids[i] == marker)
308 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
310 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
314 /* The differencing above produces many runs of ones and twos. I tried
315 fairly elaborate schemes to RLE those, but they give only very mediocre
316 improvements in compression, as coding the escapes costs quite some
317 space. Even if they are coded only as bits in IDs. The best improvement
318 was about 2.7% for the whole .solv file. It's probably better to
319 invest some complexity into sharing idarrays, than RLEing. */
320 for (i = 0; i < len - 1; i++)
323 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
324 hence all real differences are offsetted by 1. Otherwise we would
325 have to handle negative differences, which would cost code space for
326 the encoding of the sign. We loose the exact mapping of prereq here,
327 but we know the result, so we can recover from that in the reader. */
335 /* XXX If difference is zero we have multiple equal elements,
336 we might want to skip writing them out. */
338 id = (id & 63) | ((id & ~63) << 1);
339 write_id(fp, id | 64);
347 id = (id & 63) | ((id & ~63) << 1);
364 Stringpool *ownspool;
373 Id *schema; /* schema construction space */
374 Id *sp; /* pointer in above */
375 Id *oldschema, *oldsp;
382 struct extdata *extdata;
391 int doingsolvables; /* working on solvables data */
394 #define NEEDED_BLOCK 1023
395 #define SCHEMATA_BLOCK 31
396 #define SCHEMATADATA_BLOCK 255
397 #define EXTDATA_BLOCK 4095
400 data_addid(struct extdata *xd, Id sx)
402 unsigned int x = (unsigned int)sx;
404 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
405 dp = xd->buf + xd->len;
410 *dp++ = (x >> 28) | 128;
412 *dp++ = (x >> 21) | 128;
413 *dp++ = (x >> 14) | 128;
416 *dp++ = (x >> 7) | 128;
418 xd->len = dp - xd->buf;
422 data_addideof(struct extdata *xd, Id x, int eof)
425 x = (x & 63) | ((x & ~63) << 1);
426 data_addid(xd, (eof ? x: x | 64));
430 data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
436 data_addid(xd, (Id)(hx >> 3));
437 xd->buf[xd->len - 1] |= 128;
440 data_addid(xd, (Id)(x | 0x80000000));
441 xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
444 data_addid(xd, (Id)x);
448 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
461 for (len = 0; len < 64 && ids[len]; len++)
465 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
470 for (i = len + 1; ids[i]; i++)
472 sids = solv_malloc2(i, sizeof(Id));
473 memcpy(sids, lids, 64 * sizeof(Id));
474 for (; ids[len]; len++)
478 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
485 /* That bloody solvable:prereqmarker needs to stay in position :-( */
487 marker = needid[marker].need;
488 for (i = 0; i < len; i++)
489 if (sids[i] == marker)
492 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
494 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
498 /* The differencing above produces many runs of ones and twos. I tried
499 fairly elaborate schemes to RLE those, but they give only very mediocre
500 improvements in compression, as coding the escapes costs quite some
501 space. Even if they are coded only as bits in IDs. The best improvement
502 was about 2.7% for the whole .solv file. It's probably better to
503 invest some complexity into sharing idarrays, than RLEing. */
504 for (i = 0; i < len - 1; i++)
507 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
508 hence all real differences are offsetted by 1. Otherwise we would
509 have to handle negative differences, which would cost code space for
510 the encoding of the sign. We loose the exact mapping of prereq here,
511 but we know the result, so we can recover from that in the reader. */
519 /* XXX If difference is zero we have multiple equal elements,
520 we might want to skip writing them out. */
522 id = (id & 63) | ((id & ~63) << 1);
523 data_addid(xd, id | 64);
531 id = (id & 63) | ((id & ~63) << 1);
538 data_addblob(struct extdata *xd, unsigned char *blob, int len)
540 xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
541 memcpy(xd->buf + xd->len, blob, len);
546 data_addu32(struct extdata *xd, unsigned int num)
553 data_addblob(xd, d, 4);
557 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
559 const char *str = stringpool_id2str(ss, id);
560 id = stringpool_str2id(cbdata->ownspool, str, 1);
561 if (id >= cbdata->needid[0].map)
563 int oldoff = cbdata->needid[0].map;
564 int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
565 int nrels = cbdata->repo->pool->nrels;
566 cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
568 memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
569 memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
570 cbdata->needid[0].map = newoff;
576 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
580 parent = dirpool_parent(dp, dir);
582 parent = putinowndirpool(cbdata, data, dp, parent);
583 compid = dp->dirs[dir];
584 if (cbdata->ownspool && compid > 1)
585 compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
586 return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
590 * collect usage information about the dirs
591 * 1: dir used, no child of dir used
592 * 2: dir used as parent of another used dir
595 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
597 if (cbdata->dirused[dir])
599 cbdata->dirused[dir] = 1;
600 while ((dir = dirpool_parent(dp, dir)) != 0)
602 if (cbdata->dirused[dir] == 2)
604 if (cbdata->dirused[dir])
606 cbdata->dirused[dir] = 2;
609 cbdata->dirused[dir] = 2;
611 cbdata->dirused[0] = 2;
616 * collect key/id/dirid usage information, create needed schemas
619 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
624 if (key->name == REPOSITORY_SOLVABLES)
625 return SEARCH_NEXT_KEY; /* we do not want this one */
627 /* hack: ignore some keys, see BUGS */
628 if (data->repodataid != data->repo->nrepodata - 1)
629 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
630 return SEARCH_NEXT_KEY;
632 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
634 return SEARCH_NEXT_KEY; /* we do not want this one */
636 /* record key in schema */
637 if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
638 && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
643 case REPOKEY_TYPE_ID:
644 case REPOKEY_TYPE_IDARRAY:
646 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
647 id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
648 incneedid(repo->pool, id, cbdata->needid);
650 case REPOKEY_TYPE_DIR:
651 case REPOKEY_TYPE_DIRNUMNUMARRAY:
652 case REPOKEY_TYPE_DIRSTRARRAY:
654 if (cbdata->owndirpool)
655 putinowndirpool(cbdata, data, &data->dirpool, id);
657 setdirused(cbdata, &data->dirpool, id);
659 case REPOKEY_TYPE_FIXARRAY:
662 if (cbdata->oldschema)
664 fprintf(stderr, "nested structs not yet implemented\n");
667 cbdata->oldschema = cbdata->schema;
668 cbdata->oldsp = cbdata->sp;
669 cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
670 cbdata->sp = cbdata->schema;
672 else if (kv->eof == 1)
674 cbdata->current_sub++;
676 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
677 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
679 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
681 cbdata->sp = cbdata->schema;
685 solv_free(cbdata->schema);
686 cbdata->schema = cbdata->oldschema;
687 cbdata->sp = cbdata->oldsp;
688 cbdata->oldsp = cbdata->oldschema = 0;
691 case REPOKEY_TYPE_FLEXARRAY:
695 *cbdata->sp++ = 0; /* mark start */
699 /* just finished a schema, rewind */
700 Id *sp = cbdata->sp - 1;
704 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
705 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
706 cbdata->sp = kv->eof == 2 ? sp - 1: sp;
716 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
718 struct cbdata *cbdata = vcbdata;
719 Repo *repo = data->repo;
723 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);
725 return repo_write_collect_needed(cbdata, repo, data, key, kv);
731 * encode all of the data into the correct buffers
735 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
744 if (key->name == REPOSITORY_SOLVABLES)
745 return SEARCH_NEXT_KEY;
747 /* hack: ignore some keys, see BUGS */
748 if (data->repodataid != data->repo->nrepodata - 1)
749 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
750 return SEARCH_NEXT_KEY;
752 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
754 return SEARCH_NEXT_KEY; /* we do not want this one */
756 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
758 xd = cbdata->extdata + rm; /* vertical buffer */
759 if (cbdata->vstart == -1)
760 cbdata->vstart = xd->len;
763 xd = cbdata->extdata + 0; /* incore buffer */
766 case REPOKEY_TYPE_VOID:
767 case REPOKEY_TYPE_CONSTANT:
768 case REPOKEY_TYPE_CONSTANTID:
770 case REPOKEY_TYPE_ID:
772 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
773 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
774 needid = cbdata->needid;
775 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
778 case REPOKEY_TYPE_IDARRAY:
780 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
781 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
782 needid = cbdata->needid;
783 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
784 data_addideof(xd, id, kv->eof);
786 case REPOKEY_TYPE_STR:
787 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
789 case REPOKEY_TYPE_MD5:
790 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
792 case REPOKEY_TYPE_SHA1:
793 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
795 case REPOKEY_TYPE_SHA256:
796 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
798 case REPOKEY_TYPE_U32:
804 data_addblob(xd, v, 4);
806 case REPOKEY_TYPE_NUM:
807 data_addid64(xd, kv->num, kv->num2);
809 case REPOKEY_TYPE_DIR:
811 if (cbdata->owndirpool)
812 id = putinowndirpool(cbdata, data, &data->dirpool, id);
813 id = cbdata->dirused[id];
816 case REPOKEY_TYPE_BINARY:
817 data_addid(xd, kv->num);
819 data_addblob(xd, (unsigned char *)kv->str, kv->num);
821 case REPOKEY_TYPE_DIRNUMNUMARRAY:
823 if (cbdata->owndirpool)
824 id = putinowndirpool(cbdata, data, &data->dirpool, id);
825 id = cbdata->dirused[id];
827 data_addid(xd, kv->num);
828 data_addideof(xd, kv->num2, kv->eof);
830 case REPOKEY_TYPE_DIRSTRARRAY:
832 if (cbdata->owndirpool)
833 id = putinowndirpool(cbdata, data, &data->dirpool, id);
834 id = cbdata->dirused[id];
835 data_addideof(xd, id, kv->eof);
836 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
838 case REPOKEY_TYPE_FIXARRAY:
843 data_addid(xd, kv->num);
844 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
846 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
850 else if (kv->eof == 1)
852 cbdata->current_sub++;
855 case REPOKEY_TYPE_FLEXARRAY:
857 data_addid(xd, kv->num);
859 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
860 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
862 if (xd->len - cbdata->lastlen > cbdata->maxdata)
863 cbdata->maxdata = xd->len - cbdata->lastlen;
864 cbdata->lastlen = xd->len;
868 fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
871 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
873 /* we can re-use old data in the blob here! */
874 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
875 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
882 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
884 struct cbdata *cbdata = vcbdata;
885 return repo_write_adddata(cbdata, data, key, kv);
888 /* traverse through directory with first child "dir" */
890 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
896 /* special case for '/', which has to come first */
899 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
901 if (used && !used[sib])
903 if (sib == 1 && parent == 1)
904 continue; /* already did that one above */
908 /* now go through all the siblings we just added and
909 * do recursive calls on them */
911 for (; parent < lastn; parent++)
913 sib = dirmap[parent];
914 if (used && used[sib] != 2) /* 2: used as parent */
916 child = dirpool_child(dp, sib);
919 dirmap[n++] = -parent; /* start new block */
920 n = traverse_dirs(dp, dirmap, n, child, used);
927 write_compressed_page(FILE *fp, unsigned char *page, int len)
930 unsigned char cpage[REPOPAGE_BLOBSIZE];
932 clen = repopagestore_compress_page(page, len, cpage, len - 1);
935 write_u32(fp, len * 2);
936 write_blob(fp, page, len);
940 write_u32(fp, clen * 2 + 1);
941 write_blob(fp, cpage, clen);
945 static Id verticals[] = {
947 SOLVABLE_DESCRIPTION,
956 static char *languagetags[] = {
958 "solvable:description:",
959 "solvable:messageins:",
960 "solvable:messagedel:",
966 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
971 for (i = 0; verticals[i]; i++)
972 if (key->name == verticals[i])
973 return KEY_STORAGE_VERTICAL_OFFSET;
974 keyname = pool_id2str(repo->pool, key->name);
975 for (i = 0; languagetags[i] != 0; i++)
976 if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
977 return KEY_STORAGE_VERTICAL_OFFSET;
978 return KEY_STORAGE_INCORE;
986 * the code works the following way:
988 * 1) find which keys should be written
989 * 2) collect usage information for keys/ids/dirids, create schema
991 * 3) use usage information to create mapping tables, so that often
992 * used ids get a lower number
993 * 4) encode data into buffers using the mapping tables
994 * 5) write everything to disk
997 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
999 Pool *pool = repo->pool;
1003 int nstrings, nrels;
1004 unsigned int sizeid;
1005 unsigned int solv_flags;
1014 unsigned char *repodataused;
1015 int anyrepodataused = 0;
1016 int anysolvableused = 0;
1018 struct cbdata cbdata;
1021 int poolusage, dirpoolusage, idused, dirused;
1024 Repodata *data, *dirpooldata;
1035 Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1037 unsigned char *prefixcomp;
1038 unsigned int compsum;
1042 memset(&cbdata, 0, sizeof(cbdata));
1044 cbdata.target = ⌖
1046 repodata_initdata(&target, repo, 1);
1048 /* go through all repodata and find the keys we need */
1049 /* also unify keys */
1050 /* keymapstart - maps repo number to keymap offset */
1051 /* keymap - maps repo key to my key, 0 -> not used */
1053 /* start with all KEY_STORAGE_SOLVABLE ids */
1055 n = ID_NUM_INTERNAL;
1056 FOR_REPODATAS(repo, i, data)
1058 cbdata.keymap = solv_calloc(n, sizeof(Id));
1059 cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1060 repodataused = solv_calloc(repo->nrepodata, 1);
1065 /* add keys for STORAGE_SOLVABLE */
1066 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1070 if (i < SOLVABLE_PROVIDES)
1071 keyd.type = REPOKEY_TYPE_ID;
1072 else if (i < RPM_RPMDBID)
1073 keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1075 keyd.type = REPOKEY_TYPE_NUM;
1077 keyd.storage = KEY_STORAGE_SOLVABLE;
1080 keyd.storage = keyfilter(repo, &keyd, kfdata);
1081 if (keyd.storage == KEY_STORAGE_DROPPED)
1083 keyd.storage = KEY_STORAGE_SOLVABLE;
1087 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1090 if (repo->nsolvables)
1093 keyd.name = REPOSITORY_SOLVABLES;
1094 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1096 keyd.storage = KEY_STORAGE_INCORE;
1097 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1105 n = ID_NUM_INTERNAL;
1106 FOR_REPODATAS(repo, i, data)
1108 cbdata.keymapstart[i] = n;
1109 cbdata.keymap[n++] = 0; /* key 0 */
1115 /* check if we want this repodata */
1116 memset(&keyd, 0, sizeof(keyd));
1120 if (keyfilter(repo, &keyd, kfdata) == -1)
1123 for (j = 1; j < data->nkeys; j++, n++)
1125 key = data->keys + j;
1126 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1128 cbdata.keymap[n] = cbdata.keymap[key->name];
1131 if (key->type == REPOKEY_TYPE_DELETED)
1133 cbdata.keymap[n] = 0;
1136 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1138 Repokey keyd = *key;
1139 keyd.size = repodata_globalize_id(data, key->size, 1);
1140 id = repodata_key2id(&target, &keyd, 0);
1143 id = repodata_key2id(&target, key, 0);
1146 Repokey keyd = *key;
1147 keyd.storage = KEY_STORAGE_INCORE;
1148 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1149 keyd.size = repodata_globalize_id(data, key->size, 1);
1150 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1154 keyd.storage = keyfilter(repo, &keyd, kfdata);
1155 if (keyd.storage == KEY_STORAGE_DROPPED)
1157 cbdata.keymap[n] = 0;
1161 id = repodata_key2id(&target, &keyd, 1);
1163 cbdata.keymap[n] = id;
1164 /* load repodata if not already loaded */
1165 if (data->state == REPODATA_STUB)
1167 if (data->loadcallback)
1168 data->loadcallback(data);
1170 data->state = REPODATA_ERROR;
1171 if (data->state != REPODATA_ERROR)
1173 /* redo this repodata! */
1175 n = cbdata.keymapstart[i];
1179 if (data->state == REPODATA_ERROR)
1182 cbdata.keymap[n] = 0;
1186 repodataused[i] = 1;
1187 anyrepodataused = 1;
1188 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1189 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1191 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1193 idused = 1; /* dirs also use ids */
1199 if (data->localpool)
1202 poolusage = 3; /* need own pool */
1206 spool = &data->spool;
1213 else if (poolusage != 1)
1214 poolusage = 3; /* need own pool */
1220 dirpoolusage = 3; /* need own dirpool */
1224 dirpool = &data->dirpool;
1231 /* 0: no pool needed at all */
1232 /* 1: use global pool */
1233 /* 2: use repodata local pool */
1234 /* 3: need own pool */
1237 spool = &target.spool;
1238 /* hack: reuse global pool data so we don't have to map pool ids */
1241 stringpool_free(spool);
1242 stringpool_clone(spool, &pool->ss);
1244 cbdata.ownspool = spool;
1246 else if (poolusage == 0 || poolusage == 1)
1252 if (dirpoolusage == 3)
1254 dirpool = &target.dirpool;
1256 cbdata.owndirpool = dirpool;
1259 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1262 /********************************************************************/
1264 fprintf(stderr, "poolusage: %d\n", poolusage);
1265 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1266 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1267 for (i = 1; i < target.nkeys; i++)
1268 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);
1271 /* copy keys if requested */
1275 for (i = 1; i < target.nkeys; i++)
1276 queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1281 /* put all the keys we need in our string pool */
1282 /* put mapped ids right into target.keys */
1283 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1285 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1286 if (key->type == REPOKEY_TYPE_CONSTANTID)
1288 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1289 type_constantid = key->type;
1290 key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1293 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1296 stringpool_freehash(spool); /* free some mem */
1300 /********************************************************************/
1302 /* set needed count of all strings and rels,
1303 * find which keys are used in the solvables
1304 * put all strings in own spool
1307 reloff = spool->nstrings;
1309 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1311 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1312 needid[0].map = reloff;
1314 cbdata.needid = needid;
1315 cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1316 cbdata.sp = cbdata.schema;
1317 cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1319 /* create main schema */
1320 cbdata.sp = cbdata.schema;
1321 /* collect all other data from all repodatas */
1322 /* XXX: merge arrays of equal keys? */
1323 FOR_REPODATAS(repo, j, data)
1325 if (!repodataused[j])
1327 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1330 /* add solvables if needed (may revert later) */
1331 if (repo->nsolvables)
1333 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1334 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1337 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1339 idarraydata = repo->idarraydata;
1341 anysolvableused = 0;
1342 cbdata.doingsolvables = 1;
1343 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1345 if (s->repo != repo)
1348 /* set schema info, keep in sync with further down */
1350 if (cbdata.keymap[SOLVABLE_NAME])
1352 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1353 needid[s->name].need++;
1355 if (cbdata.keymap[SOLVABLE_ARCH])
1357 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1358 needid[s->arch].need++;
1360 if (cbdata.keymap[SOLVABLE_EVR])
1362 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1363 needid[s->evr].need++;
1365 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1367 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1368 needid[s->vendor].need++;
1370 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1372 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1373 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1375 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1377 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1378 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1380 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1382 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1383 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1385 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1387 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1388 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1390 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1392 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1393 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1395 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1397 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1398 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1400 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1402 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1403 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1405 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1407 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1408 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1410 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1412 *sp++ = cbdata.keymap[RPM_RPMDBID];
1413 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1417 if (anyrepodataused)
1419 FOR_REPODATAS(repo, j, data)
1421 if (!repodataused[j])
1423 if (i < data->start || i >= data->end)
1425 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1426 needid = cbdata.needid;
1430 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1431 if (cbdata.solvschemata[n])
1432 anysolvableused = 1;
1435 cbdata.doingsolvables = 0;
1436 assert(n == repo->nsolvables);
1438 if (repo->nsolvables && !anysolvableused)
1440 /* strip off solvable from the main schema */
1441 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1443 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1445 *sp = target.schemadata[target.schemata[mainschema] + i];
1446 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1449 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1451 target.schemadatalen = target.schemata[mainschema];
1453 repodata_free_schemahash(&target);
1454 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1457 /********************************************************************/
1459 /* remove unused keys */
1460 keyused = solv_calloc(target.nkeys, sizeof(Id));
1461 for (i = 1; i < target.schemadatalen; i++)
1462 keyused[target.schemadata[i]] = 1;
1464 for (n = i = 1; i < target.nkeys; i++)
1471 target.keys[n] = target.keys[i];
1474 keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1475 keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1482 queue_truncate(keyq, 2 * n - 2);
1484 /* update schema data to the new key ids */
1485 for (i = 1; i < target.schemadatalen; i++)
1486 target.schemadata[i] = keyused[target.schemadata[i]];
1487 /* update keymap to the new key ids */
1488 for (i = 0; i < cbdata.nkeymap; i++)
1489 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1490 keyused = solv_free(keyused);
1492 /* increment needid of the used keys, they are already mapped to
1493 * the correct string pool */
1494 for (i = 1; i < target.nkeys; i++)
1496 if (target.keys[i].type == type_constantid)
1497 needid[target.keys[i].size].need++;
1498 needid[target.keys[i].name].need++;
1499 needid[target.keys[i].type].need++;
1502 /********************************************************************/
1504 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1506 /* no dirs used at all */
1507 cbdata.dirused = solv_free(cbdata.dirused);
1511 /* increment need id for used dir components */
1514 /* if we have own dirpool, all entries in it are used.
1515 also, all comp ids are already mapped by putinowndirpool(),
1516 so we can simply increment needid.
1517 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1518 /* else we re-use a dirpool of repodata "dirpooldata".
1519 dirused tells us which of the ids are used.
1520 we need to map comp ids if we generate a new pool.
1521 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1522 for (i = 1; i < dirpool->ndirs; i++)
1525 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1527 if (cbdata.dirused && !cbdata.dirused[i])
1529 id = dirpool->dirs[i];
1532 if (dirpooldata && cbdata.ownspool && id > 1)
1534 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1535 needid = cbdata.needid;
1542 /********************************************************************/
1545 * create mapping table, new keys are sorted by needid[].need
1547 * needid[key].need : old key -> new key
1548 * needid[key].map : new key -> old key
1551 /* zero out id 0 and rel 0 just in case */
1552 reloff = needid[0].map;
1554 needid[reloff].need = 0;
1556 for (i = 1; i < reloff + pool->nrels; i++)
1560 solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1562 /* make first entry '' */
1564 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1566 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1567 /* now needid is in new order, needid[newid].map -> oldid */
1569 /* calculate string space size, also zero out needid[].need */
1571 for (i = 1; i < reloff; i++)
1573 if (!needid[i].need)
1574 break; /* as we have sorted, every entry after this also has need == 0 */
1576 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1578 nstrings = i; /* our new string id end */
1580 /* make needid[oldid].need point to newid */
1581 for (i = 1; i < nstrings; i++)
1582 needid[needid[i].map].need = i;
1584 /* same as above for relations */
1585 for (i = 0; i < pool->nrels; i++)
1587 if (!needid[reloff + i].need)
1589 needid[reloff + i].need = 0;
1591 nrels = i; /* our new rel id end */
1593 for (i = 0; i < nrels; i++)
1594 needid[needid[reloff + i].map].need = nstrings + i;
1596 /* now we have: needid[oldid].need -> newid
1597 needid[newid].map -> oldid
1598 both for strings and relations */
1601 /********************************************************************/
1607 /* create our new target directory structure by traversing through all
1608 * used dirs. This will concatenate blocks with the same parent
1609 * directory into single blocks.
1610 * Instead of components, traverse_dirs stores the old dirids,
1611 * we will change this in the second step below */
1612 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1613 if (cbdata.dirused && !cbdata.dirused[1])
1614 cbdata.dirused[1] = 1; /* always want / entry */
1615 dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1617 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1619 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1620 /* change dirmap so that it maps from "new dirid" to "new compid" */
1621 if (!cbdata.dirused)
1622 cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1623 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1624 for (i = 1; i < ndirmap; i++)
1628 cbdata.dirused[dirmap[i]] = i;
1629 id = dirpool->dirs[dirmap[i]];
1630 if (dirpooldata && cbdata.ownspool && id > 1)
1631 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1632 dirmap[i] = needid[id].need;
1634 /* now the new target directory structure is complete (dirmap), and we have
1635 * dirused[olddirid] -> newdirid */
1638 /********************************************************************/
1641 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1644 cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1646 xd = cbdata.extdata;
1647 cbdata.current_sub = 0;
1648 /* add main schema */
1650 data_addid(xd, mainschema);
1653 FOR_REPODATAS(repo, j, data)
1655 if (!repodataused[j])
1657 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1661 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1662 cbdata.maxdata = xd->len - cbdata.lastlen;
1663 cbdata.lastlen = xd->len;
1665 if (anysolvableused)
1667 data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1668 cbdata.doingsolvables = 1;
1669 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1671 if (s->repo != repo)
1673 data_addid(xd, cbdata.solvschemata[n]);
1674 if (cbdata.keymap[SOLVABLE_NAME])
1675 data_addid(xd, needid[s->name].need);
1676 if (cbdata.keymap[SOLVABLE_ARCH])
1677 data_addid(xd, needid[s->arch].need);
1678 if (cbdata.keymap[SOLVABLE_EVR])
1679 data_addid(xd, needid[s->evr].need);
1680 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1681 data_addid(xd, needid[s->vendor].need);
1682 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1683 data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1684 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1685 data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1686 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1687 data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1688 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1689 data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1690 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1691 data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1692 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1693 data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1694 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1695 data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1696 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1697 data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1698 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1699 data_addid(xd, repo->rpmdbid[i - repo->start]);
1700 if (anyrepodataused)
1703 FOR_REPODATAS(repo, j, data)
1705 if (!repodataused[j])
1707 if (i < data->start || i >= data->end)
1709 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1712 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1713 cbdata.maxdata = xd->len - cbdata.lastlen;
1714 cbdata.lastlen = xd->len;
1717 cbdata.doingsolvables = 0;
1720 assert(cbdata.current_sub == cbdata.nsubschemata);
1721 if (cbdata.subschemata)
1723 cbdata.subschemata = solv_free(cbdata.subschemata);
1724 cbdata.nsubschemata = 0;
1727 /********************************************************************/
1731 /* write file header */
1732 write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1733 write_u32(fp, SOLV_VERSION_8);
1737 write_u32(fp, nstrings);
1738 write_u32(fp, nrels);
1739 write_u32(fp, ndirmap);
1740 write_u32(fp, anysolvableused ? repo->nsolvables : 0);
1741 write_u32(fp, target.nkeys);
1742 write_u32(fp, target.nschemata);
1744 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1745 write_u32(fp, solv_flags);
1748 * calculate prefix encoding of the strings
1750 prefixcomp = solv_malloc(nstrings);
1755 for (i = 1; i < nstrings; i++)
1757 char *str = spool->stringspace + spool->strings[needid[i].map];
1759 for (same = 0; same < 255; same++)
1760 if (!old_str[same] || old_str[same] != str[same])
1762 prefixcomp[i] = same;
1770 write_u32(fp, sizeid);
1771 /* we save compsum bytes but need 1 extra byte for every string */
1772 write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1773 if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1775 for (i = 1; i < nstrings; i++)
1777 char *str = spool->stringspace + spool->strings[needid[i].map];
1778 write_u8(fp, prefixcomp[i]);
1779 write_str(fp, str + prefixcomp[i]);
1782 solv_free(prefixcomp);
1785 /* Build the prefix-encoding of the string pool. We need to know
1786 the size of that before writing it to the file, so we have to
1787 build a separate buffer for that. As it's temporarily possible
1788 that this actually is an expansion we can't easily reuse the
1789 stringspace for this. The max expansion per string is 1 byte,
1790 so it will fit into sizeid+nstrings bytes. */
1791 char *prefix = solv_malloc(sizeid + nstrings);
1794 for (i = 1; i < nstrings; i++)
1796 char *str = spool->stringspace + spool->strings[needid[i].map];
1799 for (same = 0; same < 255; same++)
1800 if (!old_str[same] || !str[same] || old_str[same] != str[same])
1803 len = strlen(str + same) + 1;
1804 memcpy(pp, str + same, len);
1812 write_u32(fp, sizeid);
1813 write_u32(fp, pp - prefix);
1816 if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1818 perror("write error prefix");
1828 for (i = 0; i < nrels; i++)
1830 ran = pool->rels + (needid[reloff + i].map - reloff);
1831 write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1832 write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1833 write_u8(fp, ran->flags);
1837 * write dirs (skip both root and / entry)
1839 for (i = 2; i < ndirmap; i++)
1842 write_id(fp, dirmap[i]);
1844 write_id(fp, nstrings - dirmap[i]);
1851 for (i = 1; i < target.nkeys; i++)
1853 write_id(fp, needid[target.keys[i].name].need);
1854 write_id(fp, needid[target.keys[i].type].need);
1855 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1857 if (target.keys[i].type == type_constantid)
1858 write_id(fp, needid[target.keys[i].size].need);
1860 write_id(fp, target.keys[i].size);
1863 write_id(fp, cbdata.extdata[i].len);
1864 write_id(fp, target.keys[i].storage);
1870 write_id(fp, target.schemadatalen); /* XXX -1? */
1871 for (i = 1; i < target.nschemata; i++)
1872 write_idarray(fp, pool, 0, repodata_id2schema(&target, i));
1874 /********************************************************************/
1876 write_id(fp, cbdata.maxdata);
1877 write_id(fp, cbdata.extdata[0].len);
1878 if (cbdata.extdata[0].len)
1879 write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1880 solv_free(cbdata.extdata[0].buf);
1882 /* do we have vertical data? */
1883 for (i = 1; i < target.nkeys; i++)
1884 if (cbdata.extdata[i].len)
1886 if (i < target.nkeys)
1888 /* yes, write it in pages */
1889 unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
1890 int l, ll, lpage = 0;
1892 write_u32(fp, REPOPAGE_BLOBSIZE);
1893 for (i = 1; i < target.nkeys; i++)
1895 if (!cbdata.extdata[i].len)
1897 l = cbdata.extdata[i].len;
1898 dp = cbdata.extdata[i].buf;
1901 ll = REPOPAGE_BLOBSIZE - lpage;
1904 memcpy(vpage + lpage, dp, ll);
1908 if (lpage == REPOPAGE_BLOBSIZE)
1910 write_compressed_page(fp, vpage, lpage);
1916 write_compressed_page(fp, vpage, lpage);
1919 for (i = 1; i < target.nkeys; i++)
1920 solv_free(cbdata.extdata[i].buf);
1921 solv_free(cbdata.extdata);
1923 repodata_freedata(&target);
1926 solv_free(cbdata.solvschemata);
1927 solv_free(cbdata.schema);
1929 solv_free(cbdata.keymap);
1930 solv_free(cbdata.keymapstart);
1931 solv_free(cbdata.dirused);
1932 solv_free(repodataused);
1936 struct repodata_write_data {
1937 int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1943 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1945 struct repodata_write_data *wd = kfdata;
1947 /* XXX: special repodata selection hack */
1948 if (key->name == 1 && key->size != wd->repodataid)
1950 if (key->storage == KEY_STORAGE_SOLVABLE)
1951 return KEY_STORAGE_DROPPED; /* not part of this repodata */
1953 return (*wd->keyfilter)(repo, key, wd->kfdata);
1954 return key->storage;
1958 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1960 struct repodata_write_data wd;
1962 wd.keyfilter = keyfilter;
1964 wd.repodataid = data->repodataid;
1965 return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
1969 repodata_write(Repodata *data, FILE *fp)
1971 return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
1975 repo_write(Repo *repo, FILE *fp)
1977 return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);