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 */
403 #define NEEDED_BLOCK 1023
404 #define SCHEMATA_BLOCK 31
405 #define SCHEMATADATA_BLOCK 255
406 #define EXTDATA_BLOCK 4095
409 data_addid(struct extdata *xd, Id sx)
411 unsigned int x = (unsigned int)sx;
414 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
415 dp = xd->buf + xd->len;
420 *dp++ = (x >> 28) | 128;
422 *dp++ = (x >> 21) | 128;
423 *dp++ = (x >> 14) | 128;
426 *dp++ = (x >> 7) | 128;
428 xd->len = dp - xd->buf;
432 data_addideof(struct extdata *xd, Id sx, int eof)
434 unsigned int x = (unsigned int)sx;
437 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
438 dp = xd->buf + xd->len;
443 *dp++ = (x >> 27) | 128;
445 *dp++ = (x >> 20) | 128;
446 *dp++ = (x >> 13) | 128;
449 *dp++ = (x >> 6) | 128;
450 *dp++ = eof ? (x & 63) : (x & 63) | 64;
451 xd->len = dp - xd->buf;
455 data_addideof_len(Id sx)
457 unsigned int x = (unsigned int)sx;
462 return x >= (1 << 20) ? 4 : 3;
464 return x >= (1 << 6) ? 2 : 1;
468 data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
474 data_addid(xd, (Id)(hx >> 3));
475 xd->buf[xd->len - 1] |= 128;
478 data_addid(xd, (Id)(x | 0x80000000));
479 xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
482 data_addid(xd, (Id)x);
486 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
499 for (len = 0; len < 64 && ids[len]; len++)
503 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
508 for (i = len + 1; ids[i]; i++)
510 sids = solv_malloc2(i, sizeof(Id));
511 memcpy(sids, lids, 64 * sizeof(Id));
512 for (; ids[len]; len++)
516 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
523 /* That bloody solvable:prereqmarker needs to stay in position :-( */
525 marker = needid[marker].need;
526 for (i = 0; i < len; i++)
527 if (sids[i] == marker)
530 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
532 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
536 /* The differencing above produces many runs of ones and twos. I tried
537 fairly elaborate schemes to RLE those, but they give only very mediocre
538 improvements in compression, as coding the escapes costs quite some
539 space. Even if they are coded only as bits in IDs. The best improvement
540 was about 2.7% for the whole .solv file. It's probably better to
541 invest some complexity into sharing idarrays, than RLEing. */
542 for (i = 0; i < len - 1; i++)
545 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
546 hence all real differences are offsetted by 1. Otherwise we would
547 have to handle negative differences, which would cost code space for
548 the encoding of the sign. We loose the exact mapping of prereq here,
549 but we know the result, so we can recover from that in the reader. */
557 /* XXX If difference is zero we have multiple equal elements,
558 we might want to skip writing them out. */
559 data_addideof(xd, id, 0);
566 data_addideof(xd, id, 1);
572 data_addblob(struct extdata *xd, unsigned char *blob, int len)
574 xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
575 memcpy(xd->buf + xd->len, blob, len);
580 data_addu32(struct extdata *xd, unsigned int num)
587 data_addblob(xd, d, 4);
591 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
593 const char *str = stringpool_id2str(ss, id);
594 id = stringpool_str2id(cbdata->ownspool, str, 1);
595 if (id >= cbdata->needid[0].map)
597 int oldoff = cbdata->needid[0].map;
598 int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
599 int nrels = cbdata->repo->pool->nrels;
600 cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
602 memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
603 memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
604 cbdata->needid[0].map = newoff;
610 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
614 parent = dirpool_parent(dp, dir);
616 parent = putinowndirpool(cbdata, data, dp, parent);
617 compid = dp->dirs[dir];
618 if (cbdata->ownspool && compid > 1)
619 compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
620 return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
624 * collect usage information about the dirs
625 * 1: dir used, no child of dir used
626 * 2: dir used as parent of another used dir
629 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
631 if (cbdata->dirused[dir])
633 cbdata->dirused[dir] = 1;
634 while ((dir = dirpool_parent(dp, dir)) != 0)
636 if (cbdata->dirused[dir] == 2)
638 if (cbdata->dirused[dir])
640 cbdata->dirused[dir] = 2;
643 cbdata->dirused[dir] = 2;
645 cbdata->dirused[0] = 2;
650 * collect key/id/dirid usage information, create needed schemas
653 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
658 if (key->name == REPOSITORY_SOLVABLES)
659 return SEARCH_NEXT_KEY; /* we do not want this one */
661 /* hack: ignore some keys, see BUGS */
662 if (data->repodataid != data->repo->nrepodata - 1)
663 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
664 return SEARCH_NEXT_KEY;
666 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
668 return SEARCH_NEXT_KEY; /* we do not want this one */
670 /* record key in schema */
671 if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
672 && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
677 case REPOKEY_TYPE_ID:
678 case REPOKEY_TYPE_IDARRAY:
680 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
681 id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
682 incneedid(repo->pool, id, cbdata->needid);
684 case REPOKEY_TYPE_DIR:
685 case REPOKEY_TYPE_DIRNUMNUMARRAY:
686 case REPOKEY_TYPE_DIRSTRARRAY:
688 if (cbdata->owndirpool)
689 putinowndirpool(cbdata, data, &data->dirpool, id);
691 setdirused(cbdata, &data->dirpool, id);
693 case REPOKEY_TYPE_FIXARRAY:
696 if (cbdata->oldschema)
698 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
699 return SEARCH_NEXT_KEY;
701 cbdata->oldschema = cbdata->schema;
702 cbdata->oldsp = cbdata->sp;
703 cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
704 cbdata->sp = cbdata->schema;
706 else if (kv->eof == 1)
708 cbdata->current_sub++;
710 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
711 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
713 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
715 cbdata->sp = cbdata->schema;
719 solv_free(cbdata->schema);
720 cbdata->schema = cbdata->oldschema;
721 cbdata->sp = cbdata->oldsp;
722 cbdata->oldsp = cbdata->oldschema = 0;
725 case REPOKEY_TYPE_FLEXARRAY:
729 *cbdata->sp++ = 0; /* mark start */
733 /* just finished a schema, rewind */
734 Id *sp = cbdata->sp - 1;
738 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
739 cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
740 cbdata->sp = kv->eof == 2 ? sp - 1: sp;
750 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
752 struct cbdata *cbdata = vcbdata;
753 Repo *repo = data->repo;
757 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);
759 return repo_write_collect_needed(cbdata, repo, data, key, kv);
765 * encode all of the data into the correct buffers
769 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
778 if (key->name == REPOSITORY_SOLVABLES)
779 return SEARCH_NEXT_KEY;
781 /* hack: ignore some keys, see BUGS */
782 if (data->repodataid != data->repo->nrepodata - 1)
783 if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
784 return SEARCH_NEXT_KEY;
786 rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
788 return SEARCH_NEXT_KEY; /* we do not want this one */
790 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
792 xd = cbdata->extdata + rm; /* vertical buffer */
793 if (cbdata->vstart == -1)
794 cbdata->vstart = xd->len;
797 xd = cbdata->extdata + 0; /* incore buffer */
800 case REPOKEY_TYPE_VOID:
801 case REPOKEY_TYPE_CONSTANT:
802 case REPOKEY_TYPE_CONSTANTID:
804 case REPOKEY_TYPE_ID:
806 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
807 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
808 needid = cbdata->needid;
809 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
812 case REPOKEY_TYPE_IDARRAY:
814 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
815 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
816 needid = cbdata->needid;
817 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
818 data_addideof(xd, id, kv->eof);
820 case REPOKEY_TYPE_STR:
821 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
823 case REPOKEY_TYPE_MD5:
824 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
826 case REPOKEY_TYPE_SHA1:
827 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
829 case REPOKEY_TYPE_SHA224:
830 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
832 case REPOKEY_TYPE_SHA256:
833 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
835 case REPOKEY_TYPE_SHA384:
836 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
838 case REPOKEY_TYPE_SHA512:
839 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
841 case REPOKEY_TYPE_U32:
847 data_addblob(xd, v, 4);
849 case REPOKEY_TYPE_NUM:
850 data_addid64(xd, kv->num, kv->num2);
852 case REPOKEY_TYPE_DIR:
854 if (cbdata->owndirpool)
855 id = putinowndirpool(cbdata, data, &data->dirpool, id);
856 id = cbdata->dirused[id];
859 case REPOKEY_TYPE_BINARY:
860 data_addid(xd, kv->num);
862 data_addblob(xd, (unsigned char *)kv->str, kv->num);
864 case REPOKEY_TYPE_DIRNUMNUMARRAY:
866 if (cbdata->owndirpool)
867 id = putinowndirpool(cbdata, data, &data->dirpool, id);
868 id = cbdata->dirused[id];
870 data_addid(xd, kv->num);
871 data_addideof(xd, kv->num2, kv->eof);
873 case REPOKEY_TYPE_DIRSTRARRAY:
875 if (cbdata->owndirpool)
876 id = putinowndirpool(cbdata, data, &data->dirpool, id);
877 id = cbdata->dirused[id];
878 if (cbdata->filelistmode > 0)
880 xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
883 data_addideof(xd, id, kv->eof);
884 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
885 if (cbdata->filelistmode < 0)
888 case REPOKEY_TYPE_FIXARRAY:
893 data_addid(xd, kv->num);
894 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
896 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
900 else if (kv->eof == 1)
902 cbdata->current_sub++;
905 case REPOKEY_TYPE_FLEXARRAY:
907 data_addid(xd, kv->num);
909 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
910 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
912 if (xd->len - cbdata->lastlen > cbdata->maxdata)
913 cbdata->maxdata = xd->len - cbdata->lastlen;
914 cbdata->lastlen = xd->len;
918 cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
921 if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
923 /* we can re-use old data in the blob here! */
924 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
925 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
932 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
934 struct cbdata *cbdata = vcbdata;
935 return repo_write_adddata(cbdata, data, key, kv);
938 /* traverse through directory with first child "dir" */
940 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
946 /* special case for '/', which has to come first */
949 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
951 if (used && !used[sib])
953 if (sib == 1 && parent == 1)
954 continue; /* already did that one above */
958 /* check if our block has some content */
960 return n - 1; /* nope, drop parent id again */
962 /* now go through all the siblings we just added and
963 * do recursive calls on them */
965 for (; parent < lastn; parent++)
967 sib = dirmap[parent];
968 if (used && used[sib] != 2) /* 2: used as parent */
970 child = dirpool_child(dp, sib);
973 dirmap[n++] = -parent; /* start new block */
974 n = traverse_dirs(dp, dirmap, n, child, used);
981 write_compressed_page(Repodata *data, unsigned char *page, int len)
984 unsigned char cpage[REPOPAGE_BLOBSIZE];
986 clen = repopagestore_compress_page(page, len, cpage, len - 1);
989 write_u32(data, len * 2);
990 write_blob(data, page, len);
994 write_u32(data, clen * 2 + 1);
995 write_blob(data, cpage, clen);
999 static Id verticals[] = {
1001 SOLVABLE_DESCRIPTION,
1002 SOLVABLE_MESSAGEDEL,
1003 SOLVABLE_MESSAGEINS,
1013 SOLVABLE_CHANGELOG_AUTHOR,
1014 SOLVABLE_CHANGELOG_TEXT,
1018 static char *languagetags[] = {
1019 "solvable:summary:",
1020 "solvable:description:",
1021 "solvable:messageins:",
1022 "solvable:messagedel:",
1028 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
1030 const char *keyname;
1033 for (i = 0; verticals[i]; i++)
1034 if (key->name == verticals[i])
1035 return KEY_STORAGE_VERTICAL_OFFSET;
1036 keyname = pool_id2str(repo->pool, key->name);
1037 for (i = 0; languagetags[i] != 0; i++)
1038 if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
1039 return KEY_STORAGE_VERTICAL_OFFSET;
1040 return KEY_STORAGE_INCORE;
1044 * return true if the repodata contains the filelist (and just
1045 * the filelist). The same code is used in the dataiterator. The way
1046 * it is used is completely wrong, of course, as having the filelist
1047 * key does not mean it is used for a specific solvable. Nevertheless
1048 * it is better to have it than to write broken solv files.
1051 is_filelist_extension(Repodata *data)
1054 for (j = 1; j < data->nkeys; j++)
1055 if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1062 write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
1064 unsigned char *dp = xd->buf;
1068 int ll = REPOPAGE_BLOBSIZE - lpage;
1071 memcpy(vpage + lpage, dp, ll);
1075 if (lpage == REPOPAGE_BLOBSIZE)
1077 write_compressed_page(target, vpage, lpage);
1089 * the code works the following way:
1091 * 1) find which keys should be written
1092 * 2) collect usage information for keys/ids/dirids, create schema
1094 * 3) use usage information to create mapping tables, so that often
1095 * used ids get a lower number
1096 * 4) encode data into buffers using the mapping tables
1097 * 5) write everything to disk
1100 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
1102 Pool *pool = repo->pool;
1103 int i, j, n, lastfilelistn;
1106 int nstrings, nrels;
1107 unsigned int sizeid;
1108 unsigned int solv_flags;
1117 unsigned char *repodataused;
1118 int anyrepodataused = 0;
1119 int anysolvableused = 0;
1121 struct cbdata cbdata;
1124 int poolusage, dirpoolusage, idused, dirused;
1127 Repodata *data, *dirpooldata;
1138 Id type_constantid = REPOKEY_TYPE_CONSTANTID;
1141 memset(&cbdata, 0, sizeof(cbdata));
1143 cbdata.target = ⌖
1145 repodata_initdata(&target, repo, 1);
1147 /* go through all repodata and find the keys we need */
1148 /* also unify keys */
1149 /* keymapstart - maps repo number to keymap offset */
1150 /* keymap - maps repo key to my key, 0 -> not used */
1152 /* start with all KEY_STORAGE_SOLVABLE ids */
1154 n = ID_NUM_INTERNAL;
1155 FOR_REPODATAS(repo, i, data)
1157 cbdata.keymap = solv_calloc(n, sizeof(Id));
1158 cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1159 repodataused = solv_calloc(repo->nrepodata, 1);
1164 /* add keys for STORAGE_SOLVABLE */
1165 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1169 if (i < SOLVABLE_PROVIDES)
1170 keyd.type = REPOKEY_TYPE_ID;
1171 else if (i < RPM_RPMDBID)
1172 keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1174 keyd.type = REPOKEY_TYPE_NUM;
1176 keyd.storage = KEY_STORAGE_SOLVABLE;
1179 keyd.storage = keyfilter(repo, &keyd, kfdata);
1180 if (keyd.storage == KEY_STORAGE_DROPPED)
1182 keyd.storage = KEY_STORAGE_SOLVABLE;
1186 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1189 if (repo->nsolvables)
1192 keyd.name = REPOSITORY_SOLVABLES;
1193 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1195 keyd.storage = KEY_STORAGE_INCORE;
1196 cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1204 n = ID_NUM_INTERNAL;
1206 FOR_REPODATAS(repo, i, data)
1208 cbdata.keymapstart[i] = n;
1209 cbdata.keymap[n++] = 0; /* key 0 */
1215 /* check if we want this repodata */
1216 memset(&keyd, 0, sizeof(keyd));
1220 if (keyfilter(repo, &keyd, kfdata) == -1)
1223 for (j = 1; j < data->nkeys; j++, n++)
1225 key = data->keys + j;
1226 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1228 cbdata.keymap[n] = cbdata.keymap[key->name];
1231 if (key->type == REPOKEY_TYPE_DELETED)
1233 cbdata.keymap[n] = 0;
1236 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1238 Repokey keyd = *key;
1239 keyd.size = repodata_globalize_id(data, key->size, 1);
1240 id = repodata_key2id(&target, &keyd, 0);
1243 id = repodata_key2id(&target, key, 0);
1246 Repokey keyd = *key;
1247 keyd.storage = KEY_STORAGE_INCORE;
1248 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1249 keyd.size = repodata_globalize_id(data, key->size, 1);
1250 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1254 keyd.storage = keyfilter(repo, &keyd, kfdata);
1255 if (keyd.storage == KEY_STORAGE_DROPPED)
1257 cbdata.keymap[n] = 0;
1261 id = repodata_key2id(&target, &keyd, 1);
1263 cbdata.keymap[n] = id;
1264 /* load repodata if not already loaded */
1265 if (data->state == REPODATA_STUB)
1267 if (data->loadcallback)
1268 data->loadcallback(data);
1270 data->state = REPODATA_ERROR;
1271 if (data->state != REPODATA_ERROR)
1273 /* redo this repodata! */
1275 n = cbdata.keymapstart[i];
1279 if (data->state == REPODATA_ERROR)
1282 cbdata.keymap[n] = 0;
1286 repodataused[i] = 1;
1287 anyrepodataused = 1;
1288 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1289 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1291 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1293 idused = 1; /* dirs also use ids */
1296 if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
1298 /* is this a file list extension */
1299 if (is_filelist_extension(data))
1301 /* hmm, we have a file list extension. Kill filelist of other repodata.
1302 * XXX: this is wrong, as the extension does not need to cover all
1303 * solvables of the other repodata */
1305 cbdata.keymap[lastfilelistn] = 0;
1313 if (data->localpool)
1316 poolusage = 3; /* need own pool */
1320 spool = &data->spool;
1327 else if (poolusage != 1)
1328 poolusage = 3; /* need own pool */
1334 dirpoolusage = 3; /* need own dirpool */
1338 dirpool = &data->dirpool;
1345 /* 0: no pool needed at all */
1346 /* 1: use global pool */
1347 /* 2: use repodata local pool */
1348 /* 3: need own pool */
1351 spool = &target.spool;
1352 /* hack: reuse global pool data so we don't have to map pool ids */
1355 stringpool_free(spool);
1356 stringpool_clone(spool, &pool->ss);
1358 cbdata.ownspool = spool;
1360 else if (poolusage == 0 || poolusage == 1)
1366 if (dirpoolusage == 3)
1368 dirpool = &target.dirpool;
1370 cbdata.owndirpool = dirpool;
1373 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1376 /********************************************************************/
1378 fprintf(stderr, "poolusage: %d\n", poolusage);
1379 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1380 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1381 for (i = 1; i < target.nkeys; i++)
1382 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);
1385 /* copy keys if requested */
1389 for (i = 1; i < target.nkeys; i++)
1390 queue_push2(keyq, target.keys[i].name, target.keys[i].type);
1395 /* put all the keys we need in our string pool */
1396 /* put mapped ids right into target.keys */
1397 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1399 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1400 if (key->type == REPOKEY_TYPE_CONSTANTID)
1402 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1403 type_constantid = key->type;
1404 key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1407 key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1410 stringpool_freehash(spool); /* free some mem */
1414 /********************************************************************/
1416 /* set needed count of all strings and rels,
1417 * find which keys are used in the solvables
1418 * put all strings in own spool
1421 reloff = spool->nstrings;
1423 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1425 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1426 needid[0].map = reloff;
1428 cbdata.needid = needid;
1429 cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
1430 cbdata.sp = cbdata.schema;
1431 cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
1433 /* create main schema */
1434 cbdata.sp = cbdata.schema;
1435 /* collect all other data from all repodatas */
1436 /* XXX: merge arrays of equal keys? */
1437 FOR_REPODATAS(repo, j, data)
1439 if (!repodataused[j])
1441 repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1444 /* add solvables if needed (may revert later) */
1445 if (repo->nsolvables)
1447 *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1448 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1451 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1453 idarraydata = repo->idarraydata;
1455 anysolvableused = 0;
1456 cbdata.doingsolvables = 1;
1457 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1459 if (s->repo != repo)
1462 /* set schema info, keep in sync with further down */
1464 if (cbdata.keymap[SOLVABLE_NAME])
1466 *sp++ = cbdata.keymap[SOLVABLE_NAME];
1467 needid[s->name].need++;
1469 if (cbdata.keymap[SOLVABLE_ARCH])
1471 *sp++ = cbdata.keymap[SOLVABLE_ARCH];
1472 needid[s->arch].need++;
1474 if (cbdata.keymap[SOLVABLE_EVR])
1476 *sp++ = cbdata.keymap[SOLVABLE_EVR];
1477 needid[s->evr].need++;
1479 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1481 *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
1482 needid[s->vendor].need++;
1484 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1486 *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
1487 target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
1489 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1491 *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
1492 target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1494 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1496 *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
1497 target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1499 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1501 *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
1502 target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
1504 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1506 *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
1507 target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1509 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1511 *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
1512 target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1514 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1516 *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
1517 target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1519 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1521 *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
1522 target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1524 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1526 *sp++ = cbdata.keymap[RPM_RPMDBID];
1527 target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
1531 if (anyrepodataused)
1533 FOR_REPODATAS(repo, j, data)
1535 if (!repodataused[j])
1537 if (i < data->start || i >= data->end)
1539 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1540 needid = cbdata.needid;
1544 cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1545 if (cbdata.solvschemata[n])
1546 anysolvableused = 1;
1549 cbdata.doingsolvables = 0;
1550 assert(n == repo->nsolvables);
1552 if (repo->nsolvables && !anysolvableused)
1554 /* strip off solvable from the main schema */
1555 target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
1557 for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
1559 *sp = target.schemadata[target.schemata[mainschema] + i];
1560 if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
1563 assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
1565 target.schemadatalen = target.schemata[mainschema];
1567 repodata_free_schemahash(&target);
1568 mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
1571 /********************************************************************/
1573 /* remove unused keys */
1574 keyused = solv_calloc(target.nkeys, sizeof(Id));
1575 for (i = 1; i < (int)target.schemadatalen; i++)
1576 keyused[target.schemadata[i]] = 1;
1578 for (n = i = 1; i < target.nkeys; i++)
1585 target.keys[n] = target.keys[i];
1588 keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
1589 keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
1596 queue_truncate(keyq, 2 * n - 2);
1598 /* update schema data to the new key ids */
1599 for (i = 1; i < (int)target.schemadatalen; i++)
1600 target.schemadata[i] = keyused[target.schemadata[i]];
1601 /* update keymap to the new key ids */
1602 for (i = 0; i < cbdata.nkeymap; i++)
1603 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1604 keyused = solv_free(keyused);
1606 /* increment needid of the used keys, they are already mapped to
1607 * the correct string pool */
1608 for (i = 1; i < target.nkeys; i++)
1610 if (target.keys[i].type == type_constantid)
1611 needid[target.keys[i].size].need++;
1612 needid[target.keys[i].name].need++;
1613 needid[target.keys[i].type].need++;
1616 /********************************************************************/
1618 if (dirpool && cbdata.dirused && !cbdata.dirused[0])
1620 /* no dirs used at all */
1621 cbdata.dirused = solv_free(cbdata.dirused);
1625 /* increment need id for used dir components */
1628 /* if we have own dirpool, all entries in it are used.
1629 also, all comp ids are already mapped by putinowndirpool(),
1630 so we can simply increment needid.
1631 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1632 /* else we re-use a dirpool of repodata "dirpooldata".
1633 dirused tells us which of the ids are used.
1634 we need to map comp ids if we generate a new pool.
1635 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1636 for (i = 1; i < dirpool->ndirs; i++)
1639 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1641 if (cbdata.dirused && !cbdata.dirused[i])
1643 id = dirpool->dirs[i];
1646 if (dirpooldata && cbdata.ownspool && id > 1)
1648 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1649 needid = cbdata.needid;
1656 /********************************************************************/
1659 * create mapping table, new keys are sorted by needid[].need
1661 * needid[key].need : old key -> new key
1662 * needid[key].map : new key -> old key
1665 /* zero out id 0 and rel 0 just in case */
1666 reloff = needid[0].map;
1668 needid[reloff].need = 0;
1670 for (i = 1; i < reloff + pool->nrels; i++)
1674 solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1676 /* make first entry '' */
1678 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1680 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1681 /* now needid is in new order, needid[newid].map -> oldid */
1683 /* calculate string space size, also zero out needid[].need */
1685 for (i = 1; i < reloff; i++)
1687 if (!needid[i].need)
1688 break; /* as we have sorted, every entry after this also has need == 0 */
1690 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1692 nstrings = i; /* our new string id end */
1694 /* make needid[oldid].need point to newid */
1695 for (i = 1; i < nstrings; i++)
1696 needid[needid[i].map].need = i;
1698 /* same as above for relations */
1699 for (i = 0; i < pool->nrels; i++)
1701 if (!needid[reloff + i].need)
1703 needid[reloff + i].need = 0;
1705 nrels = i; /* our new rel id end */
1707 for (i = 0; i < nrels; i++)
1708 needid[needid[reloff + i].map].need = nstrings + i;
1710 /* now we have: needid[oldid].need -> newid
1711 needid[newid].map -> oldid
1712 both for strings and relations */
1715 /********************************************************************/
1721 /* create our new target directory structure by traversing through all
1722 * used dirs. This will concatenate blocks with the same parent
1723 * directory into single blocks.
1724 * Instead of components, traverse_dirs stores the old dirids,
1725 * we will change this in the second step below */
1726 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1727 if (cbdata.dirused && !cbdata.dirused[1])
1728 cbdata.dirused[1] = 1; /* always want / entry */
1729 dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1731 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1733 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1734 /* change dirmap so that it maps from "new dirid" to "new compid" */
1735 if (!cbdata.dirused)
1736 cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1737 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1738 for (i = 1; i < ndirmap; i++)
1742 cbdata.dirused[dirmap[i]] = i;
1743 id = dirpool->dirs[dirmap[i]];
1744 if (dirpooldata && cbdata.ownspool && id > 1)
1745 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1746 dirmap[i] = needid[id].need;
1748 /* now the new target directory structure is complete (dirmap), and we have
1749 * dirused[olddirid] -> newdirid */
1752 /********************************************************************/
1755 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1758 cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
1760 xd = cbdata.extdata;
1761 cbdata.current_sub = 0;
1762 /* add main schema */
1764 data_addid(xd, mainschema);
1767 FOR_REPODATAS(repo, j, data)
1769 if (!repodataused[j])
1771 repodata_search(data, SOLVID_META, 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;
1779 if (anysolvableused)
1781 data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
1782 cbdata.doingsolvables = 1;
1784 /* check if we can do the special filelist memory optimization */
1785 if (anyrepodataused)
1787 for (i = 1; i < target.nkeys; i++)
1788 if (target.keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
1789 cbdata.filelistmode |= cbdata.filelistmode == 0 && target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY ? 1 : 2;
1790 else if (target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY)
1791 cbdata.filelistmode = 2;
1792 if (cbdata.filelistmode != 1)
1793 cbdata.filelistmode = 0;
1796 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1798 if (s->repo != repo)
1800 data_addid(xd, cbdata.solvschemata[n]);
1801 if (cbdata.keymap[SOLVABLE_NAME])
1802 data_addid(xd, needid[s->name].need);
1803 if (cbdata.keymap[SOLVABLE_ARCH])
1804 data_addid(xd, needid[s->arch].need);
1805 if (cbdata.keymap[SOLVABLE_EVR])
1806 data_addid(xd, needid[s->evr].need);
1807 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1808 data_addid(xd, needid[s->vendor].need);
1809 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1810 data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1811 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1812 data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1813 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1814 data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1815 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1816 data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1817 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1818 data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1819 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1820 data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1821 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1822 data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1823 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1824 data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1825 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1826 data_addid(xd, repo->rpmdbid[i - repo->start]);
1827 if (anyrepodataused)
1830 FOR_REPODATAS(repo, j, data)
1832 if (!repodataused[j])
1834 if (i < data->start || i >= data->end)
1836 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1839 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1840 cbdata.maxdata = xd->len - cbdata.lastlen;
1841 cbdata.lastlen = xd->len;
1844 cbdata.doingsolvables = 0;
1847 assert(cbdata.current_sub == cbdata.nsubschemata);
1848 if (cbdata.subschemata)
1850 cbdata.subschemata = solv_free(cbdata.subschemata);
1851 cbdata.nsubschemata = 0;
1854 /********************************************************************/
1860 /* write file header */
1861 write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1862 write_u32(&target, SOLV_VERSION_8);
1866 write_u32(&target, nstrings);
1867 write_u32(&target, nrels);
1868 write_u32(&target, ndirmap);
1869 write_u32(&target, anysolvableused ? repo->nsolvables : 0);
1870 write_u32(&target, target.nkeys);
1871 write_u32(&target, target.nschemata);
1873 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1874 solv_flags |= SOLV_FLAG_SIZE_BYTES;
1875 write_u32(&target, solv_flags);
1880 * calculate prefix encoding of the strings
1882 unsigned char *prefixcomp = solv_malloc(nstrings);
1883 unsigned int compsum = 0;
1887 for (i = 1; i < nstrings; i++)
1889 char *str = spool->stringspace + spool->strings[needid[i].map];
1891 for (same = 0; same < 255; same++)
1892 if (!old_str[same] || old_str[same] != str[same])
1894 prefixcomp[i] = same;
1902 write_u32(&target, sizeid);
1903 /* we save compsum bytes but need 1 extra byte for every string */
1904 write_u32(&target, sizeid + nstrings - 1 - compsum);
1905 for (i = 1; i < nstrings; i++)
1907 char *str = spool->stringspace + spool->strings[needid[i].map];
1908 write_u8(&target, prefixcomp[i]);
1909 write_str(&target, str + prefixcomp[i]);
1911 solv_free(prefixcomp);
1915 write_u32(&target, 0);
1916 write_u32(&target, 0);
1922 for (i = 0; i < nrels; i++)
1924 ran = pool->rels + (needid[reloff + i].map - reloff);
1925 write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1926 write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1927 write_u8(&target, ran->flags);
1931 * write dirs (skip both root and / entry)
1933 for (i = 2; i < ndirmap; i++)
1936 write_id(&target, dirmap[i]);
1938 write_id(&target, nstrings - dirmap[i]);
1945 for (i = 1; i < target.nkeys; i++)
1947 write_id(&target, needid[target.keys[i].name].need);
1948 write_id(&target, needid[target.keys[i].type].need);
1949 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1951 if (target.keys[i].type == type_constantid)
1952 write_id(&target, needid[target.keys[i].size].need);
1954 write_id(&target, target.keys[i].size);
1957 write_id(&target, cbdata.extdata[i].len);
1958 write_id(&target, target.keys[i].storage);
1964 write_id(&target, target.schemadatalen); /* XXX -1? */
1965 for (i = 1; i < target.nschemata; i++)
1966 write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
1968 /********************************************************************/
1970 write_id(&target, cbdata.maxdata);
1971 write_id(&target, cbdata.extdata[0].len);
1972 if (cbdata.extdata[0].len)
1973 write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1974 solv_free(cbdata.extdata[0].buf);
1976 /* do we have vertical data? */
1977 for (i = 1; i < target.nkeys; i++)
1978 if (cbdata.extdata[i].len)
1980 if (i < target.nkeys)
1982 /* yes, write it in pages */
1983 unsigned char vpage[REPOPAGE_BLOBSIZE];
1986 write_u32(&target, REPOPAGE_BLOBSIZE);
1987 for (i = 1; i < target.nkeys; i++)
1988 if (cbdata.extdata[i].len)
1990 if (cbdata.filelistmode)
1992 lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
1994 if (cbdata.filelistmode && i < target.nkeys)
1996 /* ok, just this single extdata, which is a filelist */
1997 xd = cbdata.extdata + i;
1999 cbdata.filelistmode = -1;
2000 for (j = 0; j < cbdata.nkeymap; j++)
2001 if (cbdata.keymap[j] != i)
2002 cbdata.keymap[j] = 0;
2003 for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
2005 if (s->repo != repo)
2007 FOR_REPODATAS(repo, j, data)
2009 if (!repodataused[j])
2011 if (i < data->start || i >= data->end)
2013 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
2015 if (xd->len > 1024 * 1024)
2017 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
2022 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
2025 write_compressed_page(&target, vpage, lpage);
2028 for (i = 1; i < target.nkeys; i++)
2029 solv_free(cbdata.extdata[i].buf);
2030 solv_free(cbdata.extdata);
2033 repodata_freedata(&target);
2036 solv_free(cbdata.solvschemata);
2037 solv_free(cbdata.schema);
2039 solv_free(cbdata.keymap);
2040 solv_free(cbdata.keymapstart);
2041 solv_free(cbdata.dirused);
2042 solv_free(repodataused);
2043 return target.error;
2046 struct repodata_write_data {
2047 int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
2053 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
2055 struct repodata_write_data *wd = kfdata;
2057 /* XXX: special repodata selection hack */
2058 if (key->name == 1 && key->size != wd->repodataid)
2060 if (key->storage == KEY_STORAGE_SOLVABLE)
2061 return KEY_STORAGE_DROPPED; /* not part of this repodata */
2063 return (*wd->keyfilter)(repo, key, wd->kfdata);
2064 return key->storage;
2068 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
2070 struct repodata_write_data wd;
2072 wd.keyfilter = keyfilter;
2074 wd.repodataid = data->repodataid;
2075 return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
2079 repodata_write(Repodata *data, FILE *fp)
2081 return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
2085 repo_write(Repo *repo, FILE *fp)
2087 return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);