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 = sat_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 sat_sort(sids, i, sizeof(Id), cmp_ids, 0);
308 sat_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 = sat_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 = sat_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 sat_sort(sids, i, sizeof(Id), cmp_ids, 0);
472 sat_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 = sat_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 = sat_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 = sat_calloc(cbdata->target->nkeys, sizeof(Id));
646 cbdata->sp = cbdata->schema;
648 else if (kv->eof == 1)
650 cbdata->current_sub++;
652 cbdata->subschemata = sat_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
653 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
655 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
657 cbdata->sp = cbdata->schema;
661 sat_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 = sat_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
681 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
682 cbdata->sp = kv->eof == 2 ? sp - 1: sp;
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 ? id2str(repo->pool, s->name) : "", key->name, 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[BLOB_PAGESIZE];
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 = 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 (i = 0; i < repo->nrepodata; i++)
1027 n += repo->repodata[i].nkeys;
1028 cbdata.keymap = sat_calloc(n, sizeof(Id));
1029 cbdata.keymapstart = sat_calloc(repo->nrepodata, sizeof(Id));
1030 repodataused = sat_calloc(repo->nrepodata, 1);
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 (i = 0; i < repo->nrepodata; i++)
1078 data = repo->repodata + i;
1079 cbdata.keymapstart[i] = n;
1080 cbdata.keymap[n++] = 0; /* key 0 */
1086 /* check if we want this repodata */
1087 memset(&keyd, 0, sizeof(keyd));
1091 if (keyfilter(repo, &keyd, kfdata) == -1)
1094 for (j = 1; j < data->nkeys; j++, n++)
1096 key = data->keys + j;
1097 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1099 cbdata.keymap[n] = cbdata.keymap[key->name];
1102 if (key->type == REPOKEY_TYPE_DELETED)
1104 cbdata.keymap[n] = 0;
1107 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1109 Repokey keyd = *key;
1110 keyd.size = repodata_globalize_id(data, key->size, 1);
1111 id = repodata_key2id(&target, &keyd, 0);
1114 id = repodata_key2id(&target, key, 0);
1117 Repokey keyd = *key;
1118 keyd.storage = KEY_STORAGE_INCORE;
1119 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1120 keyd.size = repodata_globalize_id(data, key->size, 1);
1121 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1125 keyd.storage = keyfilter(repo, &keyd, kfdata);
1126 if (keyd.storage == KEY_STORAGE_DROPPED)
1128 cbdata.keymap[n] = 0;
1132 id = repodata_key2id(&target, &keyd, 1);
1134 cbdata.keymap[n] = id;
1135 /* load repodata if not already loaded */
1136 if (data->state == REPODATA_STUB)
1138 if (data->loadcallback)
1139 data->loadcallback(data);
1141 data->state = REPODATA_ERROR;
1142 if (data->state != REPODATA_ERROR)
1144 /* redo this repodata! */
1146 n = cbdata.keymapstart[i];
1150 if (data->state == REPODATA_ERROR)
1153 cbdata.keymap[n] = 0;
1157 repodataused[i] = 1;
1158 anyrepodataused = 1;
1159 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1160 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1162 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1164 idused = 1; /* dirs also use ids */
1170 if (data->localpool)
1173 poolusage = 3; /* need own pool */
1177 spool = &data->spool;
1184 else if (poolusage != 1)
1185 poolusage = 3; /* need own pool */
1191 dirpoolusage = 3; /* need own dirpool */
1195 dirpool = &data->dirpool;
1202 /* 0: no pool needed at all */
1203 /* 1: use global pool */
1204 /* 2: use repodata local pool */
1205 /* 3: need own pool */
1208 spool = &target.spool;
1209 /* hack: reuse global pool data so we don't have to map pool ids */
1212 stringpool_free(spool);
1213 stringpool_clone(spool, &pool->ss);
1215 cbdata.ownspool = spool;
1217 else if (poolusage == 0 || poolusage == 1)
1223 if (dirpoolusage == 3)
1225 dirpool = &target.dirpool;
1227 cbdata.owndirpool = dirpool;
1230 cbdata.dirused = sat_calloc(dirpool->ndirs, sizeof(Id));
1233 /********************************************************************/
1235 fprintf(stderr, "poolusage: %d\n", poolusage);
1236 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1237 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1238 for (i = 1; i < target.nkeys; i++)
1239 fprintf(stderr, " %2d: %s[%d] %d %d %d\n", i, id2str(pool, target.keys[i].name), target.keys[i].name, target.keys[i].type, target.keys[i].size, target.keys[i].storage);
1242 /* copy keys if requested */
1245 *keyarrayp = sat_calloc(2 * target.nkeys + 1, sizeof(Id));
1246 for (i = 1; i < target.nkeys; i++)
1248 (*keyarrayp)[2 * i - 2] = target.keys[i].name;
1249 (*keyarrayp)[2 * i - 1] = target.keys[i].type;
1255 /* put all the keys we need in our string pool */
1256 /* put mapped ids right into target.keys */
1257 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1259 key->name = stringpool_str2id(spool, id2str(pool, key->name), 1);
1260 if (key->type == REPOKEY_TYPE_CONSTANTID)
1262 key->type = stringpool_str2id(spool, id2str(pool, key->type), 1);
1263 type_constantid = key->type;
1264 key->size = stringpool_str2id(spool, id2str(pool, key->size), 1);
1267 key->type = stringpool_str2id(spool, id2str(pool, key->type), 1);
1270 stringpool_freehash(spool); /* free some mem */
1274 /********************************************************************/
1276 /* set needed count of all strings and rels,
1277 * find which keys are used in the solvables
1278 * put all strings in own spool
1281 reloff = spool->nstrings;
1283 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1285 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1286 needid[0].map = reloff;
1288 cbdata.needid = needid;
1289 cbdata.schema = sat_calloc(target.nkeys, sizeof(Id));
1290 cbdata.sp = cbdata.schema;
1291 cbdata.solvschemata = sat_calloc(repo->nsolvables, sizeof(Id));
1293 /* create main schema */
1294 cbdata.sp = cbdata.schema;
1295 /* collect all other data from all repodatas */
1296 /* XXX: merge arrays of equal keys? */
1297 for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1299 if (!repodataused[j])
1301 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1304 /* add solvables if needed (may revert later) */
1305 if (repo->nsolvables)
1307 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1308 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1311 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1313 idarraydata = repo->idarraydata;
1315 anysolvableused = 0;
1316 cbdata.doingsolvables = 1;
1317 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1319 if (s->repo != repo)
1322 /* set schema info, keep in sync with further down */
1324 if (cbdata.keymap[SOLVABLE_NAME])
1326 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1327 needid[s->name].need++;
1329 if (cbdata.keymap[SOLVABLE_ARCH])
1331 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1332 needid[s->arch].need++;
1334 if (cbdata.keymap[SOLVABLE_EVR])
1336 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1337 needid[s->evr].need++;
1339 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1341 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1342 needid[s->vendor].need++;
1344 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1346 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1347 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1349 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1351 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1352 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1354 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1356 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1357 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1359 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1361 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1362 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1364 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1366 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1367 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1369 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1371 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1372 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1374 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1376 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1377 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1379 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1381 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1382 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1384 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1386 *sp++ = cbdata.keymap[RPM_RPMDBID];
1387 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1391 if (anyrepodataused)
1393 for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1395 if (!repodataused[j])
1397 if (i < data->start || i >= data->end)
1399 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1400 needid = cbdata.needid;
1404 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1405 if (cbdata.solvschemata[n])
1406 anysolvableused = 1;
1409 cbdata.doingsolvables = 0;
1410 assert(n == repo->nsolvables);
1412 if (repo->nsolvables && !anysolvableused)
1414 /* strip off solvable from the main schema */
1415 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1417 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1419 *sp = target.schemadata[target.schemata[mainschema] + i];
1420 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1423 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1425 target.schemadatalen = target.schemata[mainschema];
1427 repodata_free_schemahash(&target);
1428 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1431 /********************************************************************/
1433 /* remove unused keys */
1434 keyused = sat_calloc(target.nkeys, sizeof(Id));
1435 for (i = 1; i < target.schemadatalen; i++)
1436 keyused[target.schemadata[i]] = 1;
1438 for (n = i = 1; i < target.nkeys; i++)
1445 target.keys[n] = target.keys[i];
1448 *keyarrayp[2 * n - 2] = *keyarrayp[2 * i - 2];
1449 *keyarrayp[2 * n - 1] = *keyarrayp[2 * i - 1];
1457 /* terminate array */
1458 *keyarrayp[2 * n - 2] = 0;
1459 *keyarrayp[2 * n - 1] = 0;
1462 /* update schema data to the new key ids */
1463 for (i = 1; i < target.schemadatalen; i++)
1464 target.schemadata[i] = keyused[target.schemadata[i]];
1465 /* update keymap to the new key ids */
1466 for (i = 0; i < cbdata.nkeymap; i++)
1467 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1468 keyused = sat_free(keyused);
1470 /* increment needid of the used keys, they are already mapped to
1471 * the correct string pool */
1472 for (i = 1; i < target.nkeys; i++)
1474 if (target.keys[i].type == type_constantid)
1475 needid[target.keys[i].size].need++;
1476 needid[target.keys[i].name].need++;
1477 needid[target.keys[i].type].need++;
1480 /********************************************************************/
1482 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1484 /* no dirs used at all */
1485 cbdata.dirused = sat_free(cbdata.dirused);
1489 /* increment need id for used dir components */
1492 /* if we have own dirpool, all entries in it are used.
1493 also, all comp ids are already mapped by putinowndirpool(),
1494 so we can simply increment needid.
1495 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1496 /* else we re-use a dirpool of repodata "dirpooldata".
1497 dirused tells us which of the ids are used.
1498 we need to map comp ids if we generate a new pool.
1499 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1500 for (i = 1; i < dirpool->ndirs; i++)
1503 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1505 if (cbdata.dirused && !cbdata.dirused[i])
1507 id = dirpool->dirs[i];
1510 if (dirpooldata && cbdata.ownspool && id > 1)
1512 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1513 needid = cbdata.needid;
1520 /********************************************************************/
1523 * create mapping table, new keys are sorted by needid[].need
1525 * needid[key].need : old key -> new key
1526 * needid[key].map : new key -> old key
1529 /* zero out id 0 and rel 0 just in case */
1530 reloff = needid[0].map;
1532 needid[reloff].need = 0;
1534 for (i = 1; i < reloff + pool->nrels; i++)
1538 sat_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1540 /* make first entry '' */
1542 sat_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1544 sat_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1545 /* now needid is in new order, needid[newid].map -> oldid */
1547 /* calculate string space size, also zero out needid[].need */
1549 for (i = 1; i < reloff; i++)
1551 if (!needid[i].need)
1552 break; /* as we have sorted, every entry after this also has need == 0 */
1554 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1556 nstrings = i; /* our new string id end */
1558 /* make needid[oldid].need point to newid */
1559 for (i = 1; i < nstrings; i++)
1560 needid[needid[i].map].need = i;
1562 /* same as above for relations */
1563 for (i = 0; i < pool->nrels; i++)
1565 if (!needid[reloff + i].need)
1567 needid[reloff + i].need = 0;
1569 nrels = i; /* our new rel id end */
1571 for (i = 0; i < nrels; i++)
1572 needid[needid[reloff + i].map].need = nstrings + i;
1574 /* now we have: needid[oldid].need -> newid
1575 needid[newid].map -> oldid
1576 both for strings and relations */
1579 /********************************************************************/
1585 /* create our new target directory structure by traversing through all
1586 * used dirs. This will concatenate blocks with the same parent
1587 * directory into single blocks.
1588 * Instead of components, traverse_dirs stores the old dirids,
1589 * we will change this in the second step below */
1590 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1591 if (cbdata.dirused && !cbdata.dirused[1])
1592 cbdata.dirused[1] = 1; /* always want / entry */
1593 dirmap = sat_calloc(dirpool->ndirs, sizeof(Id));
1595 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1597 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1598 /* change dirmap so that it maps from "new dirid" to "new compid" */
1599 if (!cbdata.dirused)
1600 cbdata.dirused = sat_malloc2(dirpool->ndirs, sizeof(Id));
1601 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1602 for (i = 1; i < ndirmap; i++)
1606 cbdata.dirused[dirmap[i]] = i;
1607 id = dirpool->dirs[dirmap[i]];
1608 if (dirpooldata && cbdata.ownspool && id > 1)
1609 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1610 dirmap[i] = needid[id].need;
1612 /* now the new target directory structure is complete (dirmap), and we have
1613 * dirused[olddirid] -> newdirid */
1616 /********************************************************************/
1619 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1622 cbdata.extdata = sat_calloc(target.nkeys, sizeof(struct extdata));
1624 xd = cbdata.extdata;
1625 cbdata.current_sub = 0;
1626 /* add main schema */
1628 data_addid(xd, mainschema);
1631 for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1633 if (!repodataused[j])
1635 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1639 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1640 cbdata.maxdata = xd->len - cbdata.lastlen;
1641 cbdata.lastlen = xd->len;
1643 if (anysolvableused)
1645 data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1646 cbdata.doingsolvables = 1;
1647 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1649 if (s->repo != repo)
1651 data_addid(xd, cbdata.solvschemata[n]);
1652 if (cbdata.keymap[SOLVABLE_NAME])
1653 data_addid(xd, needid[s->name].need);
1654 if (cbdata.keymap[SOLVABLE_ARCH])
1655 data_addid(xd, needid[s->arch].need);
1656 if (cbdata.keymap[SOLVABLE_EVR])
1657 data_addid(xd, needid[s->evr].need);
1658 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1659 data_addid(xd, needid[s->vendor].need);
1660 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1661 data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1662 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1663 data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1664 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1665 data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1666 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1667 data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1668 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1669 data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1670 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1671 data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1672 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1673 data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1674 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1675 data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1676 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1677 data_addu32(xd, repo->rpmdbid[i - repo->start]);
1678 if (anyrepodataused)
1681 for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1683 if (!repodataused[j])
1685 if (i < data->start || i >= data->end)
1687 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1690 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1691 cbdata.maxdata = xd->len - cbdata.lastlen;
1692 cbdata.lastlen = xd->len;
1695 cbdata.doingsolvables = 0;
1698 assert(cbdata.current_sub == cbdata.nsubschemata);
1699 if (cbdata.subschemata)
1701 cbdata.subschemata = sat_free(cbdata.subschemata);
1702 cbdata.nsubschemata = 0;
1705 /********************************************************************/
1709 /* write file header */
1710 write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1711 write_u32(fp, SOLV_VERSION_8);
1715 write_u32(fp, nstrings);
1716 write_u32(fp, nrels);
1717 write_u32(fp, ndirmap);
1718 write_u32(fp, anysolvableused ? repo->nsolvables : 0);
1719 write_u32(fp, target.nkeys);
1720 write_u32(fp, target.nschemata);
1722 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1723 write_u32(fp, solv_flags);
1726 * calculate prefix encoding of the strings
1728 unsigned char *prefixcomp = sat_malloc(nstrings);
1729 unsigned int compsum = 0;
1733 for (i = 1; i < nstrings; i++)
1735 char *str = spool->stringspace + spool->strings[needid[i].map];
1737 for (same = 0; same < 255; same++)
1738 if (!old_str[same] || old_str[same] != str[same])
1740 prefixcomp[i] = same;
1748 write_u32(fp, sizeid);
1749 /* we save compsum bytes but need 1 extra byte for every string */
1750 write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1751 if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1753 for (i = 1; i < nstrings; i++)
1755 char *str = spool->stringspace + spool->strings[needid[i].map];
1756 write_u8(fp, prefixcomp[i]);
1757 write_str(fp, str + prefixcomp[i]);
1760 sat_free(prefixcomp);
1763 /* Build the prefix-encoding of the string pool. We need to know
1764 the size of that before writing it to the file, so we have to
1765 build a separate buffer for that. As it's temporarily possible
1766 that this actually is an expansion we can't easily reuse the
1767 stringspace for this. The max expansion per string is 1 byte,
1768 so it will fit into sizeid+nstrings bytes. */
1769 char *prefix = sat_malloc(sizeid + nstrings);
1772 for (i = 1; i < nstrings; i++)
1774 char *str = spool->stringspace + spool->strings[needid[i].map];
1777 for (same = 0; same < 255; same++)
1778 if (!old_str[same] || !str[same] || old_str[same] != str[same])
1781 len = strlen(str + same) + 1;
1782 memcpy(pp, str + same, len);
1790 write_u32(fp, sizeid);
1791 write_u32(fp, pp - prefix);
1794 if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1796 perror("write error prefix");
1806 for (i = 0; i < nrels; i++)
1808 ran = pool->rels + (needid[reloff + i].map - reloff);
1809 write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1810 write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1811 write_u8(fp, ran->flags);
1815 * write dirs (skip both root and / entry)
1817 for (i = 2; i < ndirmap; i++)
1820 write_id(fp, dirmap[i]);
1822 write_id(fp, nstrings - dirmap[i]);
1829 for (i = 1; i < target.nkeys; i++)
1831 write_id(fp, needid[target.keys[i].name].need);
1832 write_id(fp, needid[target.keys[i].type].need);
1833 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1835 if (target.keys[i].type == type_constantid)
1836 write_id(fp, needid[target.keys[i].size].need);
1838 write_id(fp, target.keys[i].size);
1841 write_id(fp, cbdata.extdata[i].len);
1842 write_id(fp, target.keys[i].storage);
1848 write_id(fp, target.schemadatalen); /* XXX -1? */
1849 for (i = 1; i < target.nschemata; i++)
1850 write_idarray(fp, pool, 0, repodata_id2schema(&target, i));
1852 /********************************************************************/
1854 write_id(fp, cbdata.maxdata);
1855 write_id(fp, cbdata.extdata[0].len);
1856 if (cbdata.extdata[0].len)
1857 write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1858 sat_free(cbdata.extdata[0].buf);
1860 /* do we have vertical data? */
1861 for (i = 1; i < target.nkeys; i++)
1862 if (cbdata.extdata[i].len)
1864 if (i < target.nkeys)
1866 /* yes, write it in pages */
1867 unsigned char *dp, vpage[BLOB_PAGESIZE];
1868 int l, ll, lpage = 0;
1870 write_u32(fp, BLOB_PAGESIZE);
1871 for (i = 1; i < target.nkeys; i++)
1873 if (!cbdata.extdata[i].len)
1875 l = cbdata.extdata[i].len;
1876 dp = cbdata.extdata[i].buf;
1879 ll = BLOB_PAGESIZE - lpage;
1882 memcpy(vpage + lpage, dp, ll);
1886 if (lpage == BLOB_PAGESIZE)
1888 write_compressed_page(fp, vpage, lpage);
1894 write_compressed_page(fp, vpage, lpage);
1897 for (i = 1; i < target.nkeys; i++)
1898 sat_free(cbdata.extdata[i].buf);
1899 sat_free(cbdata.extdata);
1901 repodata_freedata(&target);
1904 sat_free(cbdata.solvschemata);
1905 sat_free(cbdata.schema);
1907 sat_free(cbdata.keymap);
1908 sat_free(cbdata.keymapstart);
1909 sat_free(cbdata.dirused);
1910 sat_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 repo_write(data->repo, fp, repodata_write_keyfilter, &wd, 0);