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_SHA256:
816 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
818 case REPOKEY_TYPE_U32:
824 data_addblob(xd, v, 4);
826 case REPOKEY_TYPE_NUM:
827 data_addid64(xd, kv->num, kv->num2);
829 case REPOKEY_TYPE_DIR:
831 if (cbdata->owndirpool)
832 id = putinowndirpool(cbdata, data, &data->dirpool, id);
833 id = cbdata->dirused[id];
836 case REPOKEY_TYPE_BINARY:
837 data_addid(xd, kv->num);
839 data_addblob(xd, (unsigned char *)kv->str, kv->num);
841 case REPOKEY_TYPE_DIRNUMNUMARRAY:
843 if (cbdata->owndirpool)
844 id = putinowndirpool(cbdata, data, &data->dirpool, id);
845 id = cbdata->dirused[id];
847 data_addid(xd, kv->num);
848 data_addideof(xd, kv->num2, kv->eof);
850 case REPOKEY_TYPE_DIRSTRARRAY:
852 if (cbdata->owndirpool)
853 id = putinowndirpool(cbdata, data, &data->dirpool, id);
854 id = cbdata->dirused[id];
855 data_addideof(xd, id, kv->eof);
856 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
858 case REPOKEY_TYPE_FIXARRAY:
863 data_addid(xd, kv->num);
864 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
866 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
870 else if (kv->eof == 1)
872 cbdata->current_sub++;
875 case REPOKEY_TYPE_FLEXARRAY:
877 data_addid(xd, kv->num);
879 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
880 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
882 if (xd->len - cbdata->lastlen > cbdata->maxdata)
883 cbdata->maxdata = xd->len - cbdata->lastlen;
884 cbdata->lastlen = xd->len;
888 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
891 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
893 /* we can re-use old data in the blob here! */
894 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
895 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
902 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
904 struct cbdata *cbdata = vcbdata;
905 return repo_write_adddata(cbdata, data, key, kv);
908 /* traverse through directory with first child "dir" */
910 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
916 /* special case for '/', which has to come first */
919 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
921 if (used && !used[sib])
923 if (sib == 1 && parent == 1)
924 continue; /* already did that one above */
928 /* now go through all the siblings we just added and
929 * do recursive calls on them */
931 for (; parent < lastn; parent++)
933 sib = dirmap[parent];
934 if (used && used[sib] != 2) /* 2: used as parent */
936 child = dirpool_child(dp, sib);
939 dirmap[n++] = -parent; /* start new block */
940 n = traverse_dirs(dp, dirmap, n, child, used);
947 write_compressed_page(Repodata *data, unsigned char *page, int len)
950 unsigned char cpage[REPOPAGE_BLOBSIZE];
952 clen = repopagestore_compress_page(page, len, cpage, len - 1);
955 write_u32(data, len * 2);
956 write_blob(data, page, len);
960 write_u32(data, clen * 2 + 1);
961 write_blob(data, cpage, clen);
965 static Id verticals[] = {
967 SOLVABLE_DESCRIPTION,
982 static char *languagetags[] = {
984 "solvable:description:",
985 "solvable:messageins:",
986 "solvable:messagedel:",
992 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
997 for (i = 0; verticals[i]; i++)
998 if (key->name == verticals[i])
999 return KEY_STORAGE_VERTICAL_OFFSET;
1000 keyname = pool_id2str(repo->pool, key->name);
1001 for (i = 0; languagetags[i] != 0; i++)
1002 if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
1003 return KEY_STORAGE_VERTICAL_OFFSET;
1004 return KEY_STORAGE_INCORE;
1012 * the code works the following way:
1014 * 1) find which keys should be written
1015 * 2) collect usage information for keys/ids/dirids, create schema
1017 * 3) use usage information to create mapping tables, so that often
1018 * used ids get a lower number
1019 * 4) encode data into buffers using the mapping tables
1020 * 5) write everything to disk
1023 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1025 Pool *pool = repo->pool;
1029 int nstrings, nrels;
1030 unsigned int sizeid;
1031 unsigned int solv_flags;
1040 unsigned char *repodataused;
1041 int anyrepodataused = 0;
1042 int anysolvableused = 0;
1044 struct cbdata cbdata;
1047 int poolusage, dirpoolusage, idused, dirused;
1050 Repodata *data, *dirpooldata;
1061 Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1063 unsigned char *prefixcomp;
1064 unsigned int compsum;
1068 memset(&cbdata, 0, sizeof(cbdata));
1070 cbdata.target = ⌖
1072 repodata_initdata(&target, repo, 1);
1074 /* go through all repodata and find the keys we need */
1075 /* also unify keys */
1076 /* keymapstart - maps repo number to keymap offset */
1077 /* keymap - maps repo key to my key, 0 -> not used */
1079 /* start with all KEY_STORAGE_SOLVABLE ids */
1081 n = ID_NUM_INTERNAL;
1082 FOR_REPODATAS(repo, i, data)
1084 cbdata.keymap = solv_calloc(n, sizeof(Id));
1085 cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1086 repodataused = solv_calloc(repo->nrepodata, 1);
1091 /* add keys for STORAGE_SOLVABLE */
1092 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1096 if (i < SOLVABLE_PROVIDES)
1097 keyd.type = REPOKEY_TYPE_ID;
1098 else if (i < RPM_RPMDBID)
1099 keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1101 keyd.type = REPOKEY_TYPE_NUM;
1103 keyd.storage = KEY_STORAGE_SOLVABLE;
1106 keyd.storage = keyfilter(repo, &keyd, kfdata);
1107 if (keyd.storage == KEY_STORAGE_DROPPED)
1109 keyd.storage = KEY_STORAGE_SOLVABLE;
1113 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1116 if (repo->nsolvables)
1119 keyd.name = REPOSITORY_SOLVABLES;
1120 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1122 keyd.storage = KEY_STORAGE_INCORE;
1123 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1131 n = ID_NUM_INTERNAL;
1132 FOR_REPODATAS(repo, i, data)
1134 cbdata.keymapstart[i] = n;
1135 cbdata.keymap[n++] = 0; /* key 0 */
1141 /* check if we want this repodata */
1142 memset(&keyd, 0, sizeof(keyd));
1146 if (keyfilter(repo, &keyd, kfdata) == -1)
1149 for (j = 1; j < data->nkeys; j++, n++)
1151 key = data->keys + j;
1152 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1154 cbdata.keymap[n] = cbdata.keymap[key->name];
1157 if (key->type == REPOKEY_TYPE_DELETED)
1159 cbdata.keymap[n] = 0;
1162 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1164 Repokey keyd = *key;
1165 keyd.size = repodata_globalize_id(data, key->size, 1);
1166 id = repodata_key2id(&target, &keyd, 0);
1169 id = repodata_key2id(&target, key, 0);
1172 Repokey keyd = *key;
1173 keyd.storage = KEY_STORAGE_INCORE;
1174 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1175 keyd.size = repodata_globalize_id(data, key->size, 1);
1176 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1180 keyd.storage = keyfilter(repo, &keyd, kfdata);
1181 if (keyd.storage == KEY_STORAGE_DROPPED)
1183 cbdata.keymap[n] = 0;
1187 id = repodata_key2id(&target, &keyd, 1);
1189 cbdata.keymap[n] = id;
1190 /* load repodata if not already loaded */
1191 if (data->state == REPODATA_STUB)
1193 if (data->loadcallback)
1194 data->loadcallback(data);
1196 data->state = REPODATA_ERROR;
1197 if (data->state != REPODATA_ERROR)
1199 /* redo this repodata! */
1201 n = cbdata.keymapstart[i];
1205 if (data->state == REPODATA_ERROR)
1208 cbdata.keymap[n] = 0;
1212 repodataused[i] = 1;
1213 anyrepodataused = 1;
1214 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1215 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1217 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1219 idused = 1; /* dirs also use ids */
1225 if (data->localpool)
1228 poolusage = 3; /* need own pool */
1232 spool = &data->spool;
1239 else if (poolusage != 1)
1240 poolusage = 3; /* need own pool */
1246 dirpoolusage = 3; /* need own dirpool */
1250 dirpool = &data->dirpool;
1257 /* 0: no pool needed at all */
1258 /* 1: use global pool */
1259 /* 2: use repodata local pool */
1260 /* 3: need own pool */
1263 spool = &target.spool;
1264 /* hack: reuse global pool data so we don't have to map pool ids */
1267 stringpool_free(spool);
1268 stringpool_clone(spool, &pool->ss);
1270 cbdata.ownspool = spool;
1272 else if (poolusage == 0 || poolusage == 1)
1278 if (dirpoolusage == 3)
1280 dirpool = &target.dirpool;
1282 cbdata.owndirpool = dirpool;
1285 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1288 /********************************************************************/
1290 fprintf(stderr, "poolusage: %d\n", poolusage);
1291 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1292 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1293 for (i = 1; i < target.nkeys; i++)
1294 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);
1297 /* copy keys if requested */
1301 for (i = 1; i < target.nkeys; i++)
1302 queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1307 /* put all the keys we need in our string pool */
1308 /* put mapped ids right into target.keys */
1309 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1311 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1312 if (key->type == REPOKEY_TYPE_CONSTANTID)
1314 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1315 type_constantid = key->type;
1316 key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1319 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1322 stringpool_freehash(spool); /* free some mem */
1326 /********************************************************************/
1328 /* set needed count of all strings and rels,
1329 * find which keys are used in the solvables
1330 * put all strings in own spool
1333 reloff = spool->nstrings;
1335 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1337 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1338 needid[0].map = reloff;
1340 cbdata.needid = needid;
1341 cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1342 cbdata.sp = cbdata.schema;
1343 cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1345 /* create main schema */
1346 cbdata.sp = cbdata.schema;
1347 /* collect all other data from all repodatas */
1348 /* XXX: merge arrays of equal keys? */
1349 FOR_REPODATAS(repo, j, data)
1351 if (!repodataused[j])
1353 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1356 /* add solvables if needed (may revert later) */
1357 if (repo->nsolvables)
1359 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1360 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1363 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1365 idarraydata = repo->idarraydata;
1367 anysolvableused = 0;
1368 cbdata.doingsolvables = 1;
1369 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1371 if (s->repo != repo)
1374 /* set schema info, keep in sync with further down */
1376 if (cbdata.keymap[SOLVABLE_NAME])
1378 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1379 needid[s->name].need++;
1381 if (cbdata.keymap[SOLVABLE_ARCH])
1383 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1384 needid[s->arch].need++;
1386 if (cbdata.keymap[SOLVABLE_EVR])
1388 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1389 needid[s->evr].need++;
1391 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1393 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1394 needid[s->vendor].need++;
1396 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1398 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1399 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1401 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1403 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1404 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1406 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1408 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1409 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1411 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1413 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1414 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1416 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1418 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1419 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1421 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1423 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1424 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1426 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1428 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1429 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1431 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1433 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1434 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1436 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1438 *sp++ = cbdata.keymap[RPM_RPMDBID];
1439 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1443 if (anyrepodataused)
1445 FOR_REPODATAS(repo, j, data)
1447 if (!repodataused[j])
1449 if (i < data->start || i >= data->end)
1451 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1452 needid = cbdata.needid;
1456 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1457 if (cbdata.solvschemata[n])
1458 anysolvableused = 1;
1461 cbdata.doingsolvables = 0;
1462 assert(n == repo->nsolvables);
1464 if (repo->nsolvables && !anysolvableused)
1466 /* strip off solvable from the main schema */
1467 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1469 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1471 *sp = target.schemadata[target.schemata[mainschema] + i];
1472 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1475 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1477 target.schemadatalen = target.schemata[mainschema];
1479 repodata_free_schemahash(&target);
1480 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1483 /********************************************************************/
1485 /* remove unused keys */
1486 keyused = solv_calloc(target.nkeys, sizeof(Id));
1487 for (i = 1; i < target.schemadatalen; i++)
1488 keyused[target.schemadata[i]] = 1;
1490 for (n = i = 1; i < target.nkeys; i++)
1497 target.keys[n] = target.keys[i];
1500 keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1501 keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1508 queue_truncate(keyq, 2 * n - 2);
1510 /* update schema data to the new key ids */
1511 for (i = 1; i < target.schemadatalen; i++)
1512 target.schemadata[i] = keyused[target.schemadata[i]];
1513 /* update keymap to the new key ids */
1514 for (i = 0; i < cbdata.nkeymap; i++)
1515 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1516 keyused = solv_free(keyused);
1518 /* increment needid of the used keys, they are already mapped to
1519 * the correct string pool */
1520 for (i = 1; i < target.nkeys; i++)
1522 if (target.keys[i].type == type_constantid)
1523 needid[target.keys[i].size].need++;
1524 needid[target.keys[i].name].need++;
1525 needid[target.keys[i].type].need++;
1528 /********************************************************************/
1530 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1532 /* no dirs used at all */
1533 cbdata.dirused = solv_free(cbdata.dirused);
1537 /* increment need id for used dir components */
1540 /* if we have own dirpool, all entries in it are used.
1541 also, all comp ids are already mapped by putinowndirpool(),
1542 so we can simply increment needid.
1543 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1544 /* else we re-use a dirpool of repodata "dirpooldata".
1545 dirused tells us which of the ids are used.
1546 we need to map comp ids if we generate a new pool.
1547 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1548 for (i = 1; i < dirpool->ndirs; i++)
1551 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1553 if (cbdata.dirused && !cbdata.dirused[i])
1555 id = dirpool->dirs[i];
1558 if (dirpooldata && cbdata.ownspool && id > 1)
1560 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1561 needid = cbdata.needid;
1568 /********************************************************************/
1571 * create mapping table, new keys are sorted by needid[].need
1573 * needid[key].need : old key -> new key
1574 * needid[key].map : new key -> old key
1577 /* zero out id 0 and rel 0 just in case */
1578 reloff = needid[0].map;
1580 needid[reloff].need = 0;
1582 for (i = 1; i < reloff + pool->nrels; i++)
1586 solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1588 /* make first entry '' */
1590 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1592 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1593 /* now needid is in new order, needid[newid].map -> oldid */
1595 /* calculate string space size, also zero out needid[].need */
1597 for (i = 1; i < reloff; i++)
1599 if (!needid[i].need)
1600 break; /* as we have sorted, every entry after this also has need == 0 */
1602 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1604 nstrings = i; /* our new string id end */
1606 /* make needid[oldid].need point to newid */
1607 for (i = 1; i < nstrings; i++)
1608 needid[needid[i].map].need = i;
1610 /* same as above for relations */
1611 for (i = 0; i < pool->nrels; i++)
1613 if (!needid[reloff + i].need)
1615 needid[reloff + i].need = 0;
1617 nrels = i; /* our new rel id end */
1619 for (i = 0; i < nrels; i++)
1620 needid[needid[reloff + i].map].need = nstrings + i;
1622 /* now we have: needid[oldid].need -> newid
1623 needid[newid].map -> oldid
1624 both for strings and relations */
1627 /********************************************************************/
1633 /* create our new target directory structure by traversing through all
1634 * used dirs. This will concatenate blocks with the same parent
1635 * directory into single blocks.
1636 * Instead of components, traverse_dirs stores the old dirids,
1637 * we will change this in the second step below */
1638 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1639 if (cbdata.dirused && !cbdata.dirused[1])
1640 cbdata.dirused[1] = 1; /* always want / entry */
1641 dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1643 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1645 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1646 /* change dirmap so that it maps from "new dirid" to "new compid" */
1647 if (!cbdata.dirused)
1648 cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1649 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1650 for (i = 1; i < ndirmap; i++)
1654 cbdata.dirused[dirmap[i]] = i;
1655 id = dirpool->dirs[dirmap[i]];
1656 if (dirpooldata && cbdata.ownspool && id > 1)
1657 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1658 dirmap[i] = needid[id].need;
1660 /* now the new target directory structure is complete (dirmap), and we have
1661 * dirused[olddirid] -> newdirid */
1664 /********************************************************************/
1667 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1670 cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1672 xd = cbdata.extdata;
1673 cbdata.current_sub = 0;
1674 /* add main schema */
1676 data_addid(xd, mainschema);
1679 FOR_REPODATAS(repo, j, data)
1681 if (!repodataused[j])
1683 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1687 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1688 cbdata.maxdata = xd->len - cbdata.lastlen;
1689 cbdata.lastlen = xd->len;
1691 if (anysolvableused)
1693 data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1694 cbdata.doingsolvables = 1;
1695 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1697 if (s->repo != repo)
1699 data_addid(xd, cbdata.solvschemata[n]);
1700 if (cbdata.keymap[SOLVABLE_NAME])
1701 data_addid(xd, needid[s->name].need);
1702 if (cbdata.keymap[SOLVABLE_ARCH])
1703 data_addid(xd, needid[s->arch].need);
1704 if (cbdata.keymap[SOLVABLE_EVR])
1705 data_addid(xd, needid[s->evr].need);
1706 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1707 data_addid(xd, needid[s->vendor].need);
1708 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1709 data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1710 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1711 data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1712 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1713 data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1714 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1715 data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1716 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1717 data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1718 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1719 data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1720 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1721 data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1722 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1723 data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1724 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1725 data_addid(xd, repo->rpmdbid[i - repo->start]);
1726 if (anyrepodataused)
1729 FOR_REPODATAS(repo, j, data)
1731 if (!repodataused[j])
1733 if (i < data->start || i >= data->end)
1735 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1738 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1739 cbdata.maxdata = xd->len - cbdata.lastlen;
1740 cbdata.lastlen = xd->len;
1743 cbdata.doingsolvables = 0;
1746 assert(cbdata.current_sub == cbdata.nsubschemata);
1747 if (cbdata.subschemata)
1749 cbdata.subschemata = solv_free(cbdata.subschemata);
1750 cbdata.nsubschemata = 0;
1753 /********************************************************************/
1759 /* write file header */
1760 write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1761 write_u32(&target, SOLV_VERSION_8);
1765 write_u32(&target, nstrings);
1766 write_u32(&target, nrels);
1767 write_u32(&target, ndirmap);
1768 write_u32(&target, anysolvableused ? repo->nsolvables : 0);
1769 write_u32(&target, target.nkeys);
1770 write_u32(&target, target.nschemata);
1772 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1773 solv_flags |= SOLV_FLAG_SIZE_BYTES;
1774 write_u32(&target, solv_flags);
1777 * calculate prefix encoding of the strings
1779 prefixcomp = solv_malloc(nstrings);
1784 for (i = 1; i < nstrings; i++)
1786 char *str = spool->stringspace + spool->strings[needid[i].map];
1788 for (same = 0; same < 255; same++)
1789 if (!old_str[same] || old_str[same] != str[same])
1791 prefixcomp[i] = same;
1799 write_u32(&target, sizeid);
1800 /* we save compsum bytes but need 1 extra byte for every string */
1801 write_u32(&target, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1802 if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1804 for (i = 1; i < nstrings; i++)
1806 char *str = spool->stringspace + spool->strings[needid[i].map];
1807 write_u8(&target, prefixcomp[i]);
1808 write_str(&target, str + prefixcomp[i]);
1811 solv_free(prefixcomp);
1814 /* Build the prefix-encoding of the string pool. We need to know
1815 the size of that before writing it to the file, so we have to
1816 build a separate buffer for that. As it's temporarily possible
1817 that this actually is an expansion we can't easily reuse the
1818 stringspace for this. The max expansion per string is 1 byte,
1819 so it will fit into sizeid+nstrings bytes. */
1820 char *prefix = solv_malloc(sizeid + nstrings);
1823 for (i = 1; i < nstrings; i++)
1825 char *str = spool->stringspace + spool->strings[needid[i].map];
1828 for (same = 0; same < 255; same++)
1829 if (!old_str[same] || !str[same] || old_str[same] != str[same])
1832 len = strlen(str + same) + 1;
1833 memcpy(pp, str + same, len);
1841 write_u32(&target, sizeid);
1842 write_u32(&target, pp - prefix);
1843 write_blob(&target, prefix, pp - prefix);
1850 for (i = 0; i < nrels; i++)
1852 ran = pool->rels + (needid[reloff + i].map - reloff);
1853 write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1854 write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1855 write_u8(&target, ran->flags);
1859 * write dirs (skip both root and / entry)
1861 for (i = 2; i < ndirmap; i++)
1864 write_id(&target, dirmap[i]);
1866 write_id(&target, nstrings - dirmap[i]);
1873 for (i = 1; i < target.nkeys; i++)
1875 write_id(&target, needid[target.keys[i].name].need);
1876 write_id(&target, needid[target.keys[i].type].need);
1877 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1879 if (target.keys[i].type == type_constantid)
1880 write_id(&target, needid[target.keys[i].size].need);
1882 write_id(&target, target.keys[i].size);
1885 write_id(&target, cbdata.extdata[i].len);
1886 write_id(&target, target.keys[i].storage);
1892 write_id(&target, target.schemadatalen); /* XXX -1? */
1893 for (i = 1; i < target.nschemata; i++)
1894 write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
1896 /********************************************************************/
1898 write_id(&target, cbdata.maxdata);
1899 write_id(&target, cbdata.extdata[0].len);
1900 if (cbdata.extdata[0].len)
1901 write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1902 solv_free(cbdata.extdata[0].buf);
1904 /* do we have vertical data? */
1905 for (i = 1; i < target.nkeys; i++)
1906 if (cbdata.extdata[i].len)
1908 if (i < target.nkeys)
1910 /* yes, write it in pages */
1911 unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
1912 int l, ll, lpage = 0;
1914 write_u32(&target, REPOPAGE_BLOBSIZE);
1915 for (i = 1; i < target.nkeys; i++)
1917 if (!cbdata.extdata[i].len)
1919 l = cbdata.extdata[i].len;
1920 dp = cbdata.extdata[i].buf;
1923 ll = REPOPAGE_BLOBSIZE - lpage;
1926 memcpy(vpage + lpage, dp, ll);
1930 if (lpage == REPOPAGE_BLOBSIZE)
1932 write_compressed_page(&target, vpage, lpage);
1938 write_compressed_page(&target, vpage, lpage);
1941 for (i = 1; i < target.nkeys; i++)
1942 solv_free(cbdata.extdata[i].buf);
1943 solv_free(cbdata.extdata);
1946 repodata_freedata(&target);
1949 solv_free(cbdata.solvschemata);
1950 solv_free(cbdata.schema);
1952 solv_free(cbdata.keymap);
1953 solv_free(cbdata.keymapstart);
1954 solv_free(cbdata.dirused);
1955 solv_free(repodataused);
1956 return target.error;
1959 struct repodata_write_data {
1960 int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
1966 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
1968 struct repodata_write_data *wd = kfdata;
1970 /* XXX: special repodata selection hack */
1971 if (key->name == 1 && key->size != wd->repodataid)
1973 if (key->storage == KEY_STORAGE_SOLVABLE)
1974 return KEY_STORAGE_DROPPED; /* not part of this repodata */
1976 return (*wd->keyfilter)(repo, key, wd->kfdata);
1977 return key->storage;
1981 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1983 struct repodata_write_data wd;
1985 wd.keyfilter = keyfilter;
1987 wd.repodataid = data->repodataid;
1988 return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
1992 repodata_write(Repodata *data, FILE *fp)
1994 return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
1998 repo_write(Repo *repo, FILE *fp)
2000 return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);