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;
118 r = b->need - a->need;
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);
127 /*------------------------------------------------------------------*/
128 /* output helper routines, used for writing the header */
129 /* (the data itself is accumulated in memory and written with
137 write_u32(FILE *fp, unsigned int x)
139 if (putc(x >> 24, fp) == EOF ||
140 putc(x >> 16, fp) == EOF ||
141 putc(x >> 8, fp) == EOF ||
144 perror("write error u32");
155 write_u8(FILE *fp, unsigned int x)
157 if (putc(x, fp) == EOF)
159 perror("write error u8");
169 write_blob(FILE *fp, void *data, int len)
171 if (len && fwrite(data, len, 1, fp) != 1)
173 perror("write error blob");
183 write_id(FILE *fp, Id x)
188 putc((x >> 28) | 128, fp);
190 putc((x >> 21) | 128, fp);
191 putc((x >> 14) | 128, fp);
194 putc((x >> 7) | 128, fp);
195 if (putc(x & 127, fp) == EOF)
197 perror("write error id");
203 write_id_eof(FILE *fp, Id x, int eof)
206 x = (x & 63) | ((x & ~63) << 1);
207 write_id(fp, x | (eof ? 0 : 64));
213 write_str(FILE *fp, const char *str)
215 if (fputs(str, fp) == EOF || putc(0, fp) == EOF)
217 perror("write error str");
227 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
241 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
243 id = (id & 63) | ((id & ~63) << 1);
249 write_id(fp, id | 64);
254 cmp_ids(const void *pa, const void *pb, void *dp)
263 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
275 for (len = 0; len < 64 && ids[len]; len++)
279 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
284 for (i = len + 1; ids[i]; i++)
286 sids = solv_malloc2(i, sizeof(Id));
287 memcpy(sids, lids, 64 * sizeof(Id));
288 for (; ids[len]; len++)
292 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
299 /* That bloody solvable:prereqmarker needs to stay in position :-( */
301 marker = needid[marker].need;
302 for (i = 0; i < len; i++)
303 if (sids[i] == marker)
306 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
308 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
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++)
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. */
333 /* XXX If difference is zero we have multiple equal elements,
334 we might want to skip writing them out. */
336 id = (id & 63) | ((id & ~63) << 1);
337 write_id(fp, id | 64);
345 id = (id & 63) | ((id & ~63) << 1);
362 Stringpool *ownspool;
371 Id *schema; /* schema construction space */
372 Id *sp; /* pointer in above */
373 Id *oldschema, *oldsp;
380 struct extdata *extdata;
389 int doingsolvables; /* working on solvables data */
392 #define NEEDED_BLOCK 1023
393 #define SCHEMATA_BLOCK 31
394 #define SCHEMATADATA_BLOCK 255
395 #define EXTDATA_BLOCK 4095
398 data_addid(struct extdata *xd, Id x)
401 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
402 dp = xd->buf + xd->len;
407 *dp++ = (x >> 28) | 128;
409 *dp++ = (x >> 21) | 128;
410 *dp++ = (x >> 14) | 128;
413 *dp++ = (x >> 7) | 128;
415 xd->len = dp - xd->buf;
419 data_addideof(struct extdata *xd, Id x, int eof)
422 x = (x & 63) | ((x & ~63) << 1);
423 data_addid(xd, (eof ? x: x | 64));
427 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
439 for (len = 0; len < 64 && ids[len]; len++)
443 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
448 for (i = len + 1; ids[i]; i++)
450 sids = solv_malloc2(i, sizeof(Id));
451 memcpy(sids, lids, 64 * sizeof(Id));
452 for (; ids[len]; len++)
456 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
463 /* That bloody solvable:prereqmarker needs to stay in position :-( */
465 marker = needid[marker].need;
466 for (i = 0; i < len; i++)
467 if (sids[i] == marker)
470 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
472 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
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++)
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. */
497 /* XXX If difference is zero we have multiple equal elements,
498 we might want to skip writing them out. */
500 id = (id & 63) | ((id & ~63) << 1);
501 data_addid(xd, id | 64);
509 id = (id & 63) | ((id & ~63) << 1);
516 data_addblob(struct extdata *xd, unsigned char *blob, int len)
518 xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
519 memcpy(xd->buf + xd->len, blob, len);
524 data_addu32(struct extdata *xd, unsigned int num)
531 data_addblob(xd, d, 4);
535 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
537 const char *str = stringpool_id2str(ss, id);
538 id = stringpool_str2id(cbdata->ownspool, str, 1);
539 if (id >= cbdata->needid[0].map)
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));
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;
554 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
558 parent = dirpool_parent(dp, dir);
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);
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
573 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
575 if (cbdata->dirused[dir])
577 cbdata->dirused[dir] = 1;
578 while ((dir = dirpool_parent(dp, dir)) != 0)
580 if (cbdata->dirused[dir] == 2)
582 if (cbdata->dirused[dir])
584 cbdata->dirused[dir] = 2;
587 cbdata->dirused[dir] = 2;
589 cbdata->dirused[0] = 2;
594 * collect key/id/dirid usage information, create needed schemas
597 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
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;
608 rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
610 return SEARCH_NEXT_KEY; /* we do not want this one */
612 /* record key in schema */
613 if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
614 && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
619 case REPOKEY_TYPE_ID:
620 case REPOKEY_TYPE_IDARRAY:
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);
626 case REPOKEY_TYPE_DIR:
627 case REPOKEY_TYPE_DIRNUMNUMARRAY:
628 case REPOKEY_TYPE_DIRSTRARRAY:
630 if (cbdata->owndirpool)
631 putinowndirpool(cbdata, data, &data->dirpool, id);
633 setdirused(cbdata, &data->dirpool, id);
635 case REPOKEY_TYPE_FIXARRAY:
638 if (cbdata->oldschema)
640 fprintf(stderr, "nested structs not yet implemented\n");
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;
648 else if (kv->eof == 1)
650 cbdata->current_sub++;
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);
655 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
657 cbdata->sp = cbdata->schema;
661 solv_free(cbdata->schema);
662 cbdata->schema = cbdata->oldschema;
663 cbdata->sp = cbdata->oldsp;
664 cbdata->oldsp = cbdata->oldschema = 0;
667 case REPOKEY_TYPE_FLEXARRAY:
671 *cbdata->sp++ = 0; /* mark start */
675 /* just finished a schema, rewind */
676 Id *sp = cbdata->sp - 1;
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;
692 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
694 struct cbdata *cbdata = vcbdata;
695 Repo *repo = data->repo;
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);
701 return repo_write_collect_needed(cbdata, repo, data, key, kv);
707 * encode all of the data into the correct buffers
711 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
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;
726 rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
728 return SEARCH_NEXT_KEY; /* we do not want this one */
730 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
732 xd = cbdata->extdata + rm; /* vertical buffer */
733 if (cbdata->vstart == -1)
734 cbdata->vstart = xd->len;
737 xd = cbdata->extdata + 0; /* incore buffer */
740 case REPOKEY_TYPE_VOID:
741 case REPOKEY_TYPE_CONSTANT:
742 case REPOKEY_TYPE_CONSTANTID:
744 case REPOKEY_TYPE_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;
752 case REPOKEY_TYPE_IDARRAY:
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);
760 case REPOKEY_TYPE_STR:
761 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
763 case REPOKEY_TYPE_MD5:
764 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
766 case REPOKEY_TYPE_SHA1:
767 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
769 case REPOKEY_TYPE_SHA256:
770 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
772 case REPOKEY_TYPE_U32:
778 data_addblob(xd, v, 4);
780 case REPOKEY_TYPE_NUM:
781 data_addid(xd, kv->num);
783 case REPOKEY_TYPE_DIR:
785 if (cbdata->owndirpool)
786 id = putinowndirpool(cbdata, data, &data->dirpool, id);
787 id = cbdata->dirused[id];
790 case REPOKEY_TYPE_BINARY:
791 data_addid(xd, kv->num);
793 data_addblob(xd, (unsigned char *)kv->str, kv->num);
795 case REPOKEY_TYPE_DIRNUMNUMARRAY:
797 if (cbdata->owndirpool)
798 id = putinowndirpool(cbdata, data, &data->dirpool, id);
799 id = cbdata->dirused[id];
801 data_addid(xd, kv->num);
802 data_addideof(xd, kv->num2, kv->eof);
804 case REPOKEY_TYPE_DIRSTRARRAY:
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);
812 case REPOKEY_TYPE_FIXARRAY:
817 data_addid(xd, kv->num);
818 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
820 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
824 else if (kv->eof == 1)
826 cbdata->current_sub++;
829 case REPOKEY_TYPE_FLEXARRAY:
831 data_addid(xd, kv->num);
833 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
834 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
836 if (xd->len - cbdata->lastlen > cbdata->maxdata)
837 cbdata->maxdata = xd->len - cbdata->lastlen;
838 cbdata->lastlen = xd->len;
842 fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
845 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
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 */
856 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
858 struct cbdata *cbdata = vcbdata;
859 return repo_write_adddata(cbdata, data, key, kv);
862 /* traverse through directory with first child "dir" */
864 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
870 /* special case for '/', which has to come first */
873 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
875 if (used && !used[sib])
877 if (sib == 1 && parent == 1)
878 continue; /* already did that one above */
882 /* now go through all the siblings we just added and
883 * do recursive calls on them */
885 for (; parent < lastn; parent++)
887 sib = dirmap[parent];
888 if (used && used[sib] != 2) /* 2: used as parent */
890 child = dirpool_child(dp, sib);
893 dirmap[n++] = -parent; /* start new block */
894 n = traverse_dirs(dp, dirmap, n, child, used);
901 write_compressed_page(FILE *fp, unsigned char *page, int len)
904 unsigned char cpage[REPOPAGE_BLOBSIZE];
906 clen = repopagestore_compress_page(page, len, cpage, len - 1);
909 write_u32(fp, len * 2);
910 write_blob(fp, page, len);
914 write_u32(fp, clen * 2 + 1);
915 write_blob(fp, cpage, clen);
919 static Id verticals[] = {
921 SOLVABLE_DESCRIPTION,
930 static char *languagetags[] = {
932 "solvable:description:",
933 "solvable:messageins:",
934 "solvable:messagedel:",
940 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
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;
960 * the code works the following way:
962 * 1) find which keys should be written
963 * 2) collect usage information for keys/ids/dirids, create schema
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
971 repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Id **keyarrayp)
973 Pool *pool = repo->pool;
979 unsigned int solv_flags;
988 unsigned char *repodataused;
989 int anyrepodataused = 0;
990 int anysolvableused = 0;
992 struct cbdata cbdata;
995 int poolusage, dirpoolusage, idused, dirused;
998 Repodata *data, *dirpooldata;
1009 Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1012 memset(&cbdata, 0, sizeof(cbdata));
1014 cbdata.target = ⌖
1016 repodata_initdata(&target, repo, 1);
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 */
1023 /* start with all KEY_STORAGE_SOLVABLE ids */
1025 n = ID_NUM_INTERNAL;
1026 FOR_REPODATAS(repo, i, data)
1028 cbdata.keymap = solv_calloc(n, sizeof(Id));
1029 cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1030 repodataused = solv_calloc(repo->nrepodata, 1);
1035 /* add keys for STORAGE_SOLVABLE */
1036 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; 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;
1045 keyd.type = REPOKEY_TYPE_U32;
1047 keyd.storage = KEY_STORAGE_SOLVABLE;
1050 keyd.storage = keyfilter(repo, &keyd, kfdata);
1051 if (keyd.storage == KEY_STORAGE_DROPPED)
1053 keyd.storage = KEY_STORAGE_SOLVABLE;
1057 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1060 if (repo->nsolvables)
1063 keyd.name = REPOSITORY_SOLVABLES;
1064 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1066 keyd.storage = KEY_STORAGE_INCORE;
1067 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1075 n = ID_NUM_INTERNAL;
1076 FOR_REPODATAS(repo, i, data)
1078 cbdata.keymapstart[i] = n;
1079 cbdata.keymap[n++] = 0; /* key 0 */
1085 /* check if we want this repodata */
1086 memset(&keyd, 0, sizeof(keyd));
1090 if (keyfilter(repo, &keyd, kfdata) == -1)
1093 for (j = 1; j < data->nkeys; j++, n++)
1095 key = data->keys + j;
1096 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1098 cbdata.keymap[n] = cbdata.keymap[key->name];
1101 if (key->type == REPOKEY_TYPE_DELETED)
1103 cbdata.keymap[n] = 0;
1106 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1108 Repokey keyd = *key;
1109 keyd.size = repodata_globalize_id(data, key->size, 1);
1110 id = repodata_key2id(&target, &keyd, 0);
1113 id = repodata_key2id(&target, key, 0);
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)
1124 keyd.storage = keyfilter(repo, &keyd, kfdata);
1125 if (keyd.storage == KEY_STORAGE_DROPPED)
1127 cbdata.keymap[n] = 0;
1131 id = repodata_key2id(&target, &keyd, 1);
1133 cbdata.keymap[n] = id;
1134 /* load repodata if not already loaded */
1135 if (data->state == REPODATA_STUB)
1137 if (data->loadcallback)
1138 data->loadcallback(data);
1140 data->state = REPODATA_ERROR;
1141 if (data->state != REPODATA_ERROR)
1143 /* redo this repodata! */
1145 n = cbdata.keymapstart[i];
1149 if (data->state == REPODATA_ERROR)
1152 cbdata.keymap[n] = 0;
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)
1161 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1163 idused = 1; /* dirs also use ids */
1169 if (data->localpool)
1172 poolusage = 3; /* need own pool */
1176 spool = &data->spool;
1183 else if (poolusage != 1)
1184 poolusage = 3; /* need own pool */
1190 dirpoolusage = 3; /* need own dirpool */
1194 dirpool = &data->dirpool;
1201 /* 0: no pool needed at all */
1202 /* 1: use global pool */
1203 /* 2: use repodata local pool */
1204 /* 3: need own pool */
1207 spool = &target.spool;
1208 /* hack: reuse global pool data so we don't have to map pool ids */
1211 stringpool_free(spool);
1212 stringpool_clone(spool, &pool->ss);
1214 cbdata.ownspool = spool;
1216 else if (poolusage == 0 || poolusage == 1)
1222 if (dirpoolusage == 3)
1224 dirpool = &target.dirpool;
1226 cbdata.owndirpool = dirpool;
1229 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1232 /********************************************************************/
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);
1241 /* copy keys if requested */
1244 *keyarrayp = solv_calloc(2 * target.nkeys + 1, sizeof(Id));
1245 for (i = 1; i < target.nkeys; i++)
1247 (*keyarrayp)[2 * i - 2] = target.keys[i].name;
1248 (*keyarrayp)[2 * i - 1] = target.keys[i].type;
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++)
1258 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1259 if (key->type == REPOKEY_TYPE_CONSTANTID)
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);
1266 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1269 stringpool_freehash(spool); /* free some mem */
1273 /********************************************************************/
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
1280 reloff = spool->nstrings;
1282 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1284 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1285 needid[0].map = reloff;
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));
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)
1298 if (!repodataused[j])
1300 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1303 /* add solvables if needed (may revert later) */
1304 if (repo->nsolvables)
1306 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1307 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1310 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1312 idarraydata = repo->idarraydata;
1314 anysolvableused = 0;
1315 cbdata.doingsolvables = 1;
1316 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1318 if (s->repo != repo)
1321 /* set schema info, keep in sync with further down */
1323 if (cbdata.keymap[SOLVABLE_NAME])
1325 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1326 needid[s->name].need++;
1328 if (cbdata.keymap[SOLVABLE_ARCH])
1330 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1331 needid[s->arch].need++;
1333 if (cbdata.keymap[SOLVABLE_EVR])
1335 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1336 needid[s->evr].need++;
1338 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1340 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1341 needid[s->vendor].need++;
1343 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1345 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1346 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1348 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1350 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1351 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1353 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1355 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1356 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1358 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1360 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1361 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1363 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1365 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1366 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1368 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1370 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1371 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1373 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1375 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1376 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1378 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1380 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1381 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1383 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1385 *sp++ = cbdata.keymap[RPM_RPMDBID];
1386 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1390 if (anyrepodataused)
1392 FOR_REPODATAS(repo, j, data)
1394 if (!repodataused[j])
1396 if (i < data->start || i >= data->end)
1398 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1399 needid = cbdata.needid;
1403 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1404 if (cbdata.solvschemata[n])
1405 anysolvableused = 1;
1408 cbdata.doingsolvables = 0;
1409 assert(n == repo->nsolvables);
1411 if (repo->nsolvables && !anysolvableused)
1413 /* strip off solvable from the main schema */
1414 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1416 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1418 *sp = target.schemadata[target.schemata[mainschema] + i];
1419 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1422 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1424 target.schemadatalen = target.schemata[mainschema];
1426 repodata_free_schemahash(&target);
1427 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1430 /********************************************************************/
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;
1437 for (n = i = 1; i < target.nkeys; i++)
1444 target.keys[n] = target.keys[i];
1447 *keyarrayp[2 * n - 2] = *keyarrayp[2 * i - 2];
1448 *keyarrayp[2 * n - 1] = *keyarrayp[2 * i - 1];
1456 /* terminate array */
1457 *keyarrayp[2 * n - 2] = 0;
1458 *keyarrayp[2 * n - 1] = 0;
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);
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++)
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++;
1479 /********************************************************************/
1481 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1483 /* no dirs used at all */
1484 cbdata.dirused = solv_free(cbdata.dirused);
1488 /* increment need id for used dir components */
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++)
1502 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1504 if (cbdata.dirused && !cbdata.dirused[i])
1506 id = dirpool->dirs[i];
1509 if (dirpooldata && cbdata.ownspool && id > 1)
1511 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1512 needid = cbdata.needid;
1519 /********************************************************************/
1522 * create mapping table, new keys are sorted by needid[].need
1524 * needid[key].need : old key -> new key
1525 * needid[key].map : new key -> old key
1528 /* zero out id 0 and rel 0 just in case */
1529 reloff = needid[0].map;
1531 needid[reloff].need = 0;
1533 for (i = 1; i < reloff + pool->nrels; i++)
1537 solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1539 /* make first entry '' */
1541 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1543 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1544 /* now needid is in new order, needid[newid].map -> oldid */
1546 /* calculate string space size, also zero out needid[].need */
1548 for (i = 1; i < reloff; i++)
1550 if (!needid[i].need)
1551 break; /* as we have sorted, every entry after this also has need == 0 */
1553 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1555 nstrings = i; /* our new string id end */
1557 /* make needid[oldid].need point to newid */
1558 for (i = 1; i < nstrings; i++)
1559 needid[needid[i].map].need = i;
1561 /* same as above for relations */
1562 for (i = 0; i < pool->nrels; i++)
1564 if (!needid[reloff + i].need)
1566 needid[reloff + i].need = 0;
1568 nrels = i; /* our new rel id end */
1570 for (i = 0; i < nrels; i++)
1571 needid[needid[reloff + i].map].need = nstrings + i;
1573 /* now we have: needid[oldid].need -> newid
1574 needid[newid].map -> oldid
1575 both for strings and relations */
1578 /********************************************************************/
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));
1594 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
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++)
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;
1611 /* now the new target directory structure is complete (dirmap), and we have
1612 * dirused[olddirid] -> newdirid */
1615 /********************************************************************/
1618 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1621 cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1623 xd = cbdata.extdata;
1624 cbdata.current_sub = 0;
1625 /* add main schema */
1627 data_addid(xd, mainschema);
1630 FOR_REPODATAS(repo, j, data)
1632 if (!repodataused[j])
1634 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1638 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1639 cbdata.maxdata = xd->len - cbdata.lastlen;
1640 cbdata.lastlen = xd->len;
1642 if (anysolvableused)
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++)
1648 if (s->repo != repo)
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)
1680 FOR_REPODATAS(repo, j, data)
1682 if (!repodataused[j])
1684 if (i < data->start || i >= data->end)
1686 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1689 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1690 cbdata.maxdata = xd->len - cbdata.lastlen;
1691 cbdata.lastlen = xd->len;
1694 cbdata.doingsolvables = 0;
1697 assert(cbdata.current_sub == cbdata.nsubschemata);
1698 if (cbdata.subschemata)
1700 cbdata.subschemata = solv_free(cbdata.subschemata);
1701 cbdata.nsubschemata = 0;
1704 /********************************************************************/
1708 /* write file header */
1709 write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1710 write_u32(fp, SOLV_VERSION_8);
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);
1721 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1722 write_u32(fp, solv_flags);
1725 * calculate prefix encoding of the strings
1727 unsigned char *prefixcomp = solv_malloc(nstrings);
1728 unsigned int compsum = 0;
1732 for (i = 1; i < nstrings; i++)
1734 char *str = spool->stringspace + spool->strings[needid[i].map];
1736 for (same = 0; same < 255; same++)
1737 if (!old_str[same] || old_str[same] != str[same])
1739 prefixcomp[i] = same;
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)
1752 for (i = 1; i < nstrings; i++)
1754 char *str = spool->stringspace + spool->strings[needid[i].map];
1755 write_u8(fp, prefixcomp[i]);
1756 write_str(fp, str + prefixcomp[i]);
1759 solv_free(prefixcomp);
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);
1771 for (i = 1; i < nstrings; i++)
1773 char *str = spool->stringspace + spool->strings[needid[i].map];
1776 for (same = 0; same < 255; same++)
1777 if (!old_str[same] || !str[same] || old_str[same] != str[same])
1780 len = strlen(str + same) + 1;
1781 memcpy(pp, str + same, len);
1789 write_u32(fp, sizeid);
1790 write_u32(fp, pp - prefix);
1793 if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1795 perror("write error prefix");
1805 for (i = 0; i < nrels; i++)
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);
1814 * write dirs (skip both root and / entry)
1816 for (i = 2; i < ndirmap; i++)
1819 write_id(fp, dirmap[i]);
1821 write_id(fp, nstrings - dirmap[i]);
1828 for (i = 1; i < target.nkeys; i++)
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)
1834 if (target.keys[i].type == type_constantid)
1835 write_id(fp, needid[target.keys[i].size].need);
1837 write_id(fp, target.keys[i].size);
1840 write_id(fp, cbdata.extdata[i].len);
1841 write_id(fp, target.keys[i].storage);
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));
1851 /********************************************************************/
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);
1859 /* do we have vertical data? */
1860 for (i = 1; i < target.nkeys; i++)
1861 if (cbdata.extdata[i].len)
1863 if (i < target.nkeys)
1865 /* yes, write it in pages */
1866 unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
1867 int l, ll, lpage = 0;
1869 write_u32(fp, REPOPAGE_BLOBSIZE);
1870 for (i = 1; i < target.nkeys; i++)
1872 if (!cbdata.extdata[i].len)
1874 l = cbdata.extdata[i].len;
1875 dp = cbdata.extdata[i].buf;
1878 ll = REPOPAGE_BLOBSIZE - lpage;
1881 memcpy(vpage + lpage, dp, ll);
1885 if (lpage == REPOPAGE_BLOBSIZE)
1887 write_compressed_page(fp, vpage, lpage);
1893 write_compressed_page(fp, vpage, lpage);
1896 for (i = 1; i < target.nkeys; i++)
1897 solv_free(cbdata.extdata[i].buf);
1898 solv_free(cbdata.extdata);
1900 repodata_freedata(&target);
1903 solv_free(cbdata.solvschemata);
1904 solv_free(cbdata.schema);
1906 solv_free(cbdata.keymap);
1907 solv_free(cbdata.keymapstart);
1908 solv_free(cbdata.dirused);
1909 solv_free(repodataused);
1913 struct repodata_write_data {
1914 int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1920 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1922 struct repodata_write_data *wd = kfdata;
1924 /* XXX: special repodata selection hack */
1925 if (key->name == 1 && key->size != wd->repodataid)
1927 if (key->storage == KEY_STORAGE_SOLVABLE)
1928 return KEY_STORAGE_DROPPED; /* not part of this repodata */
1930 return (*wd->keyfilter)(repo, key, wd->kfdata);
1931 return key->storage;
1935 repodata_write(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata)
1937 struct repodata_write_data wd;
1939 wd.keyfilter = keyfilter;
1941 wd.repodataid = data - data->repo->repodata;
1942 return repo_write(data->repo, fp, repodata_write_keyfilter, &wd, 0);