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 #undef USE_IDARRAYBLOCK
33 #define USE_REL_IDARRAY
35 /*------------------------------------------------------------------*/
36 /* Id map optimizations */
38 typedef struct needid {
44 #define NEEDIDOFF(id) (ISRELDEP(id) ? (needid[0].map + GETRELID(id)) : id)
48 * idarray: array of Ids, ID_NULL terminated
49 * needid: array of Id->NeedId
51 * return size of array (including trailing zero)
56 incneedid(Id id, NeedId *needid)
58 needid[NEEDIDOFF(id)].need++;
62 incneedidarray(Id *idarray, NeedId *needid)
67 while ((id = *idarray++) != 0)
70 needid[NEEDIDOFF(id)].need++;
81 needid_cmp_need(const void *ap, const void *bp, void *dp)
86 r = b->need - a->need;
89 return a->map - b->map;
93 needid_cmp_need_s(const void *ap, const void *bp, void *dp)
97 Stringpool *spool = dp;
102 r = b->need - a->need;
105 as = spool->stringspace + spool->strings[a->map];
106 bs = spool->stringspace + spool->strings[b->map];
107 return strcmp(as, bs);
111 /*------------------------------------------------------------------*/
112 /* output helper routines, used for writing the header */
113 /* (the data itself is accumulated in memory and written with
121 write_u32(Repodata *data, unsigned int x)
126 if (putc(x >> 24, fp) == EOF ||
127 putc(x >> 16, fp) == EOF ||
128 putc(x >> 8, fp) == EOF ||
131 data->error = pool_error(data->repo->pool, -1, "write error u32: %s", strerror(errno));
141 write_u8(Repodata *data, unsigned int x)
145 if (putc(x, data->fp) == EOF)
147 data->error = pool_error(data->repo->pool, -1, "write error u8: %s", strerror(errno));
156 write_blob(Repodata *data, void *blob, int len)
160 if (len && fwrite(blob, len, 1, data->fp) != 1)
162 data->error = pool_error(data->repo->pool, -1, "write error blob: %s", strerror(errno));
167 write_compressed_blob(Repodata *data, void *blob, int len)
169 unsigned char cpage[65536];
174 int chunk = len > sizeof(cpage) ? sizeof(cpage) : len;
175 int flag = (chunk == len ? 0x80 : 0x00);
176 int clen = repopagestore_compress_page(blob, chunk, cpage, sizeof(cpage) - 1);
179 write_u8(data, flag);
180 write_u8(data, chunk >> 8);
181 write_u8(data, chunk);
182 write_blob(data, blob, chunk);
186 write_u8(data, flag | 0x40);
187 write_u8(data, clen >> 8);
188 write_u8(data, clen);
189 write_blob(data, cpage, clen);
201 write_id(Repodata *data, Id x)
209 putc((x >> 28) | 128, fp);
211 putc((x >> 21) | 128, fp);
212 putc((x >> 14) | 128, fp);
215 putc((x >> 7) | 128, fp);
216 if (putc(x & 127, fp) == EOF)
218 data->error = pool_error(data->repo->pool, -1, "write error id: %s", strerror(errno));
223 write_str(Repodata *data, const char *str)
227 if (fputs(str, data->fp) == EOF || putc(0, data->fp) == EOF)
229 data->error = pool_error(data->repo->pool, -1, "write error str: %s", strerror(errno));
238 write_idarray(Repodata *data, Pool *pool, NeedId *needid, Id *ids)
252 id = needid[NEEDIDOFF(id)].need;
254 id = (id & 63) | ((id & ~63) << 1);
260 write_id(data, id | 64);
274 Stringpool *ownspool;
276 int clonepool; /* are the pool ids cloned into ownspool? */
278 Id *keymap; /* keymap for this repodata */
282 Id *schema; /* schema construction space */
283 Id *sp; /* pointer in above */
289 struct extdata *extdata;
293 Id vstart; /* offset of key in vertical data */
298 int doingsolvables; /* working on solvables data */
301 Id lastdirid; /* last dir id seen in this repodata */
302 Id lastdirid_own; /* last dir id put in own pool */
305 #define NEEDID_BLOCK 1023
306 #define SCHEMATA_BLOCK 31
307 #define EXTDATA_BLOCK 4095
310 data_addid(struct extdata *xd, Id sx)
312 unsigned int x = (unsigned int)sx;
315 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
316 dp = xd->buf + xd->len;
321 *dp++ = (x >> 28) | 128;
323 *dp++ = (x >> 21) | 128;
324 *dp++ = (x >> 14) | 128;
327 *dp++ = (x >> 7) | 128;
329 xd->len = dp - xd->buf;
333 data_addideof(struct extdata *xd, Id sx, int eof)
335 unsigned int x = (unsigned int)sx;
338 xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
339 dp = xd->buf + xd->len;
344 *dp++ = (x >> 27) | 128;
346 *dp++ = (x >> 20) | 128;
347 *dp++ = (x >> 13) | 128;
350 *dp++ = (x >> 6) | 128;
351 *dp++ = eof ? (x & 63) : (x & 63) | 64;
352 xd->len = dp - xd->buf;
356 data_addideof_len(Id sx)
358 unsigned int x = (unsigned int)sx;
363 return x >= (1 << 20) ? 4 : 3;
365 return x >= (1 << 6) ? 2 : 1;
369 data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
375 data_addid(xd, (Id)(hx >> 3));
376 xd->buf[xd->len - 1] |= 128;
379 data_addid(xd, (Id)(x | 0x80000000));
380 xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
383 data_addid(xd, (Id)x);
386 #ifdef USE_REL_IDARRAY
389 cmp_ids(const void *pa, const void *pb, void *dp)
397 data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
405 data_addideof(xd, 0, 1);
408 for (len = 0; len < 64 && ids[len]; len++)
412 id = needid[NEEDIDOFF(id)].need;
417 for (i = len + 1; ids[i]; i++)
419 sids = solv_malloc2(i, sizeof(Id));
420 memcpy(sids, lids, 64 * sizeof(Id));
421 for (; ids[len]; len++)
425 id = needid[NEEDIDOFF(id)].need;
432 /* That bloody solvable:prereqmarker needs to stay in position :-( */
434 marker = needid[marker].need;
435 for (i = 0; i < len; i++)
436 if (sids[i] == marker)
439 solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
441 solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
445 /* The differencing above produces many runs of ones and twos. I tried
446 fairly elaborate schemes to RLE those, but they give only very mediocre
447 improvements in compression, as coding the escapes costs quite some
448 space. Even if they are coded only as bits in IDs. The best improvement
449 was about 2.7% for the whole .solv file. It's probably better to
450 invest some complexity into sharing idarrays, than RLEing. */
451 for (i = 0; i < len - 1; i++)
454 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
455 hence all real differences are offsetted by 1. Otherwise we would
456 have to handle negative differences, which would cost code space for
457 the encoding of the sign. We loose the exact mapping of prereq here,
458 but we know the result, so we can recover from that in the reader. */
466 /* XXX If difference is zero we have multiple equal elements,
467 we might want to skip writing them out. */
468 data_addideof(xd, id, 0);
475 data_addideof(xd, id, 1);
482 #ifdef USE_IDARRAYBLOCK
485 data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
491 data_addideof(xd, 0, 1);
494 while ((id = *ids++) != 0)
497 id = needid[NEEDIDOFF(id)].need;
500 id = (last - id) * 2 - 1; /* [1, 2 * last - 1] odd */
501 else if (id < 2 * last)
502 id = (id - last) * 2; /* [0, 2 * last - 2] even */
504 data_addideof(xd, id, *ids ? 0 : 1);
511 data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
516 data_addideof(xd, 0, 1);
519 while ((id = *ids++) != 0)
522 id = needid[NEEDIDOFF(id)].need;
523 data_addideof(xd, id, *ids ? 0 : 1);
532 data_addblob(struct extdata *xd, unsigned char *blob, int len)
534 xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
535 memcpy(xd->buf + xd->len, blob, len);
539 /* grow needid array so that it contains the specified id */
541 grow_needid(struct cbdata *cbdata, Id id)
543 int oldoff = cbdata->needid[0].map;
544 int newoff = (id + 1 + NEEDID_BLOCK) & ~NEEDID_BLOCK;
545 int nrels = cbdata->pool->nrels;
546 cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
548 memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
549 memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
550 cbdata->needid[0].map = newoff;
554 putinownpool(struct cbdata *cbdata, Repodata *data, Id id)
556 Stringpool *ss = data->localpool ? &data->spool : &cbdata->pool->ss;
557 const char *str = stringpool_id2str(ss, id);
558 id = stringpool_str2id(cbdata->ownspool, str, 1);
559 if (id >= cbdata->needid[0].map)
560 grow_needid(cbdata, id);
565 putinowndirpool_slow(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
569 parent = dirpool_parent(dp, dir);
571 parent = putinowndirpool_slow(cbdata, data, dp, parent);
572 compid = dirpool_compid(dp, dir);
573 if (cbdata->ownspool && compid > 1 && (!cbdata->clonepool || data->localpool))
574 compid = putinownpool(cbdata, data, compid);
575 return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
579 putinowndirpool(struct cbdata *cbdata, Repodata *data, Id dir)
581 if (dir && dir == cbdata->lastdirid)
582 return cbdata->lastdirid_own;
583 cbdata->lastdirid = dir;
584 cbdata->lastdirid_own = putinowndirpool_slow(cbdata, data, &data->dirpool, dir);
585 return cbdata->lastdirid_own;
590 * collect key/id/dirid usage information, create needed schemas
593 collect_needed_cb(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
595 struct cbdata *cbdata = vcbdata;
600 fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? (int)(s - cbdata->pool->solvables) : 0, s ? pool_id2str(cbdata->pool, s->name) : "", key->name, pool_id2str(cbdata->pool, key->name), key->type);
602 if (key->name == REPOSITORY_SOLVABLES)
603 return SEARCH_NEXT_KEY; /* we do not want this one */
605 rm = cbdata->keymap[key - data->keys];
607 return SEARCH_NEXT_KEY; /* we do not want this one */
609 /* record key in schema */
610 if (cbdata->sp[-1] != rm)
615 case REPOKEY_TYPE_ID:
616 case REPOKEY_TYPE_IDARRAY:
618 if (!ISRELDEP(id) && cbdata->ownspool && id > 1 && (!cbdata->clonepool || data->localpool))
619 id = putinownpool(cbdata, data, id);
620 incneedid(id, cbdata->needid);
622 case REPOKEY_TYPE_DIR:
623 case REPOKEY_TYPE_DIRNUMNUMARRAY:
624 case REPOKEY_TYPE_DIRSTRARRAY:
626 if (cbdata->owndirpool)
627 putinowndirpool(cbdata, data, id);
629 cbdata->dirused[id] = 1;
631 case REPOKEY_TYPE_FIXARRAY:
632 case REPOKEY_TYPE_FLEXARRAY:
635 /* finish schema, rewind to start */
636 Id *sp = cbdata->sp - 1;
641 cbdata->subschemata[sp[-2]] = repodata_schema2id(cbdata->target, sp, 1);
646 /* start new schema */
647 if (kv->entry == 0 || key->type == REPOKEY_TYPE_FLEXARRAY)
649 cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
650 *cbdata->sp++ = cbdata->nsubschemata++;
664 collect_needed_solvable(struct cbdata *cbdata, Solvable *s, Id *keymap)
666 /* set schema info, keep in sync with collect_data_solvable */
667 Repo *repo = s->repo;
669 NeedId *needid = cbdata->needid;
670 Repodata *target = cbdata->target;
671 Id *idarraydata = repo->idarraydata;
673 if (keymap[SOLVABLE_NAME])
675 *sp++ = keymap[SOLVABLE_NAME];
676 needid[s->name].need++;
678 if (keymap[SOLVABLE_ARCH])
680 *sp++ = keymap[SOLVABLE_ARCH];
681 needid[s->arch].need++;
683 if (keymap[SOLVABLE_EVR])
685 *sp++ = keymap[SOLVABLE_EVR];
686 needid[s->evr].need++;
688 if (s->vendor && keymap[SOLVABLE_VENDOR])
690 *sp++ = keymap[SOLVABLE_VENDOR];
691 needid[s->vendor].need++;
693 if (s->provides && keymap[SOLVABLE_PROVIDES])
695 *sp++ = keymap[SOLVABLE_PROVIDES];
696 target->keys[keymap[SOLVABLE_PROVIDES]].size += incneedidarray(idarraydata + s->provides, needid);
698 if (s->obsoletes && keymap[SOLVABLE_OBSOLETES])
700 *sp++ = keymap[SOLVABLE_OBSOLETES];
701 target->keys[keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(idarraydata + s->obsoletes, needid);
703 if (s->conflicts && keymap[SOLVABLE_CONFLICTS])
705 *sp++ = keymap[SOLVABLE_CONFLICTS];
706 target->keys[keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(idarraydata + s->conflicts, needid);
708 if (s->requires && keymap[SOLVABLE_REQUIRES])
710 *sp++ = keymap[SOLVABLE_REQUIRES];
711 target->keys[keymap[SOLVABLE_REQUIRES]].size += incneedidarray(idarraydata + s->requires, needid);
713 if (s->recommends && keymap[SOLVABLE_RECOMMENDS])
715 *sp++ = keymap[SOLVABLE_RECOMMENDS];
716 target->keys[keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(idarraydata + s->recommends, needid);
718 if (s->suggests && keymap[SOLVABLE_SUGGESTS])
720 *sp++ = keymap[SOLVABLE_SUGGESTS];
721 target->keys[keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(idarraydata + s->suggests, needid);
723 if (s->supplements && keymap[SOLVABLE_SUPPLEMENTS])
725 *sp++ = keymap[SOLVABLE_SUPPLEMENTS];
726 target->keys[keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(idarraydata + s->supplements, needid);
728 if (s->enhances && keymap[SOLVABLE_ENHANCES])
730 *sp++ = keymap[SOLVABLE_ENHANCES];
731 target->keys[keymap[SOLVABLE_ENHANCES]].size += incneedidarray(idarraydata + s->enhances, needid);
733 if (repo->rpmdbid && keymap[RPM_RPMDBID])
735 *sp++ = keymap[RPM_RPMDBID];
736 target->keys[keymap[RPM_RPMDBID]].size++;
744 * encode all of the data into the correct buffers
747 collect_data_cb(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
749 struct cbdata *cbdata = vcbdata;
755 if (key->name == REPOSITORY_SOLVABLES)
756 return SEARCH_NEXT_KEY;
758 rm = cbdata->keymap[key - data->keys];
760 return SEARCH_NEXT_KEY; /* we do not want this one */
761 storage = cbdata->target->keys[rm].storage;
763 xd = cbdata->extdata + 0; /* incore buffer */
764 if (storage == KEY_STORAGE_VERTICAL_OFFSET)
766 xd += rm; /* vertical buffer */
767 if (cbdata->vstart == -1)
768 cbdata->vstart = xd->len;
772 case REPOKEY_TYPE_DELETED:
773 case REPOKEY_TYPE_VOID:
774 case REPOKEY_TYPE_CONSTANT:
775 case REPOKEY_TYPE_CONSTANTID:
777 case REPOKEY_TYPE_ID:
779 if (!ISRELDEP(id) && cbdata->ownspool && id > 1 && (!cbdata->clonepool || data->localpool))
780 id = putinownpool(cbdata, data, id);
781 needid = cbdata->needid;
782 id = needid[NEEDIDOFF(id)].need;
785 case REPOKEY_TYPE_IDARRAY:
787 if (!ISRELDEP(id) && cbdata->ownspool && id > 1 && (!cbdata->clonepool || data->localpool))
788 id = putinownpool(cbdata, data, id);
789 needid = cbdata->needid;
790 id = needid[NEEDIDOFF(id)].need;
791 data_addideof(xd, id, kv->eof);
793 case REPOKEY_TYPE_STR:
794 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
796 case REPOKEY_TYPE_MD5:
797 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
799 case REPOKEY_TYPE_SHA1:
800 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
802 case REPOKEY_TYPE_SHA224:
803 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
805 case REPOKEY_TYPE_SHA256:
806 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
808 case REPOKEY_TYPE_SHA384:
809 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
811 case REPOKEY_TYPE_SHA512:
812 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
815 case REPOKEY_TYPE_NUM:
816 data_addid64(xd, kv->num, kv->num2);
818 case REPOKEY_TYPE_DIR:
820 if (cbdata->owndirpool)
821 id = putinowndirpool(cbdata, data, id);
822 id = cbdata->dirused[id];
825 case REPOKEY_TYPE_BINARY:
826 data_addid(xd, kv->num);
828 data_addblob(xd, (unsigned char *)kv->str, kv->num);
830 case REPOKEY_TYPE_DIRNUMNUMARRAY:
832 if (cbdata->owndirpool)
833 id = putinowndirpool(cbdata, data, id);
834 id = cbdata->dirused[id];
836 data_addid(xd, kv->num);
837 data_addideof(xd, kv->num2, kv->eof);
839 case REPOKEY_TYPE_DIRSTRARRAY:
841 if (cbdata->owndirpool)
842 id = putinowndirpool(cbdata, data, id);
843 id = cbdata->dirused[id];
844 if (rm == cbdata->filelistmode)
846 /* postpone adding to xd, just update len to get the correct offsets into the incore data*/
847 xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
850 data_addideof(xd, id, kv->eof);
851 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
853 case REPOKEY_TYPE_FIXARRAY:
854 case REPOKEY_TYPE_FLEXARRAY:
856 data_addid(xd, kv->num);
857 if (kv->eof != 2 && (!kv->entry || key->type == REPOKEY_TYPE_FLEXARRAY))
858 data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
859 if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
861 if (xd->len - cbdata->lastlen > cbdata->maxdata)
862 cbdata->maxdata = xd->len - cbdata->lastlen;
863 cbdata->lastlen = xd->len;
867 cbdata->target->error = pool_error(cbdata->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
870 if (storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
872 /* we can re-use old data in the blob here! */
873 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
874 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
880 /* special version of collect_data_cb that collects just one single REPOKEY_TYPE_DIRSTRARRAY vertical data */
882 collect_filelist_cb(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
884 struct cbdata *cbdata = vcbdata;
889 rm = cbdata->keymap[key - data->keys];
890 if (rm != cbdata->filelistmode)
891 return SEARCH_NEXT_KEY; /* we do not want this one */
893 if (cbdata->owndirpool)
894 id = putinowndirpool(cbdata, data, id);
895 id = cbdata->dirused[id];
896 xd = cbdata->extdata + rm; /* vertical buffer */
897 data_addideof(xd, id, kv->eof);
898 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
903 collect_data_solvable(struct cbdata *cbdata, Solvable *s, Id *keymap)
905 Repo *repo = s->repo;
906 Pool *pool = repo->pool;
907 struct extdata *xd = cbdata->extdata;
908 #ifdef USE_IDARRAYBLOCK
909 struct extdata *xda = xd + cbdata->target->nkeys; /* idarray block */
911 struct extdata *xda = xd;
914 NeedId *needid = cbdata->needid;
915 Id *idarraydata = repo->idarraydata;
917 if (keymap[SOLVABLE_NAME])
918 data_addid(xd, needid[s->name].need);
919 if (keymap[SOLVABLE_ARCH])
920 data_addid(xd, needid[s->arch].need);
921 if (keymap[SOLVABLE_EVR])
922 data_addid(xd, needid[s->evr].need);
923 if (s->vendor && keymap[SOLVABLE_VENDOR])
924 data_addid(xd, needid[s->vendor].need);
925 if (s->provides && keymap[SOLVABLE_PROVIDES])
926 data_adddepids(xda, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
927 if (s->obsoletes && keymap[SOLVABLE_OBSOLETES])
928 data_adddepids(xda, pool, needid, idarraydata + s->obsoletes, 0);
929 if (s->conflicts && keymap[SOLVABLE_CONFLICTS])
930 data_adddepids(xda, pool, needid, idarraydata + s->conflicts, 0);
931 if (s->requires && keymap[SOLVABLE_REQUIRES])
932 data_adddepids(xda, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
933 if (s->recommends && keymap[SOLVABLE_RECOMMENDS])
934 data_adddepids(xda, pool, needid, idarraydata + s->recommends, 0);
935 if (s->suggests && keymap[SOLVABLE_SUGGESTS])
936 data_adddepids(xda, pool, needid, idarraydata + s->suggests, 0);
937 if (s->supplements && keymap[SOLVABLE_SUPPLEMENTS])
938 data_adddepids(xda, pool, needid, idarraydata + s->supplements, 0);
939 if (s->enhances && keymap[SOLVABLE_ENHANCES])
940 data_adddepids(xda, pool, needid, idarraydata + s->enhances, 0);
941 if (repo->rpmdbid && keymap[RPM_RPMDBID])
942 data_addid(xd, repo->rpmdbid[(s - pool->solvables) - repo->start]);
945 /* traverse through directory with first child "dir" */
947 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
953 /* special case for '/', which has to come first */
956 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
958 if (used && !used[sib])
960 if (sib == 1 && parent == 1)
961 continue; /* already did that one above */
965 /* check if our block has some content */
967 return n - 1; /* nope, drop parent id again */
969 /* now go through all the siblings we just added and
970 * do recursive calls on them */
972 for (; parent < lastn; parent++)
974 sib = dirmap[parent];
975 if (used && used[sib] != 2) /* 2: used as parent */
977 child = dirpool_child(dp, sib);
980 dirmap[n++] = -parent; /* start new block */
981 n = traverse_dirs(dp, dirmap, n, child, used);
988 write_compressed_page(Repodata *data, unsigned char *page, int len)
991 unsigned char cpage[REPOPAGE_BLOBSIZE];
993 clen = repopagestore_compress_page(page, len, cpage, len - 1);
996 write_u32(data, len * 2);
997 write_blob(data, page, len);
1001 write_u32(data, clen * 2 + 1);
1002 write_blob(data, cpage, clen);
1006 static Id verticals[] = {
1008 SOLVABLE_DESCRIPTION,
1009 SOLVABLE_MESSAGEDEL,
1010 SOLVABLE_MESSAGEINS,
1020 SOLVABLE_CHANGELOG_AUTHOR,
1021 SOLVABLE_CHANGELOG_TEXT,
1025 static char *languagetags[] = {
1026 "solvable:summary:",
1027 "solvable:description:",
1028 "solvable:messageins:",
1029 "solvable:messagedel:",
1035 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
1037 const char *keyname;
1040 for (i = 0; verticals[i]; i++)
1041 if (key->name == verticals[i])
1042 return KEY_STORAGE_VERTICAL_OFFSET;
1043 keyname = pool_id2str(repo->pool, key->name);
1044 for (i = 0; languagetags[i] != 0; i++)
1045 if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
1046 return KEY_STORAGE_VERTICAL_OFFSET;
1047 return KEY_STORAGE_INCORE;
1051 write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
1053 unsigned char *dp = xd->buf;
1057 int ll = REPOPAGE_BLOBSIZE - lpage;
1060 memcpy(vpage + lpage, dp, ll);
1064 if (lpage == REPOPAGE_BLOBSIZE)
1066 write_compressed_page(target, vpage, lpage);
1075 create_keyskip(Repo *repo, Id entry, unsigned char *repodataused, Id **oldkeyskip)
1077 Repodata *data, *last = 0;
1081 if (repo->nrepodata <= 2)
1083 keyskip = *oldkeyskip;
1086 if (keyskip[1] >= 0x10000000)
1087 keyskip = solv_free(keyskip);
1089 keyskip[1] = keyskip[2];
1091 FOR_REPODATAS(repo, rdid, data)
1093 if (!repodataused[rdid])
1095 if (entry != SOLVID_META)
1097 if (entry < data->start || entry >= data->end)
1099 /* if repodataused is set we know that the state is AVAILABLE */
1100 if (!data->incoreoffset[entry - data->start])
1104 keyskip = repodata_fill_keyskip(last, entry, keyskip);
1108 if (cnt <= 1) /* just one repodata means we don't need a keyskip */
1110 *oldkeyskip = keyskip;
1113 keyskip = repodata_fill_keyskip(last, entry, keyskip);
1115 keyskip[2] = keyskip[1] + repo->nrepodata;
1116 *oldkeyskip = keyskip;
1125 repowriter_create(Repo *repo)
1127 Repowriter *writer = solv_calloc(1, sizeof(*writer));
1128 writer->repo = repo;
1129 writer->keyfilter = repo_write_stdkeyfilter;
1130 writer->repodatastart = 1;
1131 writer->repodataend = repo->nrepodata;
1132 writer->solvablestart = repo->start;
1133 writer->solvableend = repo->end;
1138 repowriter_free(Repowriter *writer)
1140 solv_free(writer->userdata);
1141 return solv_free(writer);
1145 repowriter_set_flags(Repowriter *writer, int flags)
1147 writer->flags = flags;
1151 repowriter_set_keyfilter(Repowriter *writer, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata)
1153 writer->keyfilter = keyfilter;
1154 writer->kfdata = kfdata;
1158 repowriter_set_keyqueue(Repowriter *writer, Queue *keyq)
1160 writer->keyq = keyq;
1164 repowriter_set_repodatarange(Repowriter *writer, int repodatastart, int repodataend)
1166 writer->repodatastart = repodatastart;
1167 writer->repodataend = repodataend;
1171 repowriter_set_solvablerange(Repowriter *writer, int solvablestart, int solvableend)
1173 writer->solvablestart = solvablestart;
1174 writer->solvableend = solvableend;
1178 repowriter_set_userdata(Repowriter *writer, const void *data, int len)
1180 writer->userdata = solv_free(writer->userdata);
1181 writer->userdatalen = 0;
1184 writer->userdata = solv_memdup(data, len);
1185 writer->userdatalen = len;
1189 * the code works the following way:
1191 * 1) find which keys should be written
1192 * 2) collect usage information for keys/ids/dirids, create schema
1194 * 3) use usage information to create mapping tables, so that often
1195 * used ids get a lower number
1196 * 4) encode data into buffers using the mapping tables
1197 * 5) write everything to disk
1200 repowriter_write(Repowriter *writer, FILE *fp)
1202 Repo *repo = writer->repo;
1203 Pool *pool = repo->pool;
1206 NeedId *needid, *needidp;
1207 int nstrings, nrels;
1208 unsigned int sizeid;
1209 unsigned int solv_flags;
1212 int searchflags = 0;
1216 Id *keymap; /* maps repo key to my key, 0 -> not used */
1218 int *keymapstart; /* maps repo number to keymap offset */
1224 unsigned char *repodataused;
1225 int anyrepodataused = 0;
1227 int solvablestart, solvableend;
1229 int anysolvableused = 0;
1232 struct cbdata cbdata;
1236 int poolusage, dirpoolusage;
1239 Repodata *data, *dirpooldata;
1246 Id mainschema, *mainschemakeys;
1250 Id type_constantid = 0;
1253 if (writer->userdatalen < 0 || writer->userdatalen >= 65536)
1254 return pool_error(pool, -1, "illegal userdata length: %d", writer->userdatalen);
1256 memset(&cbdata, 0, sizeof(cbdata));
1259 cbdata.target = ⌖
1261 repodata_initdata(&target, repo, 1);
1263 /* go through all repodata and find the keys we need */
1264 /* also unify keys */
1266 /* start with all KEY_STORAGE_SOLVABLE ids */
1268 n = ID_NUM_INTERNAL;
1269 FOR_REPODATAS(repo, i, data)
1272 keymap = solv_calloc(nkeymap, sizeof(Id));
1273 keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
1274 repodataused = solv_calloc(repo->nrepodata, 1);
1279 if (!(writer->flags & REPOWRITER_NO_STORAGE_SOLVABLE))
1281 /* add keys for STORAGE_SOLVABLE */
1282 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1286 if (i < SOLVABLE_PROVIDES)
1287 keyd.type = REPOKEY_TYPE_ID;
1288 else if (i < RPM_RPMDBID)
1289 keyd.type = REPOKEY_TYPE_IDARRAY;
1291 keyd.type = REPOKEY_TYPE_NUM;
1292 #ifdef USE_REL_IDARRAY
1293 if (keyd.type == REPOKEY_TYPE_IDARRAY)
1294 keyd.type = REPOKEY_TYPE_REL_IDARRAY;
1297 keyd.storage = KEY_STORAGE_SOLVABLE;
1298 if (writer->keyfilter)
1300 keyd.storage = writer->keyfilter(repo, &keyd, writer->kfdata);
1301 if (keyd.storage == KEY_STORAGE_DROPPED)
1303 keyd.storage = KEY_STORAGE_SOLVABLE;
1305 #ifdef USE_IDARRAYBLOCK
1306 if (keyd.type == REPOKEY_TYPE_IDARRAY)
1307 keyd.storage = KEY_STORAGE_IDARRAYBLOCK;
1311 keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1315 if (repo->nsolvables)
1318 keyd.name = REPOSITORY_SOLVABLES;
1319 keyd.type = REPOKEY_TYPE_FLEXARRAY;
1321 keyd.storage = KEY_STORAGE_INCORE;
1322 keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
1330 n = ID_NUM_INTERNAL;
1331 FOR_REPODATAS(repo, i, data)
1333 int idused, dirused;
1334 if (i < writer->repodatastart || i >= writer->repodataend)
1336 if (writer->keyfilter && (writer->flags & REPOWRITER_LEGACY) != 0)
1338 /* ask keyfilter if we want this repodata */
1340 /* check if we want this repodata */
1341 memset(&keyd, 0, sizeof(keyd));
1345 if (writer->keyfilter(repo, &keyd, writer->kfdata) == -1)
1349 keymap[n++] = 0; /* key 0 */
1350 idused = dirused = 0;
1351 for (j = 1; j < data->nkeys; j++, n++)
1353 key = data->keys + j;
1354 if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1356 keymap[n] = keymap[key->name];
1359 if (key->type == REPOKEY_TYPE_DELETED && (writer->flags & REPOWRITER_KEEP_TYPE_DELETED) == 0)
1364 if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
1366 Repokey keyd = *key;
1367 keyd.size = repodata_globalize_id(data, key->size, 1);
1368 id = repodata_key2id(&target, &keyd, 0);
1371 id = repodata_key2id(&target, key, 0);
1374 /* a new key. ask keyfilter if we want it before creating it */
1375 Repokey keyd = *key;
1376 keyd.storage = KEY_STORAGE_INCORE;
1377 if (keyd.type == REPOKEY_TYPE_CONSTANTID)
1378 keyd.size = repodata_globalize_id(data, key->size, 1);
1379 else if (keyd.type != REPOKEY_TYPE_CONSTANT)
1381 if (writer->keyfilter)
1383 keyd.storage = writer->keyfilter(repo, &keyd, writer->kfdata);
1384 if (keyd.storage == KEY_STORAGE_DROPPED)
1389 if (keyd.storage != KEY_STORAGE_VERTICAL_OFFSET)
1390 keyd.storage = KEY_STORAGE_INCORE; /* do not mess with us */
1392 if (data->state != REPODATA_STUB)
1393 id = repodata_key2id(&target, &keyd, 1);
1396 /* load repodata if not already loaded */
1397 if (data->state == REPODATA_STUB)
1399 int oldnkeys = data->nkeys;
1400 repodata_load(data);
1401 if (oldnkeys != data->nkeys)
1403 nkeymap += data->nkeys - oldnkeys; /* grow/shrink keymap */
1404 keymap = solv_realloc2(keymap, nkeymap, sizeof(Id));
1406 if (data->state == REPODATA_AVAILABLE)
1408 /* redo this repodata! */
1414 if (data->state != REPODATA_AVAILABLE && data->state != REPODATA_LOADING)
1421 repodataused[i] = 1;
1422 anyrepodataused = 1;
1423 if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
1424 key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1426 else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1428 idused = 1; /* dirs also use ids */
1434 if (data->localpool)
1437 poolusage = 3; /* need own pool */
1441 spool = &data->spool;
1448 else if (poolusage != 1)
1449 poolusage = 3; /* need own pool */
1455 dirpoolusage = 3; /* need own dirpool */
1459 dirpool = &data->dirpool;
1464 nkeymap = n; /* update */
1466 /* 0: no pool needed at all */
1467 /* 1: use global pool */
1468 /* 2: use repodata local pool */
1469 /* 3: need own pool */
1474 spool = &target.spool;
1475 target.localpool = 1; /* so we can use repodata_translate */
1476 /* hack: reuse global pool data so we don't have to map pool ids */
1479 stringpool_free(spool);
1480 stringpool_clone(spool, &pool->ss);
1481 cbdata.clonepool = 1;
1483 cbdata.ownspool = spool;
1485 else if (poolusage == 0 || poolusage == 1)
1491 if (dirpoolusage == 3)
1493 /* dirpoolusage == 3 means that at least two repodata
1494 * areas have dir keys. This means that two areas have
1495 * idused set to 1, which results in poolusage being
1496 * either 1 (global pool) or 3 (own pool) */
1497 dirpool = &target.dirpool;
1499 cbdata.owndirpool = dirpool;
1502 cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
1505 /********************************************************************/
1507 fprintf(stderr, "poolusage: %d\n", poolusage);
1508 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1509 fprintf(stderr, "clonepool: %d\n", clonepool);
1510 fprintf(stderr, "nkeys: %d\n", target.nkeys);
1511 for (i = 1; i < target.nkeys; i++)
1512 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);
1515 /********************************************************************/
1517 searchflags = SEARCH_SUB|SEARCH_ARRAYSENTINEL;
1518 if ((writer->flags & REPOWRITER_KEEP_TYPE_DELETED) != 0)
1519 searchflags |= SEARCH_KEEP_TYPE_DELETED;
1521 /* set needed count of all strings and rels,
1522 * find which keys are used in the solvables
1523 * put all strings in own spool
1526 reloff = spool->nstrings;
1527 if (cbdata.ownspool)
1528 reloff = (reloff + NEEDID_BLOCK) & ~NEEDID_BLOCK;
1529 else if (poolusage == 2)
1531 /* we'll need to put the key data into the spool,
1532 * so leave some room. 3 * nkeys is an upper bound */
1533 reloff += 3 * target.nkeys;
1536 needid = solv_calloc(reloff + pool->nrels, sizeof(*needid));
1537 needid[0].map = reloff; /* remember size in case we need to grow */
1539 cbdata.needid = needid;
1540 cbdata.schema = solv_calloc(target.nkeys + 2, sizeof(Id));
1542 /* create main schema */
1543 cbdata.sp = cbdata.schema + 1;
1545 /* collect meta data from all repodatas */
1546 /* XXX: merge arrays of equal keys? */
1547 keyskip = create_keyskip(repo, SOLVID_META, repodataused, &oldkeyskip);
1548 FOR_REPODATAS(repo, j, data)
1550 if (!repodataused[j])
1552 cbdata.keymap = keymap + keymapstart[j];
1553 cbdata.lastdirid = 0; /* clear dir mapping cache */
1554 repodata_search_keyskip(data, SOLVID_META, 0, searchflags, keyskip, collect_needed_cb, &cbdata);
1556 needid = cbdata.needid; /* maybe relocated */
1558 /* add solvables if needed (may revert later) */
1559 if (repo->nsolvables)
1561 *sp++ = keymap[REPOSITORY_SOLVABLES];
1562 target.keys[keymap[REPOSITORY_SOLVABLES]].size++;
1565 /* stash away main schema (including terminating zero) */
1566 mainschemakeys = solv_memdup2(cbdata.schema + 1, sp - cbdata.schema, sizeof(Id));
1568 /* collect data for all solvables */
1569 solvschemata = solv_calloc(repo->nsolvables, sizeof(Id)); /* allocate upper bound */
1570 solvablestart = writer->solvablestart < repo->start ? repo->start : writer->solvablestart;
1571 solvableend = writer->solvableend > repo->end ? repo->end : writer->solvableend;
1572 anysolvableused = 0;
1573 nsolvables = 0; /* solvables we are going to write, will be <= repo->nsolvables */
1574 cbdata.doingsolvables = 1;
1575 for (i = solvablestart, s = pool->solvables + i; i < solvableend; i++, s++)
1577 if (s->repo != repo)
1580 cbdata.sp = cbdata.schema + 1;
1581 collect_needed_solvable(&cbdata, s, keymap);
1583 if (anyrepodataused)
1585 keyskip = create_keyskip(repo, i, repodataused, &oldkeyskip);
1586 FOR_REPODATAS(repo, j, data)
1588 if (!repodataused[j] || i < data->start || i >= data->end)
1590 cbdata.keymap = keymap + keymapstart[j];
1591 cbdata.lastdirid = 0;
1592 repodata_search_keyskip(data, i, 0, searchflags, keyskip, collect_needed_cb, &cbdata);
1594 needid = cbdata.needid; /* maybe relocated */
1597 solvschemata[nsolvables] = repodata_schema2id(cbdata.target, cbdata.schema + 1, 1);
1598 if (solvschemata[nsolvables])
1599 anysolvableused = 1;
1602 cbdata.doingsolvables = 0;
1604 if (repo->nsolvables && !anysolvableused)
1606 /* strip off REPOSITORY_SOLVABLES from the main schema */
1607 for (sp = mainschemakeys; *sp; sp++)
1609 sp[-1] = 0; /* strip last entry */
1611 mainschema = repodata_schema2id(cbdata.target, mainschemakeys, 1);
1612 mainschemakeys = solv_free(mainschemakeys);
1614 /********************************************************************/
1616 /* remove unused keys */
1617 keyused = solv_calloc(target.nkeys, sizeof(Id));
1618 for (i = 1; i < (int)target.schemadatalen; i++)
1619 keyused[target.schemadata[i]] = 1;
1621 for (n = i = 1; i < target.nkeys; i++)
1626 target.keys[n] = target.keys[i];
1631 /* update schema data to the new key ids */
1632 for (i = 1; i < (int)target.schemadatalen; i++)
1633 target.schemadata[i] = keyused[target.schemadata[i]];
1634 /* update keymap to the new key ids */
1635 for (i = 0; i < nkeymap; i++)
1636 keymap[i] = keyused[keymap[i]];
1637 keyused = solv_free(keyused);
1639 /* copy keys if requested */
1642 queue_empty(writer->keyq);
1643 for (i = 1; i < target.nkeys; i++)
1644 queue_push2(writer->keyq, target.keys[i].name, target.keys[i].type);
1647 /********************************************************************/
1649 /* check if we can do the special filelist memory optimization
1650 * we do the check before the keys are mapped.
1651 * The optimization is done if there is just one vertical key and
1652 * it is of type REPOKEY_TYPE_DIRSTRARRAY */
1653 if (anysolvableused && anyrepodataused)
1655 for (i = 1; i < target.nkeys; i++)
1657 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1659 if (target.keys[i].type != REPOKEY_TYPE_DIRSTRARRAY || cbdata.filelistmode != 0)
1661 cbdata.filelistmode = 0;
1664 cbdata.filelistmode = i;
1668 /********************************************************************/
1672 /* put all the keys in our string pool */
1673 /* put mapped ids right into target.keys */
1674 for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
1676 key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
1677 id = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
1678 if (key->type == REPOKEY_TYPE_CONSTANTID)
1680 type_constantid = id;
1681 key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
1686 stringpool_freehash(spool); /* free some mem */
1687 if (cbdata.ownspool && spool->nstrings > needid[0].map)
1689 grow_needid(&cbdata, spool->nstrings - 1);
1690 needid = cbdata.needid; /* we relocated */
1694 type_constantid = REPOKEY_TYPE_CONSTANTID;
1696 /* increment needid of the keys */
1697 for (i = 1; i < target.nkeys; i++)
1699 if (target.keys[i].type == type_constantid)
1700 needid[target.keys[i].size].need++;
1701 needid[target.keys[i].name].need++;
1702 needid[target.keys[i].type].need++;
1705 /********************************************************************/
1707 /* increment need id of all relations
1708 * if we refer to another relation, make sure that the
1709 * need value is it is bigger than our value so that
1712 reloff = needid[0].map;
1713 for (i = pool->nrels - 1, needidp = needid + (reloff + i); i > 0; i--, needidp--)
1718 /* we have some relations with a non-zero need */
1721 for (rd = pool->rels + i; i > 0; i--, rd--)
1723 int need = needid[reloff + i].need;
1730 if (needid[reloff + id].need < need + 1)
1731 needid[reloff + id].need = need + 1;
1735 if (cbdata.ownspool && id > 1 && !cbdata.clonepool)
1737 id = stringpool_str2id(cbdata.ownspool, pool_id2str(pool, id), 1);
1738 if (id >= cbdata.needid[0].map)
1740 grow_needid(&cbdata, id);
1741 needid = cbdata.needid; /* we relocated */
1742 reloff = needid[0].map; /* we have a new offset */
1752 if (needid[reloff + id].need < need + 1)
1753 needid[reloff + id].need = need + 1;
1757 if (cbdata.ownspool && id > 1 && !cbdata.clonepool)
1759 id = stringpool_str2id(cbdata.ownspool, pool_id2str(pool, id), 1);
1760 if (id >= cbdata.needid[0].map)
1762 grow_needid(&cbdata, id);
1763 needid = cbdata.needid; /* we relocated */
1764 reloff = needid[0].map; /* we have a new offset */
1772 /********************************************************************/
1774 /* increment need id for used dir components */
1775 if (cbdata.owndirpool)
1777 /* if we have own dirpool, all entries in it are used.
1778 also, all comp ids are already mapped by putinowndirpool(),
1779 so we can simply increment needid.
1780 (owndirpool != 0, dirused == 0, dirpooldata == 0) */
1781 for (i = 1; i < dirpool->ndirs; i++)
1783 id = dirpool->dirs[i];
1792 /* else we re-use a dirpool of repodata "dirpooldata".
1793 dirused tells us which of the ids are used.
1794 we need to map comp ids if we generate a new pool.
1795 (owndirpool == 0, dirused != 0, dirpooldata != 0) */
1796 for (i = dirpool->ndirs - 1; i > 0; i--)
1798 if (!cbdata.dirused[i])
1800 parent = dirpool_parent(dirpool, i); /* always < i */
1801 cbdata.dirused[parent] = 2; /* 2: used as parent */
1802 id = dirpool->dirs[i];
1805 if (cbdata.ownspool && id > 1 && (!cbdata.clonepool || dirpooldata->localpool))
1807 id = putinownpool(&cbdata, dirpooldata, id);
1808 needid = cbdata.needid;
1812 if (!cbdata.dirused[0])
1814 cbdata.dirused = solv_free(cbdata.dirused);
1820 /********************************************************************/
1823 * create mapping table, new keys are sorted by needid[].need
1825 * needid[key].need : old key -> new key
1826 * needid[key].map : new key -> old key
1829 /* zero out id 0 and rel 0 just in case */
1830 reloff = needid[0].map;
1832 needid[reloff].need = 0;
1834 for (i = 1; i < reloff + pool->nrels; i++)
1837 /* make first entry '' */
1839 solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1840 solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1841 /* now needid is in new order, needid[newid].map -> oldid */
1843 /* calculate string space size, also zero out needid[].need */
1845 for (i = 1; i < reloff; i++)
1847 if (!needid[i].need)
1848 break; /* as we have sorted, every entry after this also has need == 0 */
1850 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1852 nstrings = i; /* our new string id end */
1854 /* make needid[oldid].need point to newid */
1855 for (i = 1; i < nstrings; i++)
1856 needid[needid[i].map].need = i;
1858 /* same as above for relations */
1859 for (i = 0; i < pool->nrels; i++)
1861 if (!needid[reloff + i].need)
1863 needid[reloff + i].need = 0;
1865 nrels = i; /* our new rel id end */
1867 for (i = 0; i < nrels; i++)
1868 needid[needid[reloff + i].map].need = nstrings + i;
1870 /* now we have: needid[oldid].need -> newid
1871 needid[newid].map -> oldid
1872 both for strings and relations */
1875 /********************************************************************/
1879 if (dirpool && dirpool->ndirs)
1881 /* create our new target directory structure by traversing through all
1882 * used dirs. This will concatenate blocks with the same parent
1883 * directory into single blocks.
1884 * Instead of components, traverse_dirs stores the old dirids,
1885 * we will change this in the second step below */
1886 /* (dirpooldata and dirused are 0 if we have our own dirpool) */
1887 if (cbdata.dirused && !cbdata.dirused[1])
1889 cbdata.dirused[1] = 1; /* always want / entry */
1890 cbdata.dirused[0] = 2; /* always want / entry */
1892 dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
1894 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1896 /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
1897 /* change dirmap so that it maps from "new dirid" to "new compid" */
1898 if (!cbdata.dirused)
1899 cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
1900 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1901 for (i = 1; i < ndirmap; i++)
1905 cbdata.dirused[dirmap[i]] = i;
1906 id = dirpool->dirs[dirmap[i]];
1907 if (dirpooldata && cbdata.ownspool && id > 1)
1908 id = putinownpool(&cbdata, dirpooldata, id);
1909 dirmap[i] = needid[id].need;
1911 /* now the new target directory structure is complete (dirmap), and we have
1912 * dirused[olddirid] -> newdirid */
1915 /********************************************************************/
1918 * we use extdata[0] for incore data and extdata[keyid] for vertical data
1919 * we use extdata[nkeys] for the idarray_block data
1921 * this must match the code above that creates the schema data!
1924 cbdata.extdata = solv_calloc(target.nkeys + 1, sizeof(struct extdata));
1926 xd = cbdata.extdata;
1927 cbdata.current_sub = 0;
1928 /* add main schema */
1930 data_addid(xd, mainschema);
1932 keyskip = create_keyskip(repo, SOLVID_META, repodataused, &oldkeyskip);
1933 FOR_REPODATAS(repo, j, data)
1935 if (!repodataused[j])
1937 cbdata.keymap = keymap + keymapstart[j];
1938 cbdata.lastdirid = 0;
1939 repodata_search_keyskip(data, SOLVID_META, 0, searchflags, keyskip, collect_data_cb, &cbdata);
1941 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1942 cbdata.maxdata = xd->len - cbdata.lastlen;
1943 cbdata.lastlen = xd->len;
1945 if (anysolvableused)
1947 data_addid(xd, nsolvables); /* FLEXARRAY nentries */
1948 cbdata.doingsolvables = 1;
1950 for (i = solvablestart, s = pool->solvables + i, n = 0; i < solvableend; i++, s++)
1952 if (s->repo != repo)
1954 data_addid(xd, solvschemata[n]);
1955 collect_data_solvable(&cbdata, s, keymap);
1956 if (anyrepodataused)
1958 keyskip = create_keyskip(repo, i, repodataused, &oldkeyskip);
1960 FOR_REPODATAS(repo, j, data)
1962 if (!repodataused[j] || i < data->start || i >= data->end)
1964 cbdata.keymap = keymap + keymapstart[j];
1965 cbdata.lastdirid = 0;
1966 repodata_search_keyskip(data, i, 0, searchflags, keyskip, collect_data_cb, &cbdata);
1969 if (xd->len - cbdata.lastlen > cbdata.maxdata)
1970 cbdata.maxdata = xd->len - cbdata.lastlen;
1971 cbdata.lastlen = xd->len;
1974 cbdata.doingsolvables = 0;
1977 assert(cbdata.current_sub == cbdata.nsubschemata);
1978 cbdata.subschemata = solv_free(cbdata.subschemata);
1979 cbdata.nsubschemata = 0;
1981 /********************************************************************/
1987 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1988 solv_flags |= SOLV_FLAG_SIZE_BYTES;
1989 if (writer->userdatalen)
1990 solv_flags |= SOLV_FLAG_USERDATA;
1991 if (cbdata.extdata[target.nkeys].len)
1992 solv_flags |= SOLV_FLAG_IDARRAYBLOCK;
1994 /* write file header */
1995 write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1996 if ((solv_flags & (SOLV_FLAG_USERDATA | SOLV_FLAG_IDARRAYBLOCK)) != 0)
1997 write_u32(&target, SOLV_VERSION_9);
1999 write_u32(&target, SOLV_VERSION_8);
2002 write_u32(&target, nstrings);
2003 write_u32(&target, nrels);
2004 write_u32(&target, ndirmap);
2005 write_u32(&target, anysolvableused ? nsolvables : 0);
2006 write_u32(&target, target.nkeys);
2007 write_u32(&target, target.nschemata);
2008 write_u32(&target, solv_flags);
2010 /* write userdata */
2011 if ((solv_flags & SOLV_FLAG_USERDATA) != 0)
2013 write_u32(&target, writer->userdatalen);
2014 write_blob(&target, writer->userdata, writer->userdatalen);
2020 * calculate prefix encoding of the strings
2022 unsigned char *prefixcomp = solv_malloc(nstrings);
2023 unsigned int compsum = 0;
2027 for (i = 1; i < nstrings; i++)
2029 char *str = spool->stringspace + spool->strings[needid[i].map];
2031 for (same = 0; same < 255; same++)
2032 if (!old_str[same] || old_str[same] != str[same])
2034 prefixcomp[i] = same;
2042 write_u32(&target, sizeid);
2043 /* we save compsum bytes but need 1 extra byte for every string */
2044 write_u32(&target, sizeid + nstrings - 1 - compsum);
2045 for (i = 1; i < nstrings; i++)
2047 char *str = spool->stringspace + spool->strings[needid[i].map];
2048 write_u8(&target, prefixcomp[i]);
2049 write_str(&target, str + prefixcomp[i]);
2051 solv_free(prefixcomp);
2055 write_u32(&target, 0); /* unpacked size */
2056 write_u32(&target, 0); /* compressed size */
2062 for (i = 0; i < nrels; i++)
2064 Reldep *ran = pool->rels + (needid[reloff + i].map - reloff);
2065 write_id(&target, needid[NEEDIDOFF(ran->name)].need);
2066 write_id(&target, needid[NEEDIDOFF(ran->evr)].need);
2067 write_u8(&target, ran->flags);
2071 * write dirs (skip both root and / entry)
2073 for (i = 2; i < ndirmap; i++)
2076 write_id(&target, dirmap[i]);
2078 write_id(&target, nstrings - dirmap[i]);
2085 for (i = 1; i < target.nkeys; i++)
2087 write_id(&target, needid[target.keys[i].name].need);
2088 write_id(&target, needid[target.keys[i].type].need);
2089 if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
2091 if (target.keys[i].type == type_constantid)
2092 write_id(&target, needid[target.keys[i].size].need);
2094 write_id(&target, target.keys[i].size);
2097 write_id(&target, cbdata.extdata[i].len);
2098 write_id(&target, target.keys[i].storage);
2104 write_id(&target, target.schemadatalen); /* XXX -1? */
2105 for (i = 1; i < target.nschemata; i++)
2106 write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
2108 /* write idarray_block data if not empty */
2109 if (cbdata.extdata[target.nkeys].len)
2111 unsigned int cnt = 0;
2115 xd = cbdata.extdata + target.nkeys;
2116 /* calculate number of entries */
2117 for (l = xd->len, b = xd->buf; l--;)
2119 unsigned char x = *b++;
2120 if ((x & 0x80) == 0)
2121 cnt += (x & 0x40) ? 1 : 2;
2123 write_id(&target, cnt);
2125 write_compressed_blob(&target, xd->buf, xd->len);
2132 xd = cbdata.extdata;
2133 write_id(&target, cbdata.maxdata);
2134 write_id(&target, xd->len);
2136 write_blob(&target, xd->buf, xd->len);
2140 * write vertical data if we have any
2142 for (i = 1; i < target.nkeys; i++)
2143 if (cbdata.extdata[i].len)
2145 if (i < target.nkeys)
2147 /* have vertical data, write it in pages */
2148 unsigned char vpage[REPOPAGE_BLOBSIZE];
2151 write_u32(&target, REPOPAGE_BLOBSIZE);
2152 if (!cbdata.filelistmode)
2154 for (i = 1; i < target.nkeys; i++)
2155 if (cbdata.extdata[i].len)
2156 lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
2160 /* ok, just one single extdata which is of type REPOKEY_TYPE_DIRSTRARRAY */
2161 xd = cbdata.extdata + i;
2163 keyskip = create_keyskip(repo, SOLVID_META, repodataused, &oldkeyskip);
2164 FOR_REPODATAS(repo, j, data)
2166 if (!repodataused[j])
2168 cbdata.keymap = keymap + keymapstart[j];
2169 cbdata.lastdirid = 0;
2170 repodata_search_keyskip(data, SOLVID_META, 0, searchflags, keyskip, collect_filelist_cb, &cbdata);
2172 for (i = solvablestart, s = pool->solvables + i; i < solvableend; i++, s++)
2174 if (s->repo != repo)
2176 keyskip = create_keyskip(repo, i, repodataused, &oldkeyskip);
2177 FOR_REPODATAS(repo, j, data)
2179 if (!repodataused[j] || i < data->start || i >= data->end)
2181 cbdata.keymap = keymap + keymapstart[j];
2182 cbdata.lastdirid = 0;
2183 repodata_search_keyskip(data, i, 0, searchflags, keyskip, collect_filelist_cb, &cbdata);
2185 if (xd->len > 1024 * 1024)
2187 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
2192 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
2195 write_compressed_page(&target, vpage, lpage);
2198 for (i = 1; i < target.nkeys; i++)
2199 solv_free(cbdata.extdata[i].buf);
2200 solv_free(cbdata.extdata);
2203 repodata_freedata(&target);
2206 solv_free(solvschemata);
2207 solv_free(cbdata.schema);
2210 solv_free(keymapstart);
2211 solv_free(cbdata.dirused);
2212 solv_free(repodataused);
2213 solv_free(oldkeyskip);
2214 return target.error;
2218 repo_write(Repo *repo, FILE *fp)
2221 Repowriter *writer = repowriter_create(repo);
2222 res = repowriter_write(writer, fp);
2223 repowriter_free(writer);
2228 repodata_write(Repodata *data, FILE *fp)
2231 Repowriter *writer = repowriter_create(data->repo);
2232 repowriter_set_repodatarange(writer, data->repodataid, data->repodataid + 1);
2233 repowriter_set_flags(writer, REPOWRITER_NO_STORAGE_SOLVABLE);
2234 res = repowriter_write(writer, fp);
2235 repowriter_free(writer);
2239 /* deprecated functions, do not use in new code! */
2241 repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
2244 Repowriter *writer = repowriter_create(repo);
2245 repowriter_set_flags(writer, REPOWRITER_LEGACY);
2246 repowriter_set_keyfilter(writer, keyfilter, kfdata);
2247 repowriter_set_keyqueue(writer, keyq);
2248 res = repowriter_write(writer, fp);
2249 repowriter_free(writer);
2254 repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
2257 Repowriter *writer = repowriter_create(data->repo);
2258 repowriter_set_repodatarange(writer, data->repodataid, data->repodataid + 1);
2259 repowriter_set_flags(writer, REPOWRITER_NO_STORAGE_SOLVABLE | REPOWRITER_LEGACY);
2260 repowriter_set_keyfilter(writer, keyfilter, kfdata);
2261 repowriter_set_keyqueue(writer, keyq);
2262 res = repowriter_write(writer, fp);
2263 repowriter_free(writer);