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>
29 #include "repo_write.h"
32 /*------------------------------------------------------------------*/
33 /* Id map optimizations */
35 typedef struct needid {
41 #define RELOFF(id) (needid[0].map + GETRELID(id))
45 * idarray: array of Ids, ID_NULL terminated
46 * needid: array of Id->NeedId
48 * return size of array (including trailing zero)
53 incneedid(Pool *pool, Id id, NeedId *needid)
57 Reldep *rd = GETRELDEP(pool, id);
58 needid[RELOFF(id)].need++;
59 if (ISRELDEP(rd->evr))
60 incneedid(pool, rd->evr, needid);
62 needid[rd->evr].need++;
69 incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
76 while ((id = *idarray++) != 0)
81 Reldep *rd = GETRELDEP(pool, id);
82 needid[RELOFF(id)].need++;
83 if (ISRELDEP(rd->evr))
84 incneedid(pool, rd->evr, needid);
86 needid[rd->evr].need++;
100 needid_cmp_need(const void *ap, const void *bp, void *dp)
102 const NeedId *a = ap;
103 const NeedId *b = bp;
105 r = b->need - a->need;
108 return a->map - b->map;
112 needid_cmp_need_s(const void *ap, const void *bp, void *dp)
114 const NeedId *a = ap;
115 const NeedId *b = bp;
116 Stringpool *spool = dp;
121 r = b->need - a->need;
124 as = spool->stringspace + spool->strings[a->map];
125 bs = spool->stringspace + spool->strings[b->map];
126 return strcmp(as, bs);
130 /*------------------------------------------------------------------*/
131 /* output helper routines, used for writing the header */
132 /* (the data itself is accumulated in memory and written with
140 write_u32(Repodata *data, unsigned int x)
145 if (putc(x >> 24, fp) == EOF ||
146 putc(x >> 16, fp) == EOF ||
147 putc(x >> 8, fp) == EOF ||
150 data->error = pool_error(data->repo->pool, -1, "write error u32: %s", strerror(errno));
160 write_u8(Repodata *data, unsigned int x)
164 if (putc(x, data->fp) == EOF)
166 data->error = pool_error(data->repo->pool, -1, "write error u8: %s", strerror(errno));
175 write_blob(Repodata *data, void *blob, int len)
179 if (len && fwrite(blob, len, 1, data->fp) != 1)
181 data->error = pool_error(data->repo->pool, -1, "write error blob: %s", strerror(errno));
190 write_id(Repodata *data, Id x)
198 putc((x >> 28) | 128, fp);
200 putc((x >> 21) | 128, fp);
201 putc((x >> 14) | 128, fp);
204 putc((x >> 7) | 128, fp);
205 if (putc(x & 127, fp) == EOF)
207 data->error = pool_error(data->repo->pool, -1, "write error id: %s", strerror(errno));
212 write_id_eof(Repodata *data, Id x, int eof)
215 x = (x & 63) | ((x & ~63) << 1);
216 write_id(data, x | (eof ? 0 : 64));
222 write_str(Repodata *data, const char *str)
226 if (fputs(str, data->fp) == EOF || putc(0, data->fp) == EOF)
228 data->error = pool_error(data->repo->pool, -1, "write error str: %s", strerror(errno));
237 write_idarray(Repodata *data, Pool *pool, NeedId *needid, Id *ids)
251 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
253 id = (id & 63) | ((id & ~63) << 1);
259 write_id(data, id | 64);
272 Stringpool *ownspool;
281 Id *schema; /* schema construction space */
282 Id *sp; /* pointer in above */
283 Id *oldschema, *oldsp;
290 struct extdata *extdata;
299 int doingsolvables; /* working on solvables data */
303 #define NEEDED_BLOCK 1023
304 #define SCHEMATA_BLOCK 31
305 #define SCHEMATADATA_BLOCK 255
306 #define EXTDATA_BLOCK 4095
309 data_addid(struct extdata *xd, Id sx)
311 unsigned int x = (unsigned int)sx;
314 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
315 dp = xd->buf + xd->len;
320 *dp++ = (x >> 28) | 128;
322 *dp++ = (x >> 21) | 128;
323 *dp++ = (x >> 14) | 128;
326 *dp++ = (x >> 7) | 128;
328 xd->len = dp - xd->buf;
332 data_addideof(struct extdata *xd, Id sx, int eof)
334 unsigned int x = (unsigned int)sx;
337 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
338 dp = xd->buf + xd->len;
343 *dp++ = (x >> 27) | 128;
345 *dp++ = (x >> 20) | 128;
346 *dp++ = (x >> 13) | 128;
349 *dp++ = (x >> 6) | 128;
350 *dp++ = eof ? (x & 63) : (x & 63) | 64;
351 xd->len = dp - xd->buf;
355 data_addideof_len(Id sx)
357 unsigned int x = (unsigned int)sx;
362 return x >= (1 << 20) ? 4 : 3;
364 return x >= (1 << 6) ? 2 : 1;
368 data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
374 data_addid(xd, (Id)(hx >> 3));
375 xd->buf[xd->len - 1] |= 128;
378 data_addid(xd, (Id)(x | 0x80000000));
379 xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
382 data_addid(xd, (Id)x);
385 #define USE_REL_IDARRAY
386 #ifdef USE_REL_IDARRAY
389 cmp_ids(const void *pa, const void *pb, void *dp)
397 data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
410 for (len = 0; len < 64 && ids[len]; len++)
414 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
419 for (i = len + 1; ids[i]; i++)
421 sids = solv_malloc2(i, sizeof(Id));
422 memcpy(sids, lids, 64 * sizeof(Id));
423 for (; ids[len]; len++)
427 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
434 /* That bloody solvable:prereqmarker needs to stay in position :-( */
436 marker = needid[marker].need;
437 for (i = 0; i < len; i++)
438 if (sids[i] == marker)
441 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
443 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
447 /* The differencing above produces many runs of ones and twos. I tried
448 fairly elaborate schemes to RLE those, but they give only very mediocre
449 improvements in compression, as coding the escapes costs quite some
450 space. Even if they are coded only as bits in IDs. The best improvement
451 was about 2.7% for the whole .solv file. It's probably better to
452 invest some complexity into sharing idarrays, than RLEing. */
453 for (i = 0; i < len - 1; i++)
456 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
457 hence all real differences are offsetted by 1. Otherwise we would
458 have to handle negative differences, which would cost code space for
459 the encoding of the sign. We loose the exact mapping of prereq here,
460 but we know the result, so we can recover from that in the reader. */
468 /* XXX If difference is zero we have multiple equal elements,
469 we might want to skip writing them out. */
470 data_addideof(xd, id, 0);
477 data_addideof(xd, id, 1);
485 data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
493 while ((id = *ids++) != 0)
496 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
497 data_addideof(xd, id, *ids ? 0 : 1);
504 data_addblob(struct extdata *xd, unsigned char *blob, int len)
506 xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
507 memcpy(xd->buf + xd->len, blob, len);
512 data_addu32(struct extdata *xd, unsigned int num)
519 data_addblob(xd, d, 4);
523 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
525 const char *str = stringpool_id2str(ss, id);
526 id = stringpool_str2id(cbdata->ownspool, str, 1);
527 if (id >= cbdata->needid[0].map)
529 int oldoff = cbdata->needid[0].map;
530 int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
531 int nrels = cbdata->repo->pool->nrels;
532 cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
534 memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
535 memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
536 cbdata->needid[0].map = newoff;
542 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
546 parent = dirpool_parent(dp, dir);
548 parent = putinowndirpool(cbdata, data, dp, parent);
549 compid = dp->dirs[dir];
550 if (cbdata->ownspool && compid > 1)
551 compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
552 return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
556 * collect usage information about the dirs
557 * 1: dir used, no child of dir used
558 * 2: dir used as parent of another used dir
561 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
563 if (cbdata->dirused[dir])
565 cbdata->dirused[dir] = 1;
566 while ((dir = dirpool_parent(dp, dir)) != 0)
568 if (cbdata->dirused[dir] == 2)
570 if (cbdata->dirused[dir])
572 cbdata->dirused[dir] = 2;
575 cbdata->dirused[dir] = 2;
577 cbdata->dirused[0] = 2;
582 * collect key/id/dirid usage information, create needed schemas
585 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
590 if (key->name == REPOSITORY_SOLVABLES)
591 return SEARCH_NEXT_KEY; /* we do not want this one */
593 /* hack: ignore some keys, see BUGS */
594 if (data->repodataid != data->repo->nrepodata - 1)
595 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
596 return SEARCH_NEXT_KEY;
598 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
600 return SEARCH_NEXT_KEY; /* we do not want this one */
602 /* record key in schema */
603 if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
604 && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
609 case REPOKEY_TYPE_ID:
610 case REPOKEY_TYPE_IDARRAY:
612 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
613 id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
614 incneedid(repo->pool, id, cbdata->needid);
616 case REPOKEY_TYPE_DIR:
617 case REPOKEY_TYPE_DIRNUMNUMARRAY:
618 case REPOKEY_TYPE_DIRSTRARRAY:
620 if (cbdata->owndirpool)
621 putinowndirpool(cbdata, data, &data->dirpool, id);
623 setdirused(cbdata, &data->dirpool, id);
625 case REPOKEY_TYPE_FIXARRAY:
628 if (cbdata->oldschema)
630 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
631 return SEARCH_NEXT_KEY;
633 cbdata->oldschema = cbdata->schema;
634 cbdata->oldsp = cbdata->sp;
635 cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
636 cbdata->sp = cbdata->schema;
638 else if (kv->eof == 1)
640 cbdata->current_sub++;
642 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
643 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
645 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
647 cbdata->sp = cbdata->schema;
651 solv_free(cbdata->schema);
652 cbdata->schema = cbdata->oldschema;
653 cbdata->sp = cbdata->oldsp;
654 cbdata->oldsp = cbdata->oldschema = 0;
657 case REPOKEY_TYPE_FLEXARRAY:
661 *cbdata->sp++ = 0; /* mark start */
665 /* just finished a schema, rewind */
666 Id *sp = cbdata->sp - 1;
670 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
671 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
672 cbdata->sp = kv->eof == 2 ? sp - 1: sp;
682 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
684 struct cbdata *cbdata = vcbdata;
685 Repo *repo = data->repo;
689 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);
691 return repo_write_collect_needed(cbdata, repo, data, key, kv);
697 * encode all of the data into the correct buffers
701 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
710 if (key->name == REPOSITORY_SOLVABLES)
711 return SEARCH_NEXT_KEY;
713 /* hack: ignore some keys, see BUGS */
714 if (data->repodataid != data->repo->nrepodata - 1)
715 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
716 return SEARCH_NEXT_KEY;
718 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
720 return SEARCH_NEXT_KEY; /* we do not want this one */
722 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
724 xd = cbdata->extdata + rm; /* vertical buffer */
725 if (cbdata->vstart == -1)
726 cbdata->vstart = xd->len;
729 xd = cbdata->extdata + 0; /* incore buffer */
732 case REPOKEY_TYPE_VOID:
733 case REPOKEY_TYPE_CONSTANT:
734 case REPOKEY_TYPE_CONSTANTID:
736 case REPOKEY_TYPE_ID:
738 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
739 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
740 needid = cbdata->needid;
741 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
744 case REPOKEY_TYPE_IDARRAY:
746 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
747 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
748 needid = cbdata->needid;
749 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
750 data_addideof(xd, id, kv->eof);
752 case REPOKEY_TYPE_STR:
753 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
755 case REPOKEY_TYPE_MD5:
756 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
758 case REPOKEY_TYPE_SHA1:
759 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
761 case REPOKEY_TYPE_SHA224:
762 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
764 case REPOKEY_TYPE_SHA256:
765 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
767 case REPOKEY_TYPE_SHA384:
768 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
770 case REPOKEY_TYPE_SHA512:
771 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
773 case REPOKEY_TYPE_U32:
779 data_addblob(xd, v, 4);
781 case REPOKEY_TYPE_NUM:
782 data_addid64(xd, kv->num, kv->num2);
784 case REPOKEY_TYPE_DIR:
786 if (cbdata->owndirpool)
787 id = putinowndirpool(cbdata, data, &data->dirpool, id);
788 id = cbdata->dirused[id];
791 case REPOKEY_TYPE_BINARY:
792 data_addid(xd, kv->num);
794 data_addblob(xd, (unsigned char *)kv->str, kv->num);
796 case REPOKEY_TYPE_DIRNUMNUMARRAY:
798 if (cbdata->owndirpool)
799 id = putinowndirpool(cbdata, data, &data->dirpool, id);
800 id = cbdata->dirused[id];
802 data_addid(xd, kv->num);
803 data_addideof(xd, kv->num2, kv->eof);
805 case REPOKEY_TYPE_DIRSTRARRAY:
807 if (cbdata->owndirpool)
808 id = putinowndirpool(cbdata, data, &data->dirpool, id);
809 id = cbdata->dirused[id];
810 if (cbdata->filelistmode > 0)
812 xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
815 data_addideof(xd, id, kv->eof);
816 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
817 if (cbdata->filelistmode < 0)
820 case REPOKEY_TYPE_FIXARRAY:
825 data_addid(xd, kv->num);
826 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
828 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
832 else if (kv->eof == 1)
834 cbdata->current_sub++;
837 case REPOKEY_TYPE_FLEXARRAY:
839 data_addid(xd, kv->num);
841 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
842 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
844 if (xd->len - cbdata->lastlen > cbdata->maxdata)
845 cbdata->maxdata = xd->len - cbdata->lastlen;
846 cbdata->lastlen = xd->len;
850 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
853 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
855 /* we can re-use old data in the blob here! */
856 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
857 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
864 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
866 struct cbdata *cbdata = vcbdata;
867 return repo_write_adddata(cbdata, data, key, kv);
870 /* traverse through directory with first child "dir" */
872 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
878 /* special case for '/', which has to come first */
881 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
883 if (used && !used[sib])
885 if (sib == 1 && parent == 1)
886 continue; /* already did that one above */
890 /* check if our block has some content */
892 return n - 1; /* nope, drop parent id again */
894 /* now go through all the siblings we just added and
895 * do recursive calls on them */
897 for (; parent < lastn; parent++)
899 sib = dirmap[parent];
900 if (used && used[sib] != 2) /* 2: used as parent */
902 child = dirpool_child(dp, sib);
905 dirmap[n++] = -parent; /* start new block */
906 n = traverse_dirs(dp, dirmap, n, child, used);
913 write_compressed_page(Repodata *data, unsigned char *page, int len)
916 unsigned char cpage[REPOPAGE_BLOBSIZE];
918 clen = repopagestore_compress_page(page, len, cpage, len - 1);
921 write_u32(data, len * 2);
922 write_blob(data, page, len);
926 write_u32(data, clen * 2 + 1);
927 write_blob(data, cpage, clen);
931 static Id verticals[] = {
933 SOLVABLE_DESCRIPTION,
945 SOLVABLE_CHANGELOG_AUTHOR,
946 SOLVABLE_CHANGELOG_TEXT,
950 static char *languagetags[] = {
952 "solvable:description:",
953 "solvable:messageins:",
954 "solvable:messagedel:",
960 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
965 for (i = 0; verticals[i]; i++)
966 if (key->name == verticals[i])
967 return KEY_STORAGE_VERTICAL_OFFSET;
968 keyname = pool_id2str(repo->pool, key->name);
969 for (i = 0; languagetags[i] != 0; i++)
970 if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
971 return KEY_STORAGE_VERTICAL_OFFSET;
972 return KEY_STORAGE_INCORE;
976 * return true if the repodata contains the filelist (and just
977 * the filelist). The same code is used in the dataiterator. The way
978 * it is used is completely wrong, of course, as having the filelist
979 * key does not mean it is used for a specific solvable. Nevertheless
980 * it is better to have it than to write broken solv files.
983 is_filelist_extension(Repodata *data)
986 for (j = 1; j < data->nkeys; j++)
987 if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
994 write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
996 unsigned char *dp = xd->buf;
1000 int ll = REPOPAGE_BLOBSIZE - lpage;
1003 memcpy(vpage + lpage, dp, ll);
1007 if (lpage == REPOPAGE_BLOBSIZE)
1009 write_compressed_page(target, vpage, lpage);
1021 * the code works the following way:
1023 * 1) find which keys should be written
1024 * 2) collect usage information for keys/ids/dirids, create schema
1026 * 3) use usage information to create mapping tables, so that often
1027 * used ids get a lower number
1028 * 4) encode data into buffers using the mapping tables
1029 * 5) write everything to disk
1032 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1034 Pool *pool = repo->pool;
1035 int i, j, n, lastfilelistn;
1038 int nstrings, nrels;
1039 unsigned int sizeid;
1040 unsigned int solv_flags;
1049 unsigned char *repodataused;
1050 int anyrepodataused = 0;
1051 int anysolvableused = 0;
1053 struct cbdata cbdata;
1056 int poolusage, dirpoolusage, idused, dirused;
1059 Repodata *data, *dirpooldata;
1070 Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1073 memset(&cbdata, 0, sizeof(cbdata));
1075 cbdata.target = ⌖
1077 repodata_initdata(&target, repo, 1);
1079 /* go through all repodata and find the keys we need */
1080 /* also unify keys */
1081 /* keymapstart - maps repo number to keymap offset */
1082 /* keymap - maps repo key to my key, 0 -> not used */
1084 /* start with all KEY_STORAGE_SOLVABLE ids */
1086 n = ID_NUM_INTERNAL;
1087 FOR_REPODATAS(repo, i, data)
1089 cbdata.keymap = solv_calloc(n, sizeof(Id));
1090 cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1091 repodataused = solv_calloc(repo->nrepodata, 1);
1096 /* add keys for STORAGE_SOLVABLE */
1097 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1101 if (i < SOLVABLE_PROVIDES)
1102 keyd.type = REPOKEY_TYPE_ID;
1103 else if (i < RPM_RPMDBID)
1104 #ifdef USE_REL_IDARRAY
1105 keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1107 keyd.type = REPOKEY_TYPE_IDARRAY;
1110 keyd.type = REPOKEY_TYPE_NUM;
1112 keyd.storage = KEY_STORAGE_SOLVABLE;
1115 keyd.storage = keyfilter(repo, &keyd, kfdata);
1116 if (keyd.storage == KEY_STORAGE_DROPPED)
1118 keyd.storage = KEY_STORAGE_SOLVABLE;
1122 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1125 if (repo->nsolvables)
1128 keyd.name = REPOSITORY_SOLVABLES;
1129 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1131 keyd.storage = KEY_STORAGE_INCORE;
1132 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1140 n = ID_NUM_INTERNAL;
1142 FOR_REPODATAS(repo, i, data)
1144 cbdata.keymapstart[i] = n;
1145 cbdata.keymap[n++] = 0; /* key 0 */
1151 /* check if we want this repodata */
1152 memset(&keyd, 0, sizeof(keyd));
1156 if (keyfilter(repo, &keyd, kfdata) == -1)
1159 for (j = 1; j < data->nkeys; j++, n++)
1161 key = data->keys + j;
1162 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1164 cbdata.keymap[n] = cbdata.keymap[key->name];
1167 if (key->type == REPOKEY_TYPE_DELETED)
1169 cbdata.keymap[n] = 0;
1172 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1174 Repokey keyd = *key;
1175 keyd.size = repodata_globalize_id(data, key->size, 1);
1176 id = repodata_key2id(&target, &keyd, 0);
1179 id = repodata_key2id(&target, key, 0);
1182 Repokey keyd = *key;
1183 keyd.storage = KEY_STORAGE_INCORE;
1184 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1185 keyd.size = repodata_globalize_id(data, key->size, 1);
1186 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1190 keyd.storage = keyfilter(repo, &keyd, kfdata);
1191 if (keyd.storage == KEY_STORAGE_DROPPED)
1193 cbdata.keymap[n] = 0;
1197 id = repodata_key2id(&target, &keyd, 1);
1199 cbdata.keymap[n] = id;
1200 /* load repodata if not already loaded */
1201 if (data->state == REPODATA_STUB)
1203 if (data->loadcallback)
1204 data->loadcallback(data);
1206 data->state = REPODATA_ERROR;
1207 if (data->state != REPODATA_ERROR)
1209 /* redo this repodata! */
1211 n = cbdata.keymapstart[i];
1215 if (data->state == REPODATA_ERROR)
1218 cbdata.keymap[n] = 0;
1222 repodataused[i] = 1;
1223 anyrepodataused = 1;
1224 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1225 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1227 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1229 idused = 1; /* dirs also use ids */
1232 if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
1234 /* is this a file list extension */
1235 if (is_filelist_extension(data))
1237 /* hmm, we have a file list extension. Kill filelist of other repodata.
1238 * XXX: this is wrong, as the extension does not need to cover all
1239 * solvables of the other repodata */
1241 cbdata.keymap[lastfilelistn] = 0;
1249 if (data->localpool)
1252 poolusage = 3; /* need own pool */
1256 spool = &data->spool;
1263 else if (poolusage != 1)
1264 poolusage = 3; /* need own pool */
1270 dirpoolusage = 3; /* need own dirpool */
1274 dirpool = &data->dirpool;
1281 /* 0: no pool needed at all */
1282 /* 1: use global pool */
1283 /* 2: use repodata local pool */
1284 /* 3: need own pool */
1287 spool = &target.spool;
1288 /* hack: reuse global pool data so we don't have to map pool ids */
1291 stringpool_free(spool);
1292 stringpool_clone(spool, &pool->ss);
1294 cbdata.ownspool = spool;
1296 else if (poolusage == 0 || poolusage == 1)
1302 if (dirpoolusage == 3)
1304 dirpool = &target.dirpool;
1306 cbdata.owndirpool = dirpool;
1309 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1312 /********************************************************************/
1314 fprintf(stderr, "poolusage: %d\n", poolusage);
1315 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1316 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1317 for (i = 1; i < target.nkeys; i++)
1318 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);
1321 /* copy keys if requested */
1325 for (i = 1; i < target.nkeys; i++)
1326 queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1331 /* put all the keys we need in our string pool */
1332 /* put mapped ids right into target.keys */
1333 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1335 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1336 if (key->type == REPOKEY_TYPE_CONSTANTID)
1338 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1339 type_constantid = key->type;
1340 key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1343 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1346 stringpool_freehash(spool); /* free some mem */
1350 /********************************************************************/
1352 /* set needed count of all strings and rels,
1353 * find which keys are used in the solvables
1354 * put all strings in own spool
1357 reloff = spool->nstrings;
1359 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1361 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1362 needid[0].map = reloff;
1364 cbdata.needid = needid;
1365 cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1366 cbdata.sp = cbdata.schema;
1367 cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1369 /* create main schema */
1370 cbdata.sp = cbdata.schema;
1371 /* collect all other data from all repodatas */
1372 /* XXX: merge arrays of equal keys? */
1373 FOR_REPODATAS(repo, j, data)
1375 if (!repodataused[j])
1377 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1380 /* add solvables if needed (may revert later) */
1381 if (repo->nsolvables)
1383 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1384 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1387 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1389 idarraydata = repo->idarraydata;
1391 anysolvableused = 0;
1392 cbdata.doingsolvables = 1;
1393 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1395 if (s->repo != repo)
1398 /* set schema info, keep in sync with further down */
1400 if (cbdata.keymap[SOLVABLE_NAME])
1402 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1403 needid[s->name].need++;
1405 if (cbdata.keymap[SOLVABLE_ARCH])
1407 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1408 needid[s->arch].need++;
1410 if (cbdata.keymap[SOLVABLE_EVR])
1412 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1413 needid[s->evr].need++;
1415 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1417 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1418 needid[s->vendor].need++;
1420 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1422 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1423 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1425 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1427 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1428 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1430 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1432 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1433 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1435 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1437 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1438 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1440 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1442 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1443 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1445 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1447 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1448 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1450 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1452 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1453 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1455 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1457 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1458 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1460 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1462 *sp++ = cbdata.keymap[RPM_RPMDBID];
1463 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1467 if (anyrepodataused)
1469 FOR_REPODATAS(repo, j, data)
1471 if (!repodataused[j])
1473 if (i < data->start || i >= data->end)
1475 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1476 needid = cbdata.needid;
1480 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1481 if (cbdata.solvschemata[n])
1482 anysolvableused = 1;
1485 cbdata.doingsolvables = 0;
1486 assert(n == repo->nsolvables);
1488 if (repo->nsolvables && !anysolvableused)
1490 /* strip off solvable from the main schema */
1491 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1493 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1495 *sp = target.schemadata[target.schemata[mainschema] + i];
1496 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1499 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1501 target.schemadatalen = target.schemata[mainschema];
1503 repodata_free_schemahash(&target);
1504 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1507 /********************************************************************/
1509 /* remove unused keys */
1510 keyused = solv_calloc(target.nkeys, sizeof(Id));
1511 for (i = 1; i < (int)target.schemadatalen; i++)
1512 keyused[target.schemadata[i]] = 1;
1514 for (n = i = 1; i < target.nkeys; i++)
1521 target.keys[n] = target.keys[i];
1524 keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1525 keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1532 queue_truncate(keyq, 2 * n - 2);
1534 /* update schema data to the new key ids */
1535 for (i = 1; i < (int)target.schemadatalen; i++)
1536 target.schemadata[i] = keyused[target.schemadata[i]];
1537 /* update keymap to the new key ids */
1538 for (i = 0; i < cbdata.nkeymap; i++)
1539 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1540 keyused = solv_free(keyused);
1542 /* increment needid of the used keys, they are already mapped to
1543 * the correct string pool */
1544 for (i = 1; i < target.nkeys; i++)
1546 if (target.keys[i].type == type_constantid)
1547 needid[target.keys[i].size].need++;
1548 needid[target.keys[i].name].need++;
1549 needid[target.keys[i].type].need++;
1552 /********************************************************************/
1554 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1556 /* no dirs used at all */
1557 cbdata.dirused = solv_free(cbdata.dirused);
1561 /* increment need id for used dir components */
1564 /* if we have own dirpool, all entries in it are used.
1565 also, all comp ids are already mapped by putinowndirpool(),
1566 so we can simply increment needid.
1567 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1568 /* else we re-use a dirpool of repodata "dirpooldata".
1569 dirused tells us which of the ids are used.
1570 we need to map comp ids if we generate a new pool.
1571 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1572 for (i = 1; i < dirpool->ndirs; i++)
1575 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1577 if (cbdata.dirused && !cbdata.dirused[i])
1579 id = dirpool->dirs[i];
1582 if (dirpooldata && cbdata.ownspool && id > 1)
1584 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1585 needid = cbdata.needid;
1592 /********************************************************************/
1595 * create mapping table, new keys are sorted by needid[].need
1597 * needid[key].need : old key -> new key
1598 * needid[key].map : new key -> old key
1601 /* zero out id 0 and rel 0 just in case */
1602 reloff = needid[0].map;
1604 needid[reloff].need = 0;
1606 for (i = 1; i < reloff + pool->nrels; i++)
1610 solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1612 /* make first entry '' */
1614 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1616 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1617 /* now needid is in new order, needid[newid].map -> oldid */
1619 /* calculate string space size, also zero out needid[].need */
1621 for (i = 1; i < reloff; i++)
1623 if (!needid[i].need)
1624 break; /* as we have sorted, every entry after this also has need == 0 */
1626 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1628 nstrings = i; /* our new string id end */
1630 /* make needid[oldid].need point to newid */
1631 for (i = 1; i < nstrings; i++)
1632 needid[needid[i].map].need = i;
1634 /* same as above for relations */
1635 for (i = 0; i < pool->nrels; i++)
1637 if (!needid[reloff + i].need)
1639 needid[reloff + i].need = 0;
1641 nrels = i; /* our new rel id end */
1643 for (i = 0; i < nrels; i++)
1644 needid[needid[reloff + i].map].need = nstrings + i;
1646 /* now we have: needid[oldid].need -> newid
1647 needid[newid].map -> oldid
1648 both for strings and relations */
1651 /********************************************************************/
1657 /* create our new target directory structure by traversing through all
1658 * used dirs. This will concatenate blocks with the same parent
1659 * directory into single blocks.
1660 * Instead of components, traverse_dirs stores the old dirids,
1661 * we will change this in the second step below */
1662 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1663 if (cbdata.dirused && !cbdata.dirused[1])
1664 cbdata.dirused[1] = 1; /* always want / entry */
1665 dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1667 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1669 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1670 /* change dirmap so that it maps from "new dirid" to "new compid" */
1671 if (!cbdata.dirused)
1672 cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1673 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1674 for (i = 1; i < ndirmap; i++)
1678 cbdata.dirused[dirmap[i]] = i;
1679 id = dirpool->dirs[dirmap[i]];
1680 if (dirpooldata && cbdata.ownspool && id > 1)
1681 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1682 dirmap[i] = needid[id].need;
1684 /* now the new target directory structure is complete (dirmap), and we have
1685 * dirused[olddirid] -> newdirid */
1688 /********************************************************************/
1691 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1694 cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1696 xd = cbdata.extdata;
1697 cbdata.current_sub = 0;
1698 /* add main schema */
1700 data_addid(xd, mainschema);
1703 FOR_REPODATAS(repo, j, data)
1705 if (!repodataused[j])
1707 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1711 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1712 cbdata.maxdata = xd->len - cbdata.lastlen;
1713 cbdata.lastlen = xd->len;
1715 if (anysolvableused)
1717 data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1718 cbdata.doingsolvables = 1;
1720 /* check if we can do the special filelist memory optimization */
1721 if (anyrepodataused)
1723 for (i = 1; i < target.nkeys; i++)
1724 if (target.keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
1725 cbdata.filelistmode |= cbdata.filelistmode == 0 && target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY ? 1 : 2;
1726 else if (target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY)
1727 cbdata.filelistmode = 2;
1728 if (cbdata.filelistmode != 1)
1729 cbdata.filelistmode = 0;
1732 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1734 if (s->repo != repo)
1736 data_addid(xd, cbdata.solvschemata[n]);
1737 if (cbdata.keymap[SOLVABLE_NAME])
1738 data_addid(xd, needid[s->name].need);
1739 if (cbdata.keymap[SOLVABLE_ARCH])
1740 data_addid(xd, needid[s->arch].need);
1741 if (cbdata.keymap[SOLVABLE_EVR])
1742 data_addid(xd, needid[s->evr].need);
1743 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1744 data_addid(xd, needid[s->vendor].need);
1745 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1746 data_adddepids(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1747 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1748 data_adddepids(xd, pool, needid, idarraydata + s->obsoletes, 0);
1749 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1750 data_adddepids(xd, pool, needid, idarraydata + s->conflicts, 0);
1751 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1752 data_adddepids(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1753 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1754 data_adddepids(xd, pool, needid, idarraydata + s->recommends, 0);
1755 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1756 data_adddepids(xd, pool, needid, idarraydata + s->suggests, 0);
1757 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1758 data_adddepids(xd, pool, needid, idarraydata + s->supplements, 0);
1759 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1760 data_adddepids(xd, pool, needid, idarraydata + s->enhances, 0);
1761 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1762 data_addid(xd, repo->rpmdbid[i - repo->start]);
1763 if (anyrepodataused)
1766 FOR_REPODATAS(repo, j, data)
1768 if (!repodataused[j])
1770 if (i < data->start || i >= data->end)
1772 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1775 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1776 cbdata.maxdata = xd->len - cbdata.lastlen;
1777 cbdata.lastlen = xd->len;
1780 cbdata.doingsolvables = 0;
1783 assert(cbdata.current_sub == cbdata.nsubschemata);
1784 if (cbdata.subschemata)
1786 cbdata.subschemata = solv_free(cbdata.subschemata);
1787 cbdata.nsubschemata = 0;
1790 /********************************************************************/
1796 /* write file header */
1797 write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1798 write_u32(&target, SOLV_VERSION_8);
1802 write_u32(&target, nstrings);
1803 write_u32(&target, nrels);
1804 write_u32(&target, ndirmap);
1805 write_u32(&target, anysolvableused ? repo->nsolvables : 0);
1806 write_u32(&target, target.nkeys);
1807 write_u32(&target, target.nschemata);
1809 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1810 solv_flags |= SOLV_FLAG_SIZE_BYTES;
1811 write_u32(&target, solv_flags);
1816 * calculate prefix encoding of the strings
1818 unsigned char *prefixcomp = solv_malloc(nstrings);
1819 unsigned int compsum = 0;
1823 for (i = 1; i < nstrings; i++)
1825 char *str = spool->stringspace + spool->strings[needid[i].map];
1827 for (same = 0; same < 255; same++)
1828 if (!old_str[same] || old_str[same] != str[same])
1830 prefixcomp[i] = same;
1838 write_u32(&target, sizeid);
1839 /* we save compsum bytes but need 1 extra byte for every string */
1840 write_u32(&target, sizeid + nstrings - 1 - compsum);
1841 for (i = 1; i < nstrings; i++)
1843 char *str = spool->stringspace + spool->strings[needid[i].map];
1844 write_u8(&target, prefixcomp[i]);
1845 write_str(&target, str + prefixcomp[i]);
1847 solv_free(prefixcomp);
1851 write_u32(&target, 0);
1852 write_u32(&target, 0);
1858 for (i = 0; i < nrels; i++)
1860 ran = pool->rels + (needid[reloff + i].map - reloff);
1861 write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1862 write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1863 write_u8(&target, ran->flags);
1867 * write dirs (skip both root and / entry)
1869 for (i = 2; i < ndirmap; i++)
1872 write_id(&target, dirmap[i]);
1874 write_id(&target, nstrings - dirmap[i]);
1881 for (i = 1; i < target.nkeys; i++)
1883 write_id(&target, needid[target.keys[i].name].need);
1884 write_id(&target, needid[target.keys[i].type].need);
1885 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1887 if (target.keys[i].type == type_constantid)
1888 write_id(&target, needid[target.keys[i].size].need);
1890 write_id(&target, target.keys[i].size);
1893 write_id(&target, cbdata.extdata[i].len);
1894 write_id(&target, target.keys[i].storage);
1900 write_id(&target, target.schemadatalen); /* XXX -1? */
1901 for (i = 1; i < target.nschemata; i++)
1902 write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
1904 /********************************************************************/
1906 write_id(&target, cbdata.maxdata);
1907 write_id(&target, cbdata.extdata[0].len);
1908 if (cbdata.extdata[0].len)
1909 write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1910 solv_free(cbdata.extdata[0].buf);
1912 /* do we have vertical data? */
1913 for (i = 1; i < target.nkeys; i++)
1914 if (cbdata.extdata[i].len)
1916 if (i < target.nkeys)
1918 /* yes, write it in pages */
1919 unsigned char vpage[REPOPAGE_BLOBSIZE];
1922 write_u32(&target, REPOPAGE_BLOBSIZE);
1923 for (i = 1; i < target.nkeys; i++)
1924 if (cbdata.extdata[i].len)
1926 if (cbdata.filelistmode)
1928 lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
1930 if (cbdata.filelistmode && i < target.nkeys)
1932 /* ok, just this single extdata, which is a filelist */
1933 xd = cbdata.extdata + i;
1935 cbdata.filelistmode = -1;
1936 for (j = 0; j < cbdata.nkeymap; j++)
1937 if (cbdata.keymap[j] != i)
1938 cbdata.keymap[j] = 0;
1939 for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
1941 if (s->repo != repo)
1943 FOR_REPODATAS(repo, j, data)
1945 if (!repodataused[j])
1947 if (i < data->start || i >= data->end)
1949 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1951 if (xd->len > 1024 * 1024)
1953 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
1958 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
1961 write_compressed_page(&target, vpage, lpage);
1964 for (i = 1; i < target.nkeys; i++)
1965 solv_free(cbdata.extdata[i].buf);
1966 solv_free(cbdata.extdata);
1969 repodata_freedata(&target);
1972 solv_free(cbdata.solvschemata);
1973 solv_free(cbdata.schema);
1975 solv_free(cbdata.keymap);
1976 solv_free(cbdata.keymapstart);
1977 solv_free(cbdata.dirused);
1978 solv_free(repodataused);
1979 return target.error;
1982 struct repodata_write_data {
1983 int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1989 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1991 struct repodata_write_data *wd = kfdata;
1993 /* XXX: special repodata selection hack */
1994 if (key->name == 1 && key->size != wd->repodataid)
1996 if (key->storage == KEY_STORAGE_SOLVABLE)
1997 return KEY_STORAGE_DROPPED; /* not part of this repodata */
1999 return (*wd->keyfilter)(repo, key, wd->kfdata);
2000 return key->storage;
2004 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
2006 struct repodata_write_data wd;
2008 wd.keyfilter = keyfilter;
2010 wd.repodataid = data->repodataid;
2011 return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
2015 repodata_write(Repodata *data, FILE *fp)
2017 return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
2021 repo_write(Repo *repo, FILE *fp)
2023 return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);