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);
264 cmp_ids(const void *pa, const void *pb, void *dp)
273 write_idarray_sort(Repodata *data, Pool *pool, NeedId *needid, Id *ids, Id marker)
285 for (len = 0; len < 64 && ids[len]; len++)
289 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
294 for (i = len + 1; ids[i]; i++)
296 sids = solv_malloc2(i, sizeof(Id));
297 memcpy(sids, lids, 64 * sizeof(Id));
298 for (; ids[len]; len++)
302 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
309 /* That bloody solvable:prereqmarker needs to stay in position :-( */
311 marker = needid[marker].need;
312 for (i = 0; i < len; i++)
313 if (sids[i] == marker)
316 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
318 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
322 /* The differencing above produces many runs of ones and twos. I tried
323 fairly elaborate schemes to RLE those, but they give only very mediocre
324 improvements in compression, as coding the escapes costs quite some
325 space. Even if they are coded only as bits in IDs. The best improvement
326 was about 2.7% for the whole .solv file. It's probably better to
327 invest some complexity into sharing idarrays, than RLEing. */
328 for (i = 0; i < len - 1; i++)
331 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
332 hence all real differences are offsetted by 1. Otherwise we would
333 have to handle negative differences, which would cost code space for
334 the encoding of the sign. We loose the exact mapping of prereq here,
335 but we know the result, so we can recover from that in the reader. */
343 /* XXX If difference is zero we have multiple equal elements,
344 we might want to skip writing them out. */
346 id = (id & 63) | ((id & ~63) << 1);
347 write_id(data, id | 64);
355 id = (id & 63) | ((id & ~63) << 1);
372 Stringpool *ownspool;
381 Id *schema; /* schema construction space */
382 Id *sp; /* pointer in above */
383 Id *oldschema, *oldsp;
390 struct extdata *extdata;
399 int doingsolvables; /* working on solvables data */
402 #define NEEDED_BLOCK 1023
403 #define SCHEMATA_BLOCK 31
404 #define SCHEMATADATA_BLOCK 255
405 #define EXTDATA_BLOCK 4095
408 data_addid(struct extdata *xd, Id sx)
410 unsigned int x = (unsigned int)sx;
413 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
414 dp = xd->buf + xd->len;
419 *dp++ = (x >> 28) | 128;
421 *dp++ = (x >> 21) | 128;
422 *dp++ = (x >> 14) | 128;
425 *dp++ = (x >> 7) | 128;
427 xd->len = dp - xd->buf;
431 data_addideof(struct extdata *xd, Id sx, int eof)
433 unsigned int x = (unsigned int)sx;
436 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
437 dp = xd->buf + xd->len;
442 *dp++ = (x >> 27) | 128;
444 *dp++ = (x >> 20) | 128;
445 *dp++ = (x >> 13) | 128;
448 *dp++ = (x >> 6) | 128;
449 *dp++ = eof ? (x & 63) : (x & 63) | 64;
450 xd->len = dp - xd->buf;
454 data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
460 data_addid(xd, (Id)(hx >> 3));
461 xd->buf[xd->len - 1] |= 128;
464 data_addid(xd, (Id)(x | 0x80000000));
465 xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
468 data_addid(xd, (Id)x);
472 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
485 for (len = 0; len < 64 && ids[len]; len++)
489 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
494 for (i = len + 1; ids[i]; i++)
496 sids = solv_malloc2(i, sizeof(Id));
497 memcpy(sids, lids, 64 * sizeof(Id));
498 for (; ids[len]; len++)
502 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
509 /* That bloody solvable:prereqmarker needs to stay in position :-( */
511 marker = needid[marker].need;
512 for (i = 0; i < len; i++)
513 if (sids[i] == marker)
516 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
518 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
522 /* The differencing above produces many runs of ones and twos. I tried
523 fairly elaborate schemes to RLE those, but they give only very mediocre
524 improvements in compression, as coding the escapes costs quite some
525 space. Even if they are coded only as bits in IDs. The best improvement
526 was about 2.7% for the whole .solv file. It's probably better to
527 invest some complexity into sharing idarrays, than RLEing. */
528 for (i = 0; i < len - 1; i++)
531 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
532 hence all real differences are offsetted by 1. Otherwise we would
533 have to handle negative differences, which would cost code space for
534 the encoding of the sign. We loose the exact mapping of prereq here,
535 but we know the result, so we can recover from that in the reader. */
543 /* XXX If difference is zero we have multiple equal elements,
544 we might want to skip writing them out. */
545 data_addideof(xd, id, 0);
552 data_addideof(xd, id, 1);
558 data_addblob(struct extdata *xd, unsigned char *blob, int len)
560 xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
561 memcpy(xd->buf + xd->len, blob, len);
566 data_addu32(struct extdata *xd, unsigned int num)
573 data_addblob(xd, d, 4);
577 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
579 const char *str = stringpool_id2str(ss, id);
580 id = stringpool_str2id(cbdata->ownspool, str, 1);
581 if (id >= cbdata->needid[0].map)
583 int oldoff = cbdata->needid[0].map;
584 int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
585 int nrels = cbdata->repo->pool->nrels;
586 cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
588 memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
589 memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
590 cbdata->needid[0].map = newoff;
596 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
600 parent = dirpool_parent(dp, dir);
602 parent = putinowndirpool(cbdata, data, dp, parent);
603 compid = dp->dirs[dir];
604 if (cbdata->ownspool && compid > 1)
605 compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
606 return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
610 * collect usage information about the dirs
611 * 1: dir used, no child of dir used
612 * 2: dir used as parent of another used dir
615 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
617 if (cbdata->dirused[dir])
619 cbdata->dirused[dir] = 1;
620 while ((dir = dirpool_parent(dp, dir)) != 0)
622 if (cbdata->dirused[dir] == 2)
624 if (cbdata->dirused[dir])
626 cbdata->dirused[dir] = 2;
629 cbdata->dirused[dir] = 2;
631 cbdata->dirused[0] = 2;
636 * collect key/id/dirid usage information, create needed schemas
639 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
644 if (key->name == REPOSITORY_SOLVABLES)
645 return SEARCH_NEXT_KEY; /* we do not want this one */
647 /* hack: ignore some keys, see BUGS */
648 if (data->repodataid != data->repo->nrepodata - 1)
649 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
650 return SEARCH_NEXT_KEY;
652 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
654 return SEARCH_NEXT_KEY; /* we do not want this one */
656 /* record key in schema */
657 if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
658 && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
663 case REPOKEY_TYPE_ID:
664 case REPOKEY_TYPE_IDARRAY:
666 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
667 id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
668 incneedid(repo->pool, id, cbdata->needid);
670 case REPOKEY_TYPE_DIR:
671 case REPOKEY_TYPE_DIRNUMNUMARRAY:
672 case REPOKEY_TYPE_DIRSTRARRAY:
674 if (cbdata->owndirpool)
675 putinowndirpool(cbdata, data, &data->dirpool, id);
677 setdirused(cbdata, &data->dirpool, id);
679 case REPOKEY_TYPE_FIXARRAY:
682 if (cbdata->oldschema)
684 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
685 return SEARCH_NEXT_KEY;
687 cbdata->oldschema = cbdata->schema;
688 cbdata->oldsp = cbdata->sp;
689 cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
690 cbdata->sp = cbdata->schema;
692 else if (kv->eof == 1)
694 cbdata->current_sub++;
696 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
697 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
699 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
701 cbdata->sp = cbdata->schema;
705 solv_free(cbdata->schema);
706 cbdata->schema = cbdata->oldschema;
707 cbdata->sp = cbdata->oldsp;
708 cbdata->oldsp = cbdata->oldschema = 0;
711 case REPOKEY_TYPE_FLEXARRAY:
715 *cbdata->sp++ = 0; /* mark start */
719 /* just finished a schema, rewind */
720 Id *sp = cbdata->sp - 1;
724 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
725 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
726 cbdata->sp = kv->eof == 2 ? sp - 1: sp;
736 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
738 struct cbdata *cbdata = vcbdata;
739 Repo *repo = data->repo;
743 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);
745 return repo_write_collect_needed(cbdata, repo, data, key, kv);
751 * encode all of the data into the correct buffers
755 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
764 if (key->name == REPOSITORY_SOLVABLES)
765 return SEARCH_NEXT_KEY;
767 /* hack: ignore some keys, see BUGS */
768 if (data->repodataid != data->repo->nrepodata - 1)
769 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
770 return SEARCH_NEXT_KEY;
772 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
774 return SEARCH_NEXT_KEY; /* we do not want this one */
776 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
778 xd = cbdata->extdata + rm; /* vertical buffer */
779 if (cbdata->vstart == -1)
780 cbdata->vstart = xd->len;
783 xd = cbdata->extdata + 0; /* incore buffer */
786 case REPOKEY_TYPE_VOID:
787 case REPOKEY_TYPE_CONSTANT:
788 case REPOKEY_TYPE_CONSTANTID:
790 case REPOKEY_TYPE_ID:
792 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
793 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
794 needid = cbdata->needid;
795 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
798 case REPOKEY_TYPE_IDARRAY:
800 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
801 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
802 needid = cbdata->needid;
803 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
804 data_addideof(xd, id, kv->eof);
806 case REPOKEY_TYPE_STR:
807 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
809 case REPOKEY_TYPE_MD5:
810 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
812 case REPOKEY_TYPE_SHA1:
813 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
815 case REPOKEY_TYPE_SHA224:
816 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
818 case REPOKEY_TYPE_SHA256:
819 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
821 case REPOKEY_TYPE_SHA384:
822 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
824 case REPOKEY_TYPE_SHA512:
825 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
827 case REPOKEY_TYPE_U32:
833 data_addblob(xd, v, 4);
835 case REPOKEY_TYPE_NUM:
836 data_addid64(xd, kv->num, kv->num2);
838 case REPOKEY_TYPE_DIR:
840 if (cbdata->owndirpool)
841 id = putinowndirpool(cbdata, data, &data->dirpool, id);
842 id = cbdata->dirused[id];
845 case REPOKEY_TYPE_BINARY:
846 data_addid(xd, kv->num);
848 data_addblob(xd, (unsigned char *)kv->str, kv->num);
850 case REPOKEY_TYPE_DIRNUMNUMARRAY:
852 if (cbdata->owndirpool)
853 id = putinowndirpool(cbdata, data, &data->dirpool, id);
854 id = cbdata->dirused[id];
856 data_addid(xd, kv->num);
857 data_addideof(xd, kv->num2, kv->eof);
859 case REPOKEY_TYPE_DIRSTRARRAY:
861 if (cbdata->owndirpool)
862 id = putinowndirpool(cbdata, data, &data->dirpool, id);
863 id = cbdata->dirused[id];
864 data_addideof(xd, id, kv->eof);
865 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
867 case REPOKEY_TYPE_FIXARRAY:
872 data_addid(xd, kv->num);
873 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
875 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
879 else if (kv->eof == 1)
881 cbdata->current_sub++;
884 case REPOKEY_TYPE_FLEXARRAY:
886 data_addid(xd, kv->num);
888 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
889 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
891 if (xd->len - cbdata->lastlen > cbdata->maxdata)
892 cbdata->maxdata = xd->len - cbdata->lastlen;
893 cbdata->lastlen = xd->len;
897 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
900 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
902 /* we can re-use old data in the blob here! */
903 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
904 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
911 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
913 struct cbdata *cbdata = vcbdata;
914 return repo_write_adddata(cbdata, data, key, kv);
917 /* traverse through directory with first child "dir" */
919 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
925 /* special case for '/', which has to come first */
928 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
930 if (used && !used[sib])
932 if (sib == 1 && parent == 1)
933 continue; /* already did that one above */
937 /* now go through all the siblings we just added and
938 * do recursive calls on them */
940 for (; parent < lastn; parent++)
942 sib = dirmap[parent];
943 if (used && used[sib] != 2) /* 2: used as parent */
945 child = dirpool_child(dp, sib);
948 dirmap[n++] = -parent; /* start new block */
949 n = traverse_dirs(dp, dirmap, n, child, used);
956 write_compressed_page(Repodata *data, unsigned char *page, int len)
959 unsigned char cpage[REPOPAGE_BLOBSIZE];
961 clen = repopagestore_compress_page(page, len, cpage, len - 1);
964 write_u32(data, len * 2);
965 write_blob(data, page, len);
969 write_u32(data, clen * 2 + 1);
970 write_blob(data, cpage, clen);
974 static Id verticals[] = {
976 SOLVABLE_DESCRIPTION,
988 SOLVABLE_CHANGELOG_AUTHOR,
989 SOLVABLE_CHANGELOG_TEXT,
993 static char *languagetags[] = {
995 "solvable:description:",
996 "solvable:messageins:",
997 "solvable:messagedel:",
1003 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
1005 const char *keyname;
1008 for (i = 0; verticals[i]; i++)
1009 if (key->name == verticals[i])
1010 return KEY_STORAGE_VERTICAL_OFFSET;
1011 keyname = pool_id2str(repo->pool, key->name);
1012 for (i = 0; languagetags[i] != 0; i++)
1013 if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
1014 return KEY_STORAGE_VERTICAL_OFFSET;
1015 return KEY_STORAGE_INCORE;
1019 * return true if the repodata contains the filelist (and just
1020 * the filelist). The same code is used in the dataiterator. The way
1021 * it is used is completely wrong, of course, as having the filelist
1022 * key does not mean it is used for a specific solvable. Nevertheless
1023 * it is better to have it than to write broken solv files.
1026 is_filelist_extension(Repodata *data)
1029 for (j = 1; j < data->nkeys; j++)
1030 if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1040 * the code works the following way:
1042 * 1) find which keys should be written
1043 * 2) collect usage information for keys/ids/dirids, create schema
1045 * 3) use usage information to create mapping tables, so that often
1046 * used ids get a lower number
1047 * 4) encode data into buffers using the mapping tables
1048 * 5) write everything to disk
1051 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1053 Pool *pool = repo->pool;
1054 int i, j, n, lastfilelistn;
1057 int nstrings, nrels;
1058 unsigned int sizeid;
1059 unsigned int solv_flags;
1068 unsigned char *repodataused;
1069 int anyrepodataused = 0;
1070 int anysolvableused = 0;
1072 struct cbdata cbdata;
1075 int poolusage, dirpoolusage, idused, dirused;
1078 Repodata *data, *dirpooldata;
1089 Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1092 memset(&cbdata, 0, sizeof(cbdata));
1094 cbdata.target = ⌖
1096 repodata_initdata(&target, repo, 1);
1098 /* go through all repodata and find the keys we need */
1099 /* also unify keys */
1100 /* keymapstart - maps repo number to keymap offset */
1101 /* keymap - maps repo key to my key, 0 -> not used */
1103 /* start with all KEY_STORAGE_SOLVABLE ids */
1105 n = ID_NUM_INTERNAL;
1106 FOR_REPODATAS(repo, i, data)
1108 cbdata.keymap = solv_calloc(n, sizeof(Id));
1109 cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1110 repodataused = solv_calloc(repo->nrepodata, 1);
1115 /* add keys for STORAGE_SOLVABLE */
1116 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1120 if (i < SOLVABLE_PROVIDES)
1121 keyd.type = REPOKEY_TYPE_ID;
1122 else if (i < RPM_RPMDBID)
1123 keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1125 keyd.type = REPOKEY_TYPE_NUM;
1127 keyd.storage = KEY_STORAGE_SOLVABLE;
1130 keyd.storage = keyfilter(repo, &keyd, kfdata);
1131 if (keyd.storage == KEY_STORAGE_DROPPED)
1133 keyd.storage = KEY_STORAGE_SOLVABLE;
1137 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1140 if (repo->nsolvables)
1143 keyd.name = REPOSITORY_SOLVABLES;
1144 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1146 keyd.storage = KEY_STORAGE_INCORE;
1147 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1155 n = ID_NUM_INTERNAL;
1157 FOR_REPODATAS(repo, i, data)
1159 cbdata.keymapstart[i] = n;
1160 cbdata.keymap[n++] = 0; /* key 0 */
1166 /* check if we want this repodata */
1167 memset(&keyd, 0, sizeof(keyd));
1171 if (keyfilter(repo, &keyd, kfdata) == -1)
1174 for (j = 1; j < data->nkeys; j++, n++)
1176 key = data->keys + j;
1177 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1179 cbdata.keymap[n] = cbdata.keymap[key->name];
1182 if (key->type == REPOKEY_TYPE_DELETED)
1184 cbdata.keymap[n] = 0;
1187 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1189 Repokey keyd = *key;
1190 keyd.size = repodata_globalize_id(data, key->size, 1);
1191 id = repodata_key2id(&target, &keyd, 0);
1194 id = repodata_key2id(&target, key, 0);
1197 Repokey keyd = *key;
1198 keyd.storage = KEY_STORAGE_INCORE;
1199 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1200 keyd.size = repodata_globalize_id(data, key->size, 1);
1201 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1205 keyd.storage = keyfilter(repo, &keyd, kfdata);
1206 if (keyd.storage == KEY_STORAGE_DROPPED)
1208 cbdata.keymap[n] = 0;
1212 id = repodata_key2id(&target, &keyd, 1);
1214 cbdata.keymap[n] = id;
1215 /* load repodata if not already loaded */
1216 if (data->state == REPODATA_STUB)
1218 if (data->loadcallback)
1219 data->loadcallback(data);
1221 data->state = REPODATA_ERROR;
1222 if (data->state != REPODATA_ERROR)
1224 /* redo this repodata! */
1226 n = cbdata.keymapstart[i];
1230 if (data->state == REPODATA_ERROR)
1233 cbdata.keymap[n] = 0;
1237 repodataused[i] = 1;
1238 anyrepodataused = 1;
1239 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1240 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1242 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1244 idused = 1; /* dirs also use ids */
1247 if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
1249 /* is this a file list extension */
1250 if (is_filelist_extension(data))
1252 /* hmm, we have a file list extension. Kill filelist of other repodata.
1253 * XXX: this is wrong, as the extension does not need to cover all
1254 * solvables of the other repodata */
1256 cbdata.keymap[lastfilelistn] = 0;
1264 if (data->localpool)
1267 poolusage = 3; /* need own pool */
1271 spool = &data->spool;
1278 else if (poolusage != 1)
1279 poolusage = 3; /* need own pool */
1285 dirpoolusage = 3; /* need own dirpool */
1289 dirpool = &data->dirpool;
1296 /* 0: no pool needed at all */
1297 /* 1: use global pool */
1298 /* 2: use repodata local pool */
1299 /* 3: need own pool */
1302 spool = &target.spool;
1303 /* hack: reuse global pool data so we don't have to map pool ids */
1306 stringpool_free(spool);
1307 stringpool_clone(spool, &pool->ss);
1309 cbdata.ownspool = spool;
1311 else if (poolusage == 0 || poolusage == 1)
1317 if (dirpoolusage == 3)
1319 dirpool = &target.dirpool;
1321 cbdata.owndirpool = dirpool;
1324 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1327 /********************************************************************/
1329 fprintf(stderr, "poolusage: %d\n", poolusage);
1330 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1331 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1332 for (i = 1; i < target.nkeys; i++)
1333 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);
1336 /* copy keys if requested */
1340 for (i = 1; i < target.nkeys; i++)
1341 queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1346 /* put all the keys we need in our string pool */
1347 /* put mapped ids right into target.keys */
1348 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1350 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1351 if (key->type == REPOKEY_TYPE_CONSTANTID)
1353 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1354 type_constantid = key->type;
1355 key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1358 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1361 stringpool_freehash(spool); /* free some mem */
1365 /********************************************************************/
1367 /* set needed count of all strings and rels,
1368 * find which keys are used in the solvables
1369 * put all strings in own spool
1372 reloff = spool->nstrings;
1374 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1376 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1377 needid[0].map = reloff;
1379 cbdata.needid = needid;
1380 cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1381 cbdata.sp = cbdata.schema;
1382 cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1384 /* create main schema */
1385 cbdata.sp = cbdata.schema;
1386 /* collect all other data from all repodatas */
1387 /* XXX: merge arrays of equal keys? */
1388 FOR_REPODATAS(repo, j, data)
1390 if (!repodataused[j])
1392 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1395 /* add solvables if needed (may revert later) */
1396 if (repo->nsolvables)
1398 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1399 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1402 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1404 idarraydata = repo->idarraydata;
1406 anysolvableused = 0;
1407 cbdata.doingsolvables = 1;
1408 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1410 if (s->repo != repo)
1413 /* set schema info, keep in sync with further down */
1415 if (cbdata.keymap[SOLVABLE_NAME])
1417 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1418 needid[s->name].need++;
1420 if (cbdata.keymap[SOLVABLE_ARCH])
1422 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1423 needid[s->arch].need++;
1425 if (cbdata.keymap[SOLVABLE_EVR])
1427 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1428 needid[s->evr].need++;
1430 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1432 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1433 needid[s->vendor].need++;
1435 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1437 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1438 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1440 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1442 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1443 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1445 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1447 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1448 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1450 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1452 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1453 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1455 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1457 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1458 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1460 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1462 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1463 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1465 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1467 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1468 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1470 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1472 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1473 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1475 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1477 *sp++ = cbdata.keymap[RPM_RPMDBID];
1478 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1482 if (anyrepodataused)
1484 FOR_REPODATAS(repo, j, data)
1486 if (!repodataused[j])
1488 if (i < data->start || i >= data->end)
1490 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1491 needid = cbdata.needid;
1495 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1496 if (cbdata.solvschemata[n])
1497 anysolvableused = 1;
1500 cbdata.doingsolvables = 0;
1501 assert(n == repo->nsolvables);
1503 if (repo->nsolvables && !anysolvableused)
1505 /* strip off solvable from the main schema */
1506 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1508 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1510 *sp = target.schemadata[target.schemata[mainschema] + i];
1511 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1514 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1516 target.schemadatalen = target.schemata[mainschema];
1518 repodata_free_schemahash(&target);
1519 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1522 /********************************************************************/
1524 /* remove unused keys */
1525 keyused = solv_calloc(target.nkeys, sizeof(Id));
1526 for (i = 1; i < target.schemadatalen; i++)
1527 keyused[target.schemadata[i]] = 1;
1529 for (n = i = 1; i < target.nkeys; i++)
1536 target.keys[n] = target.keys[i];
1539 keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1540 keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1547 queue_truncate(keyq, 2 * n - 2);
1549 /* update schema data to the new key ids */
1550 for (i = 1; i < target.schemadatalen; i++)
1551 target.schemadata[i] = keyused[target.schemadata[i]];
1552 /* update keymap to the new key ids */
1553 for (i = 0; i < cbdata.nkeymap; i++)
1554 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1555 keyused = solv_free(keyused);
1557 /* increment needid of the used keys, they are already mapped to
1558 * the correct string pool */
1559 for (i = 1; i < target.nkeys; i++)
1561 if (target.keys[i].type == type_constantid)
1562 needid[target.keys[i].size].need++;
1563 needid[target.keys[i].name].need++;
1564 needid[target.keys[i].type].need++;
1567 /********************************************************************/
1569 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1571 /* no dirs used at all */
1572 cbdata.dirused = solv_free(cbdata.dirused);
1576 /* increment need id for used dir components */
1579 /* if we have own dirpool, all entries in it are used.
1580 also, all comp ids are already mapped by putinowndirpool(),
1581 so we can simply increment needid.
1582 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1583 /* else we re-use a dirpool of repodata "dirpooldata".
1584 dirused tells us which of the ids are used.
1585 we need to map comp ids if we generate a new pool.
1586 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1587 for (i = 1; i < dirpool->ndirs; i++)
1590 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1592 if (cbdata.dirused && !cbdata.dirused[i])
1594 id = dirpool->dirs[i];
1597 if (dirpooldata && cbdata.ownspool && id > 1)
1599 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1600 needid = cbdata.needid;
1607 /********************************************************************/
1610 * create mapping table, new keys are sorted by needid[].need
1612 * needid[key].need : old key -> new key
1613 * needid[key].map : new key -> old key
1616 /* zero out id 0 and rel 0 just in case */
1617 reloff = needid[0].map;
1619 needid[reloff].need = 0;
1621 for (i = 1; i < reloff + pool->nrels; i++)
1625 solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1627 /* make first entry '' */
1629 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1631 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1632 /* now needid is in new order, needid[newid].map -> oldid */
1634 /* calculate string space size, also zero out needid[].need */
1636 for (i = 1; i < reloff; i++)
1638 if (!needid[i].need)
1639 break; /* as we have sorted, every entry after this also has need == 0 */
1641 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1643 nstrings = i; /* our new string id end */
1645 /* make needid[oldid].need point to newid */
1646 for (i = 1; i < nstrings; i++)
1647 needid[needid[i].map].need = i;
1649 /* same as above for relations */
1650 for (i = 0; i < pool->nrels; i++)
1652 if (!needid[reloff + i].need)
1654 needid[reloff + i].need = 0;
1656 nrels = i; /* our new rel id end */
1658 for (i = 0; i < nrels; i++)
1659 needid[needid[reloff + i].map].need = nstrings + i;
1661 /* now we have: needid[oldid].need -> newid
1662 needid[newid].map -> oldid
1663 both for strings and relations */
1666 /********************************************************************/
1672 /* create our new target directory structure by traversing through all
1673 * used dirs. This will concatenate blocks with the same parent
1674 * directory into single blocks.
1675 * Instead of components, traverse_dirs stores the old dirids,
1676 * we will change this in the second step below */
1677 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1678 if (cbdata.dirused && !cbdata.dirused[1])
1679 cbdata.dirused[1] = 1; /* always want / entry */
1680 dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1682 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1684 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1685 /* change dirmap so that it maps from "new dirid" to "new compid" */
1686 if (!cbdata.dirused)
1687 cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1688 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1689 for (i = 1; i < ndirmap; i++)
1693 cbdata.dirused[dirmap[i]] = i;
1694 id = dirpool->dirs[dirmap[i]];
1695 if (dirpooldata && cbdata.ownspool && id > 1)
1696 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1697 dirmap[i] = needid[id].need;
1699 /* now the new target directory structure is complete (dirmap), and we have
1700 * dirused[olddirid] -> newdirid */
1703 /********************************************************************/
1706 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1709 cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1711 xd = cbdata.extdata;
1712 cbdata.current_sub = 0;
1713 /* add main schema */
1715 data_addid(xd, mainschema);
1718 FOR_REPODATAS(repo, j, data)
1720 if (!repodataused[j])
1722 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1726 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1727 cbdata.maxdata = xd->len - cbdata.lastlen;
1728 cbdata.lastlen = xd->len;
1730 if (anysolvableused)
1732 data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1733 cbdata.doingsolvables = 1;
1734 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1736 if (s->repo != repo)
1738 data_addid(xd, cbdata.solvschemata[n]);
1739 if (cbdata.keymap[SOLVABLE_NAME])
1740 data_addid(xd, needid[s->name].need);
1741 if (cbdata.keymap[SOLVABLE_ARCH])
1742 data_addid(xd, needid[s->arch].need);
1743 if (cbdata.keymap[SOLVABLE_EVR])
1744 data_addid(xd, needid[s->evr].need);
1745 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1746 data_addid(xd, needid[s->vendor].need);
1747 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1748 data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1749 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1750 data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1751 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1752 data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1753 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1754 data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1755 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1756 data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1757 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1758 data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1759 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1760 data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1761 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1762 data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1763 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1764 data_addid(xd, repo->rpmdbid[i - repo->start]);
1765 if (anyrepodataused)
1768 FOR_REPODATAS(repo, j, data)
1770 if (!repodataused[j])
1772 if (i < data->start || i >= data->end)
1774 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1777 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1778 cbdata.maxdata = xd->len - cbdata.lastlen;
1779 cbdata.lastlen = xd->len;
1782 cbdata.doingsolvables = 0;
1785 assert(cbdata.current_sub == cbdata.nsubschemata);
1786 if (cbdata.subschemata)
1788 cbdata.subschemata = solv_free(cbdata.subschemata);
1789 cbdata.nsubschemata = 0;
1792 /********************************************************************/
1798 /* write file header */
1799 write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1800 write_u32(&target, SOLV_VERSION_8);
1804 write_u32(&target, nstrings);
1805 write_u32(&target, nrels);
1806 write_u32(&target, ndirmap);
1807 write_u32(&target, anysolvableused ? repo->nsolvables : 0);
1808 write_u32(&target, target.nkeys);
1809 write_u32(&target, target.nschemata);
1811 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1812 solv_flags |= SOLV_FLAG_SIZE_BYTES;
1813 write_u32(&target, solv_flags);
1818 * calculate prefix encoding of the strings
1820 unsigned char *prefixcomp = solv_malloc(nstrings);
1821 unsigned int compsum = 0;
1825 for (i = 1; i < nstrings; i++)
1827 char *str = spool->stringspace + spool->strings[needid[i].map];
1829 for (same = 0; same < 255; same++)
1830 if (!old_str[same] || old_str[same] != str[same])
1832 prefixcomp[i] = same;
1840 write_u32(&target, sizeid);
1841 /* we save compsum bytes but need 1 extra byte for every string */
1842 write_u32(&target, sizeid + nstrings - 1 - compsum);
1843 for (i = 1; i < nstrings; i++)
1845 char *str = spool->stringspace + spool->strings[needid[i].map];
1846 write_u8(&target, prefixcomp[i]);
1847 write_str(&target, str + prefixcomp[i]);
1849 solv_free(prefixcomp);
1853 write_u32(&target, 0);
1854 write_u32(&target, 0);
1860 for (i = 0; i < nrels; i++)
1862 ran = pool->rels + (needid[reloff + i].map - reloff);
1863 write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1864 write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1865 write_u8(&target, ran->flags);
1869 * write dirs (skip both root and / entry)
1871 for (i = 2; i < ndirmap; i++)
1874 write_id(&target, dirmap[i]);
1876 write_id(&target, nstrings - dirmap[i]);
1883 for (i = 1; i < target.nkeys; i++)
1885 write_id(&target, needid[target.keys[i].name].need);
1886 write_id(&target, needid[target.keys[i].type].need);
1887 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1889 if (target.keys[i].type == type_constantid)
1890 write_id(&target, needid[target.keys[i].size].need);
1892 write_id(&target, target.keys[i].size);
1895 write_id(&target, cbdata.extdata[i].len);
1896 write_id(&target, target.keys[i].storage);
1902 write_id(&target, target.schemadatalen); /* XXX -1? */
1903 for (i = 1; i < target.nschemata; i++)
1904 write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
1906 /********************************************************************/
1908 write_id(&target, cbdata.maxdata);
1909 write_id(&target, cbdata.extdata[0].len);
1910 if (cbdata.extdata[0].len)
1911 write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1912 solv_free(cbdata.extdata[0].buf);
1914 /* do we have vertical data? */
1915 for (i = 1; i < target.nkeys; i++)
1916 if (cbdata.extdata[i].len)
1918 if (i < target.nkeys)
1920 /* yes, write it in pages */
1921 unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
1922 int l, ll, lpage = 0;
1924 write_u32(&target, REPOPAGE_BLOBSIZE);
1925 for (i = 1; i < target.nkeys; i++)
1927 if (!cbdata.extdata[i].len)
1929 l = cbdata.extdata[i].len;
1930 dp = cbdata.extdata[i].buf;
1933 ll = REPOPAGE_BLOBSIZE - lpage;
1936 memcpy(vpage + lpage, dp, ll);
1940 if (lpage == REPOPAGE_BLOBSIZE)
1942 write_compressed_page(&target, vpage, lpage);
1948 write_compressed_page(&target, vpage, lpage);
1951 for (i = 1; i < target.nkeys; i++)
1952 solv_free(cbdata.extdata[i].buf);
1953 solv_free(cbdata.extdata);
1956 repodata_freedata(&target);
1959 solv_free(cbdata.solvschemata);
1960 solv_free(cbdata.schema);
1962 solv_free(cbdata.keymap);
1963 solv_free(cbdata.keymapstart);
1964 solv_free(cbdata.dirused);
1965 solv_free(repodataused);
1966 return target.error;
1969 struct repodata_write_data {
1970 int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1976 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1978 struct repodata_write_data *wd = kfdata;
1980 /* XXX: special repodata selection hack */
1981 if (key->name == 1 && key->size != wd->repodataid)
1983 if (key->storage == KEY_STORAGE_SOLVABLE)
1984 return KEY_STORAGE_DROPPED; /* not part of this repodata */
1986 return (*wd->keyfilter)(repo, key, wd->kfdata);
1987 return key->storage;
1991 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1993 struct repodata_write_data wd;
1995 wd.keyfilter = keyfilter;
1997 wd.repodataid = data->repodataid;
1998 return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
2002 repodata_write(Repodata *data, FILE *fp)
2004 return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
2008 repo_write(Repo *repo, FILE *fp)
2010 return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);