From: Michael Schroeder Date: Tue, 29 Jul 2025 09:52:42 +0000 (+0200) Subject: Move whatprovides handling from pool.c to poolwhatprovides.c X-Git-Tag: 0.7.35~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f72d9ed12704d335efe32619b7ee0dd5d451047c;p=thirdparty%2Flibsolv.git Move whatprovides handling from pool.c to poolwhatprovides.c --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25ad09be..f11d555a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,8 +14,9 @@ ENDIF (HAVE___QSORT_R) ADD_DEFINITIONS (-DLIBSOLV_INTERNAL=1) SET (libsolv_SRCS - bitmap.c poolarch.c poolvendor.c poolid.c strpool.c dirpool.c - solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c + bitmap.c poolarch.c poolvendor.c poolid.c + poolwhatprovides.c pool.c strpool.c dirpool.c + solver.c solverdebug.c repo_solv.c repo_write.c evr.c queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c chksum.c md5.c sha1.c sha2.c solvversion.c selection.c diff --git a/src/pool.c b/src/pool.c index 0f989b16..4c4c4625 100644 --- a/src/pool.c +++ b/src/pool.c @@ -305,404 +305,77 @@ pool_set_installed(Pool *pool, Repo *installed) pool_freewhatprovides(pool); } -static int -pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp, void *dp) -{ - int r; - Pool *pool = dp; - Id oa, ob, *da, *db; - oa = pool->whatprovides[*(Id *)ap]; - ob = pool->whatprovides[*(Id *)bp]; - if (oa == ob) - return *(Id *)ap - *(Id *)bp; - da = pool->whatprovidesdata + oa; - db = pool->whatprovidesdata + ob; - while (*db) - if ((r = (*da++ - *db++)) != 0) - return r; - if (*da) - return *da; - return *(Id *)ap - *(Id *)bp; -} - -/* - * pool_shrink_whatprovides - unify whatprovides data - * - * whatprovides_rel must be empty for this to work! - * - */ -static void -pool_shrink_whatprovides(Pool *pool) -{ - Id i, n, id; - Id *sorted; - Id lastid, *last, *dp, *lp; - Offset o; - int r; - - if (pool->ss.nstrings < 3) - return; - sorted = solv_malloc2(pool->ss.nstrings, sizeof(Id)); - for (i = id = 0; id < pool->ss.nstrings; id++) - if (pool->whatprovides[id] >= 4) - sorted[i++] = id; - n = i; - solv_sort(sorted, n, sizeof(Id), pool_shrink_whatprovides_sortcmp, pool); - last = 0; - lastid = 0; - for (i = 0; i < n; i++) - { - id = sorted[i]; - o = pool->whatprovides[id]; - dp = pool->whatprovidesdata + o; - if (last) - { - lp = last; - while (*dp) - if (*dp++ != *lp++) - { - last = 0; - break; - } - if (last && *lp) - last = 0; - if (last) - { - pool->whatprovides[id] = -lastid; - continue; - } - } - last = pool->whatprovidesdata + o; - lastid = id; - } - solv_free(sorted); - dp = pool->whatprovidesdata + 4; - for (id = 1; id < pool->ss.nstrings; id++) - { - o = pool->whatprovides[id]; - if (!o) - continue; - if ((Id)o < 0) - { - i = -(Id)o; - if (i >= id) - abort(); - pool->whatprovides[id] = pool->whatprovides[i]; - continue; - } - if (o < 4) - continue; - lp = pool->whatprovidesdata + o; - if (lp < dp) - abort(); - pool->whatprovides[id] = dp - pool->whatprovidesdata; - while ((*dp++ = *lp++) != 0) - ; - } - o = dp - pool->whatprovidesdata; - POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesdata from %d to %d\n", pool->whatprovidesdataoff, o); - if (pool->whatprovidesdataoff == o) - return; - r = pool->whatprovidesdataoff - o; - pool->whatprovidesdataoff = o; - pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id)); - if (r > pool->whatprovidesdataleft) - r = pool->whatprovidesdataleft; - memset(pool->whatprovidesdata + o, 0, r * sizeof(Id)); -} -/* this gets rid of all the zeros in the aux */ -static void -pool_shrink_whatprovidesaux(Pool *pool) -{ - int num = pool->whatprovidesauxoff; - Id id; - Offset newoff; - Id *op, *wp = pool->whatprovidesauxdata + 1; - int i; +/*************************************************************************/ - for (i = 0; i < num; i++) - { - Offset o = pool->whatprovidesaux[i]; - if (o < 2) - continue; - op = pool->whatprovidesauxdata + o; - pool->whatprovidesaux[i] = wp - pool->whatprovidesauxdata; - if (op < wp) - abort(); - while ((id = *op++) != 0) - *wp++ = id; - } - newoff = wp - pool->whatprovidesauxdata; - pool->whatprovidesauxdata = solv_realloc(pool->whatprovidesauxdata, newoff * sizeof(Id)); - POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesauxdata from %d to %d\n", pool->whatprovidesauxdataoff, newoff); - pool->whatprovidesauxdataoff = newoff; -} +#if defined(HAIKU) || defined(MULTI_SEMANTICS) +/* forward declaration */ +static int pool_intersect_evrs_rel_compat(Pool *pool, Reldep *range, int flags, int evr); +#endif +#if defined(MULTI_SEMANTICS) +# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE) +#elif defined(DEBIAN) +# define EVRCMP_DEPCMP EVRCMP_COMPARE +#else +# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE +#endif -/* - * pool_createwhatprovides() - * - * create hashes over pool of solvables to ease provide lookups - * - */ -void -pool_createwhatprovides(Pool *pool) +/* match (flags, evr) against provider (pflags, pevr) */ +/* note that this code is also in poolwhatprovides */ +int +pool_intersect_evrs(Pool *pool, int pflags, Id pevr, int flags, int evr) { - int i, num, np, extra; - Offset off; - Solvable *s; - Id id; - Offset *idp, n; - Offset *whatprovides; - Id *whatprovidesdata, *dp, *whatprovidesauxdata; - Offset *whatprovidesaux; - unsigned int now; - - now = solv_timems(0); - POOL_DEBUG(SOLV_DEBUG_STATS, "number of solvables: %d, memory used: %d K\n", pool->nsolvables, pool->nsolvables * (int)sizeof(Solvable) / 1024); - POOL_DEBUG(SOLV_DEBUG_STATS, "number of ids: %d + %d\n", pool->ss.nstrings, pool->nrels); - POOL_DEBUG(SOLV_DEBUG_STATS, "string memory used: %d K array + %d K data, rel memory used: %d K array\n", pool->ss.nstrings / (1024 / (int)sizeof(Id)), pool->ss.sstrings / 1024, pool->nrels * (int)sizeof(Reldep) / 1024); - if (pool->ss.stringhashmask || pool->relhashmask) - POOL_DEBUG(SOLV_DEBUG_STATS, "string hash memory: %d K, rel hash memory : %d K\n", (pool->ss.stringhashmask + 1) / (int)(1024/sizeof(Id)), (pool->relhashmask + 1) / (int)(1024/sizeof(Id))); - - pool_freeidhashes(pool); /* XXX: should not be here! */ - pool_freewhatprovides(pool); - num = pool->ss.nstrings; - pool->whatprovides = whatprovides = solv_calloc_block(num, sizeof(Offset), WHATPROVIDES_BLOCK); - pool->whatprovides_rel = solv_calloc_block(pool->nrels, sizeof(Offset), WHATPROVIDES_BLOCK); - - /* count providers for each name */ - for (i = pool->nsolvables - 1; i > 0; i--) - { - Id *pp; - s = pool->solvables + i; - if (!s->provides || !s->repo || s->repo->disabled) - continue; - if (!pool_installable_whatprovides(pool, s)) - continue; - pp = s->repo->idarraydata + s->provides; - while ((id = *pp++) != 0) - { - while (ISRELDEP(id)) - { - Reldep *rd = GETRELDEP(pool, id); - id = rd->name; - } - whatprovides[id]++; /* inc count of providers */ - } - } - - off = 4; /* first entry is undef, second is empty list, third is system solvable */ - np = 0; /* number of names provided */ - for (i = 0, idp = whatprovides; i < num; i++, idp++) - { - n = *idp; - if (!n) /* no providers */ - { - *idp = 1; /* offset for empty list */ - continue; - } - off += n; /* make space for all providers */ - *idp = off++; /* now idp points to terminating zero */ - np++; /* inc # of provider 'slots' for stats */ - } - - POOL_DEBUG(SOLV_DEBUG_STATS, "provide ids: %d\n", np); - - /* reserve some space for relation data */ - extra = 2 * pool->nrels; - if (extra < 256) - extra = 256; - - POOL_DEBUG(SOLV_DEBUG_STATS, "provide space needed: %d + %d\n", off, extra); - - /* alloc space for all providers + extra */ - whatprovidesdata = solv_calloc(off + extra, sizeof(Id)); - whatprovidesdata[2] = SYSTEMSOLVABLE; - - /* alloc aux vector */ - whatprovidesauxdata = 0; - if (!pool->nowhatprovidesaux) - { - pool->whatprovidesaux = whatprovidesaux = solv_calloc(num, sizeof(Offset)); - pool->whatprovidesauxoff = num; - pool->whatprovidesauxdataoff = off; - pool->whatprovidesauxdata = whatprovidesauxdata = solv_calloc(pool->whatprovidesauxdataoff, sizeof(Id)); - } - - /* now fill data for all provides */ - for (i = pool->nsolvables - 1; i > 0; i--) - { - Id *pp; - s = pool->solvables + i; - if (!s->provides || !s->repo || s->repo->disabled) - continue; - if (!pool_installable_whatprovides(pool, s)) - continue; - /* for all provides of this solvable */ - pp = s->repo->idarraydata + s->provides; - while ((id = *pp++) != 0) - { - Id auxid = id; - while (ISRELDEP(id)) - { - Reldep *rd = GETRELDEP(pool, id); - id = rd->name; - } - dp = whatprovidesdata + whatprovides[id]; /* offset into whatprovidesdata */ - if (*dp != i) /* don't add same solvable twice */ - { - dp[-1] = i; - whatprovides[id]--; - } - else - auxid = 1; - if (whatprovidesauxdata) - whatprovidesauxdata[whatprovides[id]] = auxid; - } - } - if (pool->whatprovidesaux) - memcpy(pool->whatprovidesaux, pool->whatprovides, num * sizeof(Id)); - pool->whatprovidesdata = whatprovidesdata; - pool->whatprovidesdataoff = off; - pool->whatprovidesdataleft = extra; - pool_shrink_whatprovides(pool); - if (pool->whatprovidesaux) - pool_shrink_whatprovidesaux(pool); - POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovides memory used: %d K id array, %d K data\n", (pool->ss.nstrings + pool->nrels + WHATPROVIDES_BLOCK) / (int)(1024/sizeof(Id)), (pool->whatprovidesdataoff + pool->whatprovidesdataleft) / (int)(1024/sizeof(Id))); - if (pool->whatprovidesaux) - POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovidesaux memory used: %d K id array, %d K data\n", pool->whatprovidesauxoff / (int)(1024/sizeof(Id)), pool->whatprovidesauxdataoff / (int)(1024/sizeof(Id))); - - queue_empty(&pool->lazywhatprovidesq); - if (pool->addedfileprovides == 1) - { - /* lazyly add file provides for nonstd */ - for (i = 0; i < pool->nonstd_nids; i++) - { - Id id = pool->nonstd_ids[i]; - const char *str = pool->ss.stringspace + pool->ss.strings[id]; - if (str[0] != '/') /* just in case, all entries should start with '/' */ - continue; - /* setup lazy adding, but remember old value */ - if (pool->whatprovides[id] > 1) - queue_push2(&pool->lazywhatprovidesq, id, pool->whatprovides[id]); - pool->whatprovides[id] = 0; - if (pool->whatprovidesaux) - pool->whatprovidesaux[id] = 0; /* sorry */ - } - } - else if (!pool->addedfileprovides && pool->disttype == DISTTYPE_RPM) + if (!pflags || !flags || pflags >= 8 || flags >= 8) + return 0; + if (flags == 7 || pflags == 7) + return 1; /* rel provides every version */ + if ((pflags & flags & (REL_LT | REL_GT)) != 0) + return 1; /* both rels show in the same direction */ + if (pevr == evr) + return (flags & pflags & REL_EQ) ? 1 : 0; +#if defined(HAIKU) || defined(MULTI_SEMANTICS) + if (ISRELDEP(pevr)) { - POOL_DEBUG(SOLV_DEBUG_STATS, "WARNING: pool_addfileprovides was not called, this may result in slow operation\n"); - /* lazyly add file provides */ - for (i = 1; i < num; i++) - { - const char *str = pool->ss.stringspace + pool->ss.strings[i]; - if (str[0] != '/') - continue; - if (pool->addedfileprovides == 1 && repodata_filelistfilter_matches(0, str)) - continue; - /* setup lazy adding, but remember old value */ - if (pool->whatprovides[i] > 1) - queue_push2(&pool->lazywhatprovidesq, i, pool->whatprovides[i]); - pool->whatprovides[i] = 0; - if (pool->whatprovidesaux) - pool->whatprovidesaux[i] = 0; /* sorry */ - } + Reldep *rd = GETRELDEP(pool, pevr); + if (rd->flags == REL_COMPAT) + return pool_intersect_evrs_rel_compat(pool, rd, flags, evr); } - if (pool->lazywhatprovidesq.count) - POOL_DEBUG(SOLV_DEBUG_STATS, "lazywhatprovidesq size: %d entries\n", pool->lazywhatprovidesq.count / 2); - - POOL_DEBUG(SOLV_DEBUG_STATS, "createwhatprovides took %d ms\n", solv_timems(now)); -} - -/* - * free all of our whatprovides data - * be careful, everything internalized with pool_queuetowhatprovides is - * gone, too - */ -void -pool_freewhatprovides(Pool *pool) -{ - pool->whatprovides = solv_free(pool->whatprovides); - pool->whatprovides_rel = solv_free(pool->whatprovides_rel); - pool->whatprovidesdata = solv_free(pool->whatprovidesdata); - pool->whatprovidesdataoff = 0; - pool->whatprovidesdataleft = 0; - pool->whatprovidesaux = solv_free(pool->whatprovidesaux); - pool->whatprovidesauxdata = solv_free(pool->whatprovidesauxdata); - pool->whatprovidesauxoff = 0; - pool->whatprovidesauxdataoff = 0; -} - - -/******************************************************************************/ - -/* - * pool_queuetowhatprovides - add queue contents to whatprovidesdata - * - * used for whatprovides, jobs, learnt rules, selections - * input: q: queue of Ids - * returns: Offset into whatprovidesdata - * - */ - -Id -pool_ids2whatprovides(Pool *pool, Id *ids, int count) -{ - Offset off; - - if (count == 0) /* queue empty -> 1 */ - return 1; - if (count == 1 && *ids == SYSTEMSOLVABLE) - return 2; - - /* extend whatprovidesdata if needed, +1 for 0-termination */ - if (pool->whatprovidesdataleft < count + 1) +#endif + switch (pool_evrcmp(pool, pevr, evr, EVRCMP_DEPCMP)) { - POOL_DEBUG(SOLV_DEBUG_STATS, "growing provides hash data...\n"); - pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (pool->whatprovidesdataoff + count + 4096) * sizeof(Id)); - pool->whatprovidesdataleft = count + 4096; + case -2: + return (pflags & REL_EQ) ? 1 : 0; + case -1: + return (flags & REL_LT) || (pflags & REL_GT) ? 1 : 0; + case 0: + return (flags & pflags & REL_EQ) ? 1 : 0; + case 1: + return (flags & REL_GT) || (pflags & REL_LT) ? 1 : 0; + case 2: + return (flags & REL_EQ) ? 1 : 0; + default: + break; } - - /* copy queue to next free slot */ - off = pool->whatprovidesdataoff; - memcpy(pool->whatprovidesdata + pool->whatprovidesdataoff, ids, count * sizeof(Id)); - - /* adapt count and 0-terminate */ - pool->whatprovidesdataoff += count; - pool->whatprovidesdata[pool->whatprovidesdataoff++] = 0; - pool->whatprovidesdataleft -= count + 1; - - return (Id)off; + return 0; } -Id -pool_queuetowhatprovides(Pool *pool, Queue *q) +#if defined(HAIKU) || defined(MULTI_SEMANTICS) +static int +pool_intersect_evrs_rel_compat(Pool *pool, Reldep *range, int flags, int evr) { - int count = q->count; - if (count == 0) /* queue empty -> 1 */ - return 1; - if (count == 1 && q->elements[0] == SYSTEMSOLVABLE) - return 2; - return pool_ids2whatprovides(pool, q->elements, count); + /* range->name is the actual version, range->evr the backwards compatibility + version. If flags are '>=' or '>', we match the compatibility version + as well, otherwise only the actual version. */ + if (!(flags & REL_GT) || (flags & REL_LT)) + return pool_intersect_evrs(pool, REL_EQ, range->name, flags, evr); + return pool_intersect_evrs(pool, REL_LT | REL_EQ, range->name, flags, evr) && + pool_intersect_evrs(pool, REL_GT | REL_EQ, range->evr, REL_EQ, evr); } - - -/*************************************************************************/ - -#if defined(MULTI_SEMANTICS) -# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE) -#elif defined(DEBIAN) -# define EVRCMP_DEPCMP EVRCMP_COMPARE -#else -# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE #endif /* check if a package's nevr matches a dependency */ /* semi-private, called from public pool_match_nevr */ - int pool_match_nevr_rel(Pool *pool, Solvable *s, Id d) { @@ -770,71 +443,6 @@ pool_match_nevr_rel(Pool *pool, Solvable *s, Id d) return 0; } -#if defined(HAIKU) || defined(MULTI_SEMANTICS) -/* forward declaration */ -static int pool_match_flags_evr_rel_compat(Pool *pool, Reldep *range, int flags, int evr); -#endif - -/* match (flags, evr) against provider (pflags, pevr) */ -static inline int -pool_match_flags_evr(Pool *pool, int pflags, Id pevr, int flags, int evr) -{ - if (!pflags || !flags || pflags >= 8 || flags >= 8) - return 0; - if (flags == 7 || pflags == 7) - return 1; /* rel provides every version */ - if ((pflags & flags & (REL_LT | REL_GT)) != 0) - return 1; /* both rels show in the same direction */ - if (pevr == evr) - return (flags & pflags & REL_EQ) ? 1 : 0; -#if defined(HAIKU) || defined(MULTI_SEMANTICS) - if (ISRELDEP(pevr)) - { - Reldep *rd = GETRELDEP(pool, pevr); - if (rd->flags == REL_COMPAT) - return pool_match_flags_evr_rel_compat(pool, rd, flags, evr); - } -#endif - switch (pool_evrcmp(pool, pevr, evr, EVRCMP_DEPCMP)) - { - case -2: - return (pflags & REL_EQ) ? 1 : 0; - case -1: - return (flags & REL_LT) || (pflags & REL_GT) ? 1 : 0; - case 0: - return (flags & pflags & REL_EQ) ? 1 : 0; - case 1: - return (flags & REL_GT) || (pflags & REL_LT) ? 1 : 0; - case 2: - return (flags & REL_EQ) ? 1 : 0; - default: - break; - } - return 0; -} - -#if defined(HAIKU) || defined(MULTI_SEMANTICS) -static int -pool_match_flags_evr_rel_compat(Pool *pool, Reldep *range, int flags, int evr) -{ - /* range->name is the actual version, range->evr the backwards compatibility - version. If flags are '>=' or '>', we match the compatibility version - as well, otherwise only the actual version. */ - if (!(flags & REL_GT) || (flags & REL_LT)) - return pool_match_flags_evr(pool, REL_EQ, range->name, flags, evr); - return pool_match_flags_evr(pool, REL_LT | REL_EQ, range->name, flags, evr) && - pool_match_flags_evr(pool, REL_GT | REL_EQ, range->evr, REL_EQ, evr); -} -#endif - -/* public (i.e. not inlined) version of pool_match_flags_evr */ -int -pool_intersect_evrs(Pool *pool, int pflags, Id pevr, int flags, Id evr) -{ - return pool_match_flags_evr(pool, pflags, pevr, flags, evr); -} - - static int is_interval_dep(Pool *pool, Id d1, Id d2) { @@ -923,549 +531,6 @@ pool_match_dep(Pool *pool, Id d1, Id d2) return pool_intersect_evrs(pool, rd1->flags, rd1->evr, rd2->flags, rd2->evr); } -Id -pool_searchlazywhatprovidesq(Pool *pool, Id d) -{ - int start = 0; - int end = pool->lazywhatprovidesq.count; - Id *elements; - if (!end) - return 0; - elements = pool->lazywhatprovidesq.elements; - while (end - start > 16) - { - int mid = (start + end) / 2 & ~1; - if (elements[mid] == d) - return elements[mid + 1]; - if (elements[mid] < d) - start = mid + 2; - else - end = mid; - } - for (; start < end; start += 2) - if (elements[start] == d) - return elements[start + 1]; - return 0; -} - -/* - * addstdproviders - * - * lazy populating of the whatprovides array, non relation case - */ -static Id -pool_addstdproviders(Pool *pool, Id d) -{ - const char *str; - Queue q; - Id qbuf[16]; - Dataiterator di; - Id oldoffset; - - if (pool->addedfileprovides == 2) - { - pool->whatprovides[d] = 1; - return 1; - } - str = pool->ss.stringspace + pool->ss.strings[d]; - if (*str != '/') - { - pool->whatprovides[d] = 1; - return 1; - } - queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf)); - dataiterator_init(&di, pool, 0, 0, SOLVABLE_FILELIST, str, SEARCH_STRING|SEARCH_FILES); - for (; dataiterator_step(&di); dataiterator_skip_solvable(&di)) - { - Solvable *s = pool->solvables + di.solvid; - /* XXX: maybe should add a provides dependency to the solvables - * OTOH this is only needed for rel deps that filter the provides, - * and those should not use filelist entries */ - if (s->repo->disabled) - continue; - if (!pool_installable_whatprovides(pool, s)) - continue; - queue_push(&q, di.solvid); - } - dataiterator_free(&di); - oldoffset = pool_searchlazywhatprovidesq(pool, d); - if (!q.count) - pool->whatprovides[d] = oldoffset ? oldoffset : 1; - else - { - if (oldoffset) - { - Id *oo = pool->whatprovidesdata + oldoffset; - int i; - /* unify both queues. easy, as we know both are sorted */ - for (i = 0; i < q.count; i++) - { - if (*oo > q.elements[i]) - continue; - if (*oo < q.elements[i]) - queue_insert(&q, i, *oo); - oo++; - if (!*oo) - break; - } - while (*oo) - queue_push(&q, *oo++); - if (q.count == oo - (pool->whatprovidesdata + oldoffset)) - { - /* end result has same size as oldoffset -> no new entries */ - queue_free(&q); - pool->whatprovides[d] = oldoffset; - return oldoffset; - } - } - pool->whatprovides[d] = pool_queuetowhatprovides(pool, &q); - } - queue_free(&q); - return pool->whatprovides[d]; -} - - -static inline int -pool_is_kind(Pool *pool, Id name, Id kind) -{ - const char *n; - if (!kind) - return 1; - n = pool_id2str(pool, name); - if (kind != 1) - { - const char *kn = pool_id2str(pool, kind); - int knl = strlen(kn); - return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0; - } - else - { - if (*n == ':') - return 1; - while(*n >= 'a' && *n <= 'z') - n++; - return *n == ':' ? 0 : 1; - } -} - -/* - * rpm eq magic: - * - * some dependencies are of the from "foo = md5sum", like the - * buildid provides. There's not much we can do to speed up - * getting the providers, because rpm's complex evr comparison - * rules get in the way. But we can use the first character(s) - * of the md5sum to do a simple pre-check. - */ -static inline int -rpmeqmagic(Pool *pool, Id evr) -{ - const char *s = pool_id2str(pool, evr); - if (*s == '0') - { - while (*s == '0' && s[1] >= '0' && s[1] <= '9') - s++; - /* ignore epoch 0 */ - if (*s == '0' && s[1] == ':') - { - s += 2; - while (*s == '0' && s[1] >= '0' && s[1] <= '9') - s++; - } - } - if (*s >= '0' && *s <= '9') - { - if (s[1] >= '0' && s[1] <= '9') - return *s + (s[1] << 8); - return *s; - } - if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z')) - { - if ((s[1] >= 'a' && s[1] <= 'z') || (s[1] >= 'A' && s[1] <= 'Z')) - return *s + (s[1] << 8); - return *s; - } - return -1; -} - -static inline int -rpmeqmagic_init(Pool *pool, int flags, Id evr) -{ - if (flags != REL_EQ || pool->disttype != DISTTYPE_RPM || pool->promoteepoch || ISRELDEP(evr)) - return -1; - else - return rpmeqmagic(pool, evr); -} - -static inline int -rpmeqmagic_cantmatch(Pool *pool, int flags, Id evr, int eqmagic) -{ - int peqmagic = rpmeqmagic(pool, evr); - if (peqmagic > 0 && peqmagic != eqmagic) - return 1; - return 0; -} - -/* - * addrelproviders - * - * add packages fulfilling the relation to whatprovides array - * - * some words about REL_AND and REL_IF: we assume the best case - * here, so that you get a "potential" result if you ask for a match. - * E.g. if you ask for "whatrequires A" and package X contains - * "Requires: A & B", you'll get "X" as an answer. - */ -Id -pool_addrelproviders(Pool *pool, Id d) -{ - Reldep *rd; - Reldep *prd; - Queue plist; - Id buf[16]; - Id name, evr, flags; - Id pid, *pidp; - Id p, *pp; - - if (!ISRELDEP(d)) - return pool_addstdproviders(pool, d); - rd = GETRELDEP(pool, d); - name = rd->name; - evr = rd->evr; - flags = rd->flags; - d = GETRELID(d); - queue_init_buffer(&plist, buf, sizeof(buf)/sizeof(*buf)); - - if (flags >= 8) - { - /* special relation */ - Id wp = 0; - Id *pp2, *pp3; - - switch (flags) - { - case REL_WITH: - wp = pool_whatprovides(pool, name); - pp2 = pool_whatprovides_ptr(pool, evr); - pp = pool->whatprovidesdata + wp; - while ((p = *pp++) != 0) - { - for (pp3 = pp2; *pp3; pp3++) - if (*pp3 == p) - break; - if (*pp3) - queue_push(&plist, p); /* found it */ - else - wp = 0; - } - break; - case REL_WITHOUT: - wp = pool_whatprovides(pool, name); - pp2 = pool_whatprovides_ptr(pool, evr); - pp = pool->whatprovidesdata + wp; - while ((p = *pp++) != 0) - { - for (pp3 = pp2; *pp3; pp3++) - if (*pp3 == p) - break; - if (!*pp3) - queue_push(&plist, p); /* use it */ - else - wp = 0; - } - break; - case REL_AND: - case REL_OR: - case REL_COND: - case REL_UNLESS: - if (flags == REL_COND || flags == REL_UNLESS) - { - if (ISRELDEP(evr)) - { - Reldep *rd2 = GETRELDEP(pool, evr); - evr = rd2->flags == REL_ELSE ? rd2->evr : 0; - } - else - evr = 0; /* assume cond is true */ - } - wp = pool_whatprovides(pool, name); - if (!pool->whatprovidesdata[wp]) - wp = evr ? pool_whatprovides(pool, evr) : 1; - else if (evr) - { - /* sorted merge */ - pp2 = pool_whatprovides_ptr(pool, evr); - pp = pool->whatprovidesdata + wp; - while (*pp && *pp2) - { - if (*pp < *pp2) - queue_push(&plist, *pp++); - else - { - if (*pp == *pp2) - pp++; - queue_push(&plist, *pp2++); - } - } - while (*pp) - queue_push(&plist, *pp++); - while (*pp2) - queue_push(&plist, *pp2++); - /* if the number of elements did not change, we can reuse wp */ - if (pp - (pool->whatprovidesdata + wp) != plist.count) - wp = 0; - } - break; - - case REL_NAMESPACE: - if (name == NAMESPACE_OTHERPROVIDERS) - { - wp = pool_whatprovides(pool, evr); - break; - } - if (pool->nscallback) - { - /* ask callback which packages provide the dependency - * 0: none - * 1: the system (aka SYSTEMSOLVABLE) - * >1: set of packages, stored as offset on whatprovidesdata - */ - p = pool->nscallback(pool, pool->nscallbackdata, name, evr); - if (p > 1) - wp = p; - if (p == 1) - queue_push(&plist, SYSTEMSOLVABLE); - } - break; - case REL_ARCH: - /* small hack: make it possible to match .src - * we have to iterate over the solvables as src packages do not - * provide anything, thus they are not indexed in our - * whatprovides hash */ - if (evr == ARCH_SRC || evr == ARCH_NOSRC) - { - Solvable *s; - for (p = 1, s = pool->solvables + p; p < pool->nsolvables; p++, s++) - { - if (!s->repo) - continue; - if (s->arch != evr && s->arch != ARCH_NOSRC) - continue; - if (pool_disabled_solvable(pool, s)) - continue; - if (!name || pool_match_nevr(pool, s, name)) - queue_push(&plist, p); - } - break; - } - if (!name) - { - FOR_POOL_SOLVABLES(p) - { - Solvable *s = pool->solvables + p; - if (!pool_installable_whatprovides(pool, s)) - continue; - if (s->arch == evr) - queue_push(&plist, p); - } - break; - } - wp = pool_whatprovides(pool, name); - pp = pool->whatprovidesdata + wp; - while ((p = *pp++) != 0) - { - Solvable *s = pool->solvables + p; - if (s->arch == evr) - queue_push(&plist, p); - else - wp = 0; - } - break; - case REL_MULTIARCH: - if (evr != ARCH_ANY) - break; - /* XXX : need to check for Multi-Arch: allowed! */ - wp = pool_whatprovides(pool, name); - break; - case REL_KIND: - /* package kind filtering */ - if (!name) - { - FOR_POOL_SOLVABLES(p) - { - Solvable *s = pool->solvables + p; - if (s->repo != pool->installed && !pool_installable(pool, s)) - continue; - if (pool_is_kind(pool, s->name, evr)) - queue_push(&plist, p); - } - break; - } - wp = pool_whatprovides(pool, name); - pp = pool->whatprovidesdata + wp; - while ((p = *pp++) != 0) - { - Solvable *s = pool->solvables + p; - if (pool_is_kind(pool, s->name, evr)) - queue_push(&plist, p); - else - wp = 0; - } - break; - case REL_FILECONFLICT: - pp = pool_whatprovides_ptr(pool, name); - while ((p = *pp++) != 0) - { - Id origd = MAKERELDEP(d); - Solvable *s = pool->solvables + p; - if (!s->provides) - continue; - pidp = s->repo->idarraydata + s->provides; - while ((pid = *pidp++) != 0) - if (pid == origd) - break; - if (pid) - queue_push(&plist, p); - } - break; -#ifdef ENABLE_CONDA - case REL_CONDA: - wp = pool_addrelproviders_conda(pool, name, evr, &plist); - break; -#endif - default: - break; - } - if (wp) - { - /* we can reuse an existing entry */ - queue_free(&plist); - pool->whatprovides_rel[d] = wp; - return wp; - } - } - else if (flags) - { - Id *ppaux = 0; - int eqmagic = 0; - /* simple version comparison relation */ -#if 0 - POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: what provides %s?\n", pool_dep2str(pool, name)); -#endif - pp = pool_whatprovides_ptr(pool, name); - if (!ISRELDEP(name) && (Offset)name < pool->whatprovidesauxoff) - ppaux = pool->whatprovidesaux[name] ? pool->whatprovidesauxdata + pool->whatprovidesaux[name] : 0; - while (ISRELDEP(name)) - { - rd = GETRELDEP(pool, name); - name = rd->name; - } - while ((p = *pp++) != 0) - { - Solvable *s = pool->solvables + p; - if (ppaux) - { - pid = *ppaux++; - if (pid && pid != 1) - { -#if 0 - POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: aux hit %d %s\n", p, pool_dep2str(pool, pid)); -#endif - if (!ISRELDEP(pid)) - { - if (pid != name) - continue; /* wrong provides name */ - if (pool->disttype == DISTTYPE_DEB) - continue; /* unversioned provides can never match versioned deps */ - } - else - { - prd = GETRELDEP(pool, pid); - if (prd->name != name) - continue; /* wrong provides name */ - if (!eqmagic) - eqmagic = rpmeqmagic_init(pool, flags, evr); - if (eqmagic > 0 && prd->flags == REL_EQ && !ISRELDEP(prd->evr) && - rpmeqmagic_cantmatch(pool, prd->flags, prd->evr, eqmagic)) - continue; - /* right package, both deps are rels. check flags/evr */ - if (!pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr)) - continue; - } - queue_push(&plist, p); - continue; - } - } - if (!s->provides || s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) - { - /* no provides or src rpm - check nevr */ - if (pool_match_nevr_rel(pool, s, MAKERELDEP(d))) - queue_push(&plist, p); - continue; - } - /* solvable p provides name in some rels */ - if (!eqmagic) - eqmagic = rpmeqmagic_init(pool, flags, evr); - pidp = s->repo->idarraydata + s->provides; - while ((pid = *pidp++) != 0) - { - if (!ISRELDEP(pid)) - { - if (pid != name) - continue; /* wrong provides name */ - if (pool->disttype == DISTTYPE_DEB) - continue; /* unversioned provides can never match versioned deps */ - break; - } - prd = GETRELDEP(pool, pid); - if (prd->name != name) - continue; /* wrong provides name */ - /* right package, both deps are rels. check flags/evr */ - if (eqmagic > 0 && prd->flags == REL_EQ && !ISRELDEP(prd->evr) && - rpmeqmagic_cantmatch(pool, prd->flags, prd->evr, eqmagic)) - continue; - if (pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr)) - break; /* matches */ - } - if (!pid) - continue; /* none of the providers matched */ - queue_push(&plist, p); - } - /* make our system solvable provide all unknown rpmlib() stuff */ - if (plist.count == 0 && !strncmp(pool_id2str(pool, name), "rpmlib(", 7)) - queue_push(&plist, SYSTEMSOLVABLE); - } - /* add providers to whatprovides */ -#if 0 - POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: adding %d packages to %d\n", plist.count, d); -#endif - pool->whatprovides_rel[d] = pool_queuetowhatprovides(pool, &plist); - queue_free(&plist); - - return pool->whatprovides_rel[d]; -} - -void -pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr) -{ - int nrels = pool->nrels; - Id d; - Reldep *rd; - - if (!pool->whatprovides_rel) - return; - for (d = 1, rd = pool->rels + d; d < nrels; d++, rd++) - { - if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS) - continue; - if (ns && rd->name != ns) - continue; - if (evr && rd->evr != evr) - continue; - if (pool->whatprovides_rel[d]) - pool_set_whatprovides(pool, MAKERELDEP(d), 0); - } -} /* intersect dependencies in keyname with dep, return list of matching packages */ void @@ -1950,81 +1015,6 @@ pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp) return loc; } -void -pool_set_whatprovides(Pool *pool, Id id, Id providers) -{ - int d, nrels = pool->nrels; - Reldep *rd; - Map m; - - /* set new entry */ - if (ISRELDEP(id)) - { - d = GETRELID(id); - pool->whatprovides_rel[d] = providers; - d++; - } - else - { - pool->whatprovides[id] = providers; - if ((Offset)id < pool->whatprovidesauxoff) - pool->whatprovidesaux[id] = 0; /* sorry */ - d = 1; - } - if (!pool->whatprovides_rel) - return; - /* clear cache of all rels that use it */ - map_init(&m, 0); - for (rd = pool->rels + d; d < nrels; d++, rd++) - { - if (rd->name == id || rd->evr == id || - (m.size && ISRELDEP(rd->name) && MAPTST(&m, GETRELID(rd->name))) || - (m.size && ISRELDEP(rd->evr) && MAPTST(&m, GETRELID(rd->evr)))) - { - pool->whatprovides_rel[d] = 0; /* clear cache */ - if (!m.size) - map_init(&m, nrels); - MAPSET(&m, d); - } - } - map_free(&m); -} - -static void -add_new_provider(Pool *pool, Id id, Id p) -{ - Queue q; - Id *pp; - - /* find whatprovides entry */ - while (ISRELDEP(id)) - { - Reldep *rd = GETRELDEP(pool, id); - id = rd->name; - } - - /* add new provider to existing list keeping it sorted */ - queue_init(&q); - for (pp = pool->whatprovidesdata + pool->whatprovides[id]; *pp; pp++) - { - if (*pp == p) - { - queue_free(&q); /* already have it */ - return; - } - if (*pp > p) - { - queue_push(&q, p); - p = 0; - } - queue_push(&q, *pp); - } - if (p) - queue_push(&q, p); - pool_set_whatprovides(pool, id, pool_queuetowhatprovides(pool, &q)); - queue_free(&q); -} - void pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts) { @@ -2048,7 +1038,7 @@ pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts) continue; s->provides = repo_addid_dep(s->repo, s->provides, id, SOLVABLE_FILEMARKER); if (pool->whatprovides) - add_new_provider(pool, id, p); + pool_add_new_provider(pool, id, p); s = pool->solvables + q; if (!s->repo) continue; diff --git a/src/pool.h b/src/pool.h index d8c938e8..ccd21a3f 100644 --- a/src/pool.h +++ b/src/pool.h @@ -357,6 +357,7 @@ void pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker); void pool_whatcontainsdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker); void pool_whatmatchessolvable(Pool *pool, Id keyname, Id solvid, Queue *q, int marker); void pool_set_whatprovides(Pool *pool, Id id, Id providers); +void pool_add_new_provider(Pool *pool, Id id, Id p); /* search the pool. the following filters are available: diff --git a/src/poolwhatprovides.c b/src/poolwhatprovides.c new file mode 100644 index 00000000..2685bb29 --- /dev/null +++ b/src/poolwhatprovides.c @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 2025, SUSE LLC + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * poolwhatprovides.c + * + * dependency provider lookup functions + */ + +#include +#include +#include +#include +#include +#include + +#include "pool.h" +#include "poolid_private.h" +#include "repo.h" +#include "util.h" +#include "evr.h" +#ifdef ENABLE_CONDA +#include "conda.h" +#endif + +static int +pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp, void *dp) +{ + int r; + Pool *pool = dp; + Id oa, ob, *da, *db; + oa = pool->whatprovides[*(Id *)ap]; + ob = pool->whatprovides[*(Id *)bp]; + if (oa == ob) + return *(Id *)ap - *(Id *)bp; + da = pool->whatprovidesdata + oa; + db = pool->whatprovidesdata + ob; + while (*db) + if ((r = (*da++ - *db++)) != 0) + return r; + if (*da) + return *da; + return *(Id *)ap - *(Id *)bp; +} + +/* + * pool_shrink_whatprovides - unify whatprovides data + * + * whatprovides_rel must be empty for this to work! + * + */ +static void +pool_shrink_whatprovides(Pool *pool) +{ + Id i, n, id; + Id *sorted; + Id lastid, *last, *dp, *lp; + Offset o; + int r; + + if (pool->ss.nstrings < 3) + return; + sorted = solv_malloc2(pool->ss.nstrings, sizeof(Id)); + for (i = id = 0; id < pool->ss.nstrings; id++) + if (pool->whatprovides[id] >= 4) + sorted[i++] = id; + n = i; + solv_sort(sorted, n, sizeof(Id), pool_shrink_whatprovides_sortcmp, pool); + last = 0; + lastid = 0; + for (i = 0; i < n; i++) + { + id = sorted[i]; + o = pool->whatprovides[id]; + dp = pool->whatprovidesdata + o; + if (last) + { + lp = last; + while (*dp) + if (*dp++ != *lp++) + { + last = 0; + break; + } + if (last && *lp) + last = 0; + if (last) + { + pool->whatprovides[id] = -lastid; + continue; + } + } + last = pool->whatprovidesdata + o; + lastid = id; + } + solv_free(sorted); + dp = pool->whatprovidesdata + 4; + for (id = 1; id < pool->ss.nstrings; id++) + { + o = pool->whatprovides[id]; + if (!o) + continue; + if ((Id)o < 0) + { + i = -(Id)o; + if (i >= id) + abort(); + pool->whatprovides[id] = pool->whatprovides[i]; + continue; + } + if (o < 4) + continue; + lp = pool->whatprovidesdata + o; + if (lp < dp) + abort(); + pool->whatprovides[id] = dp - pool->whatprovidesdata; + while ((*dp++ = *lp++) != 0) + ; + } + o = dp - pool->whatprovidesdata; + POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesdata from %d to %d\n", pool->whatprovidesdataoff, o); + if (pool->whatprovidesdataoff == o) + return; + r = pool->whatprovidesdataoff - o; + pool->whatprovidesdataoff = o; + pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id)); + if (r > pool->whatprovidesdataleft) + r = pool->whatprovidesdataleft; + memset(pool->whatprovidesdata + o, 0, r * sizeof(Id)); +} + +/* this gets rid of all the zeros in the aux */ +static void +pool_shrink_whatprovidesaux(Pool *pool) +{ + int num = pool->whatprovidesauxoff; + Id id; + Offset newoff; + Id *op, *wp = pool->whatprovidesauxdata + 1; + int i; + + for (i = 0; i < num; i++) + { + Offset o = pool->whatprovidesaux[i]; + if (o < 2) + continue; + op = pool->whatprovidesauxdata + o; + pool->whatprovidesaux[i] = wp - pool->whatprovidesauxdata; + if (op < wp) + abort(); + while ((id = *op++) != 0) + *wp++ = id; + } + newoff = wp - pool->whatprovidesauxdata; + pool->whatprovidesauxdata = solv_realloc(pool->whatprovidesauxdata, newoff * sizeof(Id)); + POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesauxdata from %d to %d\n", pool->whatprovidesauxdataoff, newoff); + pool->whatprovidesauxdataoff = newoff; +} + + +/* + * pool_createwhatprovides() + * + * create hashes over pool of solvables to ease provide lookups + * + */ +void +pool_createwhatprovides(Pool *pool) +{ + int i, num, np, extra; + Offset off; + Solvable *s; + Id id; + Offset *idp, n; + Offset *whatprovides; + Id *whatprovidesdata, *dp, *whatprovidesauxdata; + Offset *whatprovidesaux; + unsigned int now; + + now = solv_timems(0); + POOL_DEBUG(SOLV_DEBUG_STATS, "number of solvables: %d, memory used: %d K\n", pool->nsolvables, pool->nsolvables * (int)sizeof(Solvable) / 1024); + POOL_DEBUG(SOLV_DEBUG_STATS, "number of ids: %d + %d\n", pool->ss.nstrings, pool->nrels); + POOL_DEBUG(SOLV_DEBUG_STATS, "string memory used: %d K array + %d K data, rel memory used: %d K array\n", pool->ss.nstrings / (1024 / (int)sizeof(Id)), pool->ss.sstrings / 1024, pool->nrels * (int)sizeof(Reldep) / 1024); + if (pool->ss.stringhashmask || pool->relhashmask) + POOL_DEBUG(SOLV_DEBUG_STATS, "string hash memory: %d K, rel hash memory : %d K\n", (pool->ss.stringhashmask + 1) / (int)(1024/sizeof(Id)), (pool->relhashmask + 1) / (int)(1024/sizeof(Id))); + + pool_freeidhashes(pool); /* XXX: should not be here! */ + pool_freewhatprovides(pool); + num = pool->ss.nstrings; + pool->whatprovides = whatprovides = solv_calloc_block(num, sizeof(Offset), WHATPROVIDES_BLOCK); + pool->whatprovides_rel = solv_calloc_block(pool->nrels, sizeof(Offset), WHATPROVIDES_BLOCK); + + /* count providers for each name */ + for (i = pool->nsolvables - 1; i > 0; i--) + { + Id *pp; + s = pool->solvables + i; + if (!s->provides || !s->repo || s->repo->disabled) + continue; + if (!pool_installable_whatprovides(pool, s)) + continue; + pp = s->repo->idarraydata + s->provides; + while ((id = *pp++) != 0) + { + while (ISRELDEP(id)) + { + Reldep *rd = GETRELDEP(pool, id); + id = rd->name; + } + whatprovides[id]++; /* inc count of providers */ + } + } + + off = 4; /* first entry is undef, second is empty list, third is system solvable */ + np = 0; /* number of names provided */ + for (i = 0, idp = whatprovides; i < num; i++, idp++) + { + n = *idp; + if (!n) /* no providers */ + { + *idp = 1; /* offset for empty list */ + continue; + } + off += n; /* make space for all providers */ + *idp = off++; /* now idp points to terminating zero */ + np++; /* inc # of provider 'slots' for stats */ + } + + POOL_DEBUG(SOLV_DEBUG_STATS, "provide ids: %d\n", np); + + /* reserve some space for relation data */ + extra = 2 * pool->nrels; + if (extra < 256) + extra = 256; + + POOL_DEBUG(SOLV_DEBUG_STATS, "provide space needed: %d + %d\n", off, extra); + + /* alloc space for all providers + extra */ + whatprovidesdata = solv_calloc(off + extra, sizeof(Id)); + whatprovidesdata[2] = SYSTEMSOLVABLE; + + /* alloc aux vector */ + whatprovidesauxdata = 0; + if (!pool->nowhatprovidesaux) + { + pool->whatprovidesaux = whatprovidesaux = solv_calloc(num, sizeof(Offset)); + pool->whatprovidesauxoff = num; + pool->whatprovidesauxdataoff = off; + pool->whatprovidesauxdata = whatprovidesauxdata = solv_calloc(pool->whatprovidesauxdataoff, sizeof(Id)); + } + + /* now fill data for all provides */ + for (i = pool->nsolvables - 1; i > 0; i--) + { + Id *pp; + s = pool->solvables + i; + if (!s->provides || !s->repo || s->repo->disabled) + continue; + if (!pool_installable_whatprovides(pool, s)) + continue; + /* for all provides of this solvable */ + pp = s->repo->idarraydata + s->provides; + while ((id = *pp++) != 0) + { + Id auxid = id; + while (ISRELDEP(id)) + { + Reldep *rd = GETRELDEP(pool, id); + id = rd->name; + } + dp = whatprovidesdata + whatprovides[id]; /* offset into whatprovidesdata */ + if (*dp != i) /* don't add same solvable twice */ + { + dp[-1] = i; + whatprovides[id]--; + } + else + auxid = 1; + if (whatprovidesauxdata) + whatprovidesauxdata[whatprovides[id]] = auxid; + } + } + if (pool->whatprovidesaux) + memcpy(pool->whatprovidesaux, pool->whatprovides, num * sizeof(Id)); + pool->whatprovidesdata = whatprovidesdata; + pool->whatprovidesdataoff = off; + pool->whatprovidesdataleft = extra; + pool_shrink_whatprovides(pool); + if (pool->whatprovidesaux) + pool_shrink_whatprovidesaux(pool); + POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovides memory used: %d K id array, %d K data\n", (pool->ss.nstrings + pool->nrels + WHATPROVIDES_BLOCK) / (int)(1024/sizeof(Id)), (pool->whatprovidesdataoff + pool->whatprovidesdataleft) / (int)(1024/sizeof(Id))); + if (pool->whatprovidesaux) + POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovidesaux memory used: %d K id array, %d K data\n", pool->whatprovidesauxoff / (int)(1024/sizeof(Id)), pool->whatprovidesauxdataoff / (int)(1024/sizeof(Id))); + + queue_empty(&pool->lazywhatprovidesq); + if (pool->addedfileprovides == 1) + { + /* lazyly add file provides for nonstd */ + for (i = 0; i < pool->nonstd_nids; i++) + { + Id id = pool->nonstd_ids[i]; + const char *str = pool->ss.stringspace + pool->ss.strings[id]; + if (str[0] != '/') /* just in case, all entries should start with '/' */ + continue; + /* setup lazy adding, but remember old value */ + if (pool->whatprovides[id] > 1) + queue_push2(&pool->lazywhatprovidesq, id, pool->whatprovides[id]); + pool->whatprovides[id] = 0; + if (pool->whatprovidesaux) + pool->whatprovidesaux[id] = 0; /* sorry */ + } + } + else if (!pool->addedfileprovides && pool->disttype == DISTTYPE_RPM) + { + POOL_DEBUG(SOLV_DEBUG_STATS, "WARNING: pool_addfileprovides was not called, this may result in slow operation\n"); + /* lazyly add file provides */ + for (i = 1; i < num; i++) + { + const char *str = pool->ss.stringspace + pool->ss.strings[i]; + if (str[0] != '/') + continue; + if (pool->addedfileprovides == 1 && repodata_filelistfilter_matches(0, str)) + continue; + /* setup lazy adding, but remember old value */ + if (pool->whatprovides[i] > 1) + queue_push2(&pool->lazywhatprovidesq, i, pool->whatprovides[i]); + pool->whatprovides[i] = 0; + if (pool->whatprovidesaux) + pool->whatprovidesaux[i] = 0; /* sorry */ + } + } + if (pool->lazywhatprovidesq.count) + POOL_DEBUG(SOLV_DEBUG_STATS, "lazywhatprovidesq size: %d entries\n", pool->lazywhatprovidesq.count / 2); + + POOL_DEBUG(SOLV_DEBUG_STATS, "createwhatprovides took %d ms\n", solv_timems(now)); +} + +/* + * free all of our whatprovides data + * be careful, everything internalized with pool_queuetowhatprovides is + * gone, too + */ +void +pool_freewhatprovides(Pool *pool) +{ + pool->whatprovides = solv_free(pool->whatprovides); + pool->whatprovides_rel = solv_free(pool->whatprovides_rel); + pool->whatprovidesdata = solv_free(pool->whatprovidesdata); + pool->whatprovidesdataoff = 0; + pool->whatprovidesdataleft = 0; + pool->whatprovidesaux = solv_free(pool->whatprovidesaux); + pool->whatprovidesauxdata = solv_free(pool->whatprovidesauxdata); + pool->whatprovidesauxoff = 0; + pool->whatprovidesauxdataoff = 0; +} + + +/******************************************************************************/ + +/* + * pool_queuetowhatprovides - add queue contents to whatprovidesdata + * + * used for whatprovides, jobs, learnt rules, selections + * input: q: queue of Ids + * returns: Offset into whatprovidesdata + * + */ + +Id +pool_ids2whatprovides(Pool *pool, Id *ids, int count) +{ + Offset off; + + if (count == 0) /* queue empty -> 1 */ + return 1; + if (count == 1 && *ids == SYSTEMSOLVABLE) + return 2; + + /* extend whatprovidesdata if needed, +1 for 0-termination */ + if (pool->whatprovidesdataleft < count + 1) + { + POOL_DEBUG(SOLV_DEBUG_STATS, "growing provides hash data...\n"); + pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (pool->whatprovidesdataoff + count + 4096) * sizeof(Id)); + pool->whatprovidesdataleft = count + 4096; + } + + /* copy queue to next free slot */ + off = pool->whatprovidesdataoff; + memcpy(pool->whatprovidesdata + pool->whatprovidesdataoff, ids, count * sizeof(Id)); + + /* adapt count and 0-terminate */ + pool->whatprovidesdataoff += count; + pool->whatprovidesdata[pool->whatprovidesdataoff++] = 0; + pool->whatprovidesdataleft -= count + 1; + + return (Id)off; +} + +Id +pool_queuetowhatprovides(Pool *pool, Queue *q) +{ + int count = q->count; + if (count == 0) /* queue empty -> 1 */ + return 1; + if (count == 1 && q->elements[0] == SYSTEMSOLVABLE) + return 2; + return pool_ids2whatprovides(pool, q->elements, count); +} + + +Id +pool_searchlazywhatprovidesq(Pool *pool, Id d) +{ + int start = 0; + int end = pool->lazywhatprovidesq.count; + Id *elements; + if (!end) + return 0; + elements = pool->lazywhatprovidesq.elements; + while (end - start > 16) + { + int mid = (start + end) / 2 & ~1; + if (elements[mid] == d) + return elements[mid + 1]; + if (elements[mid] < d) + start = mid + 2; + else + end = mid; + } + for (; start < end; start += 2) + if (elements[start] == d) + return elements[start + 1]; + return 0; +} + +/* + * addstdproviders + * + * lazy populating of the whatprovides array, non relation case + */ +static Id +pool_addstdproviders(Pool *pool, Id d) +{ + const char *str; + Queue q; + Id qbuf[16]; + Dataiterator di; + Id oldoffset; + + if (pool->addedfileprovides == 2) + { + pool->whatprovides[d] = 1; + return 1; + } + str = pool->ss.stringspace + pool->ss.strings[d]; + if (*str != '/') + { + pool->whatprovides[d] = 1; + return 1; + } + queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf)); + dataiterator_init(&di, pool, 0, 0, SOLVABLE_FILELIST, str, SEARCH_STRING|SEARCH_FILES); + for (; dataiterator_step(&di); dataiterator_skip_solvable(&di)) + { + Solvable *s = pool->solvables + di.solvid; + /* XXX: maybe should add a provides dependency to the solvables + * OTOH this is only needed for rel deps that filter the provides, + * and those should not use filelist entries */ + if (s->repo->disabled) + continue; + if (!pool_installable_whatprovides(pool, s)) + continue; + queue_push(&q, di.solvid); + } + dataiterator_free(&di); + oldoffset = pool_searchlazywhatprovidesq(pool, d); + if (!q.count) + pool->whatprovides[d] = oldoffset ? oldoffset : 1; + else + { + if (oldoffset) + { + Id *oo = pool->whatprovidesdata + oldoffset; + int i; + /* unify both queues. easy, as we know both are sorted */ + for (i = 0; i < q.count; i++) + { + if (*oo > q.elements[i]) + continue; + if (*oo < q.elements[i]) + queue_insert(&q, i, *oo); + oo++; + if (!*oo) + break; + } + while (*oo) + queue_push(&q, *oo++); + if (q.count == oo - (pool->whatprovidesdata + oldoffset)) + { + /* end result has same size as oldoffset -> no new entries */ + queue_free(&q); + pool->whatprovides[d] = oldoffset; + return oldoffset; + } + } + pool->whatprovides[d] = pool_queuetowhatprovides(pool, &q); + } + queue_free(&q); + return pool->whatprovides[d]; +} + + +static inline int +pool_is_kind(Pool *pool, Id name, Id kind) +{ + const char *n; + if (!kind) + return 1; + n = pool_id2str(pool, name); + if (kind != 1) + { + const char *kn = pool_id2str(pool, kind); + int knl = strlen(kn); + return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0; + } + else + { + if (*n == ':') + return 1; + while(*n >= 'a' && *n <= 'z') + n++; + return *n == ':' ? 0 : 1; + } +} + +/* + * rpm eq magic: + * + * some dependencies are of the from "foo = md5sum", like the + * buildid provides. There's not much we can do to speed up + * getting the providers, because rpm's complex evr comparison + * rules get in the way. But we can use the first character(s) + * of the md5sum to do a simple pre-check. + */ +static inline int +rpmeqmagic(Pool *pool, Id evr) +{ + const char *s = pool_id2str(pool, evr); + if (*s == '0') + { + while (*s == '0' && s[1] >= '0' && s[1] <= '9') + s++; + /* ignore epoch 0 */ + if (*s == '0' && s[1] == ':') + { + s += 2; + while (*s == '0' && s[1] >= '0' && s[1] <= '9') + s++; + } + } + if (*s >= '0' && *s <= '9') + { + if (s[1] >= '0' && s[1] <= '9') + return *s + (s[1] << 8); + return *s; + } + if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z')) + { + if ((s[1] >= 'a' && s[1] <= 'z') || (s[1] >= 'A' && s[1] <= 'Z')) + return *s + (s[1] << 8); + return *s; + } + return -1; +} + +static inline int +rpmeqmagic_init(Pool *pool, int flags, Id evr) +{ + if (flags != REL_EQ || pool->disttype != DISTTYPE_RPM || pool->promoteepoch || ISRELDEP(evr)) + return -1; + else + return rpmeqmagic(pool, evr); +} + +static inline int +rpmeqmagic_cantmatch(Pool *pool, int flags, Id evr, int eqmagic) +{ + int peqmagic = rpmeqmagic(pool, evr); + if (peqmagic > 0 && peqmagic != eqmagic) + return 1; + return 0; +} + + +#if defined(MULTI_SEMANTICS) +# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE) +#elif defined(DEBIAN) +# define EVRCMP_DEPCMP EVRCMP_COMPARE +#else +# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE +#endif + +/* match (flags, evr) against provider (pflags, pevr) */ +/* this copies parts of pool_intersect_evrs for speed */ +static inline int +pool_match_flags_evr(Pool *pool, int pflags, Id pevr, int flags, int evr) +{ + if (!pflags || !flags || pflags >= 8 || flags >= 8) + return 0; + if (flags == 7 || pflags == 7) + return 1; /* rel provides every version */ + if ((pflags & flags & (REL_LT | REL_GT)) != 0) + return 1; /* both rels show in the same direction */ + if (pevr == evr) + return (flags & pflags & REL_EQ) ? 1 : 0; +#if defined(HAIKU) || defined(MULTI_SEMANTICS) + if (ISRELDEP(pevr)) + return pool_intersect_evrs(pool, pflags, pevr, flags, evr); /* too complex */ +#endif + switch (pool_evrcmp(pool, pevr, evr, EVRCMP_DEPCMP)) + { + case -2: + return (pflags & REL_EQ) ? 1 : 0; + case -1: + return (flags & REL_LT) || (pflags & REL_GT) ? 1 : 0; + case 0: + return (flags & pflags & REL_EQ) ? 1 : 0; + case 1: + return (flags & REL_GT) || (pflags & REL_LT) ? 1 : 0; + case 2: + return (flags & REL_EQ) ? 1 : 0; + default: + break; + } + return 0; +} + +/* + * addrelproviders + * + * add packages fulfilling the relation to whatprovides array + * + * some words about REL_AND and REL_IF: we assume the best case + * here, so that you get a "potential" result if you ask for a match. + * E.g. if you ask for "whatrequires A" and package X contains + * "Requires: A & B", you'll get "X" as an answer. + */ +Id +pool_addrelproviders(Pool *pool, Id d) +{ + Reldep *rd; + Reldep *prd; + Queue plist; + Id buf[16]; + Id name, evr, flags; + Id pid, *pidp; + Id p, *pp; + + if (!ISRELDEP(d)) + return pool_addstdproviders(pool, d); + rd = GETRELDEP(pool, d); + name = rd->name; + evr = rd->evr; + flags = rd->flags; + d = GETRELID(d); + queue_init_buffer(&plist, buf, sizeof(buf)/sizeof(*buf)); + + if (flags >= 8) + { + /* special relation */ + Id wp = 0; + Id *pp2, *pp3; + + switch (flags) + { + case REL_WITH: + wp = pool_whatprovides(pool, name); + pp2 = pool_whatprovides_ptr(pool, evr); + pp = pool->whatprovidesdata + wp; + while ((p = *pp++) != 0) + { + for (pp3 = pp2; *pp3; pp3++) + if (*pp3 == p) + break; + if (*pp3) + queue_push(&plist, p); /* found it */ + else + wp = 0; + } + break; + case REL_WITHOUT: + wp = pool_whatprovides(pool, name); + pp2 = pool_whatprovides_ptr(pool, evr); + pp = pool->whatprovidesdata + wp; + while ((p = *pp++) != 0) + { + for (pp3 = pp2; *pp3; pp3++) + if (*pp3 == p) + break; + if (!*pp3) + queue_push(&plist, p); /* use it */ + else + wp = 0; + } + break; + case REL_AND: + case REL_OR: + case REL_COND: + case REL_UNLESS: + if (flags == REL_COND || flags == REL_UNLESS) + { + if (ISRELDEP(evr)) + { + Reldep *rd2 = GETRELDEP(pool, evr); + evr = rd2->flags == REL_ELSE ? rd2->evr : 0; + } + else + evr = 0; /* assume cond is true */ + } + wp = pool_whatprovides(pool, name); + if (!pool->whatprovidesdata[wp]) + wp = evr ? pool_whatprovides(pool, evr) : 1; + else if (evr) + { + /* sorted merge */ + pp2 = pool_whatprovides_ptr(pool, evr); + pp = pool->whatprovidesdata + wp; + while (*pp && *pp2) + { + if (*pp < *pp2) + queue_push(&plist, *pp++); + else + { + if (*pp == *pp2) + pp++; + queue_push(&plist, *pp2++); + } + } + while (*pp) + queue_push(&plist, *pp++); + while (*pp2) + queue_push(&plist, *pp2++); + /* if the number of elements did not change, we can reuse wp */ + if (pp - (pool->whatprovidesdata + wp) != plist.count) + wp = 0; + } + break; + + case REL_NAMESPACE: + if (name == NAMESPACE_OTHERPROVIDERS) + { + wp = pool_whatprovides(pool, evr); + break; + } + if (pool->nscallback) + { + /* ask callback which packages provide the dependency + * 0: none + * 1: the system (aka SYSTEMSOLVABLE) + * >1: set of packages, stored as offset on whatprovidesdata + */ + p = pool->nscallback(pool, pool->nscallbackdata, name, evr); + if (p > 1) + wp = p; + if (p == 1) + queue_push(&plist, SYSTEMSOLVABLE); + } + break; + case REL_ARCH: + /* small hack: make it possible to match .src + * we have to iterate over the solvables as src packages do not + * provide anything, thus they are not indexed in our + * whatprovides hash */ + if (evr == ARCH_SRC || evr == ARCH_NOSRC) + { + Solvable *s; + for (p = 1, s = pool->solvables + p; p < pool->nsolvables; p++, s++) + { + if (!s->repo) + continue; + if (s->arch != evr && s->arch != ARCH_NOSRC) + continue; + if (pool_disabled_solvable(pool, s)) + continue; + if (!name || pool_match_nevr(pool, s, name)) + queue_push(&plist, p); + } + break; + } + if (!name) + { + FOR_POOL_SOLVABLES(p) + { + Solvable *s = pool->solvables + p; + if (!pool_installable_whatprovides(pool, s)) + continue; + if (s->arch == evr) + queue_push(&plist, p); + } + break; + } + wp = pool_whatprovides(pool, name); + pp = pool->whatprovidesdata + wp; + while ((p = *pp++) != 0) + { + Solvable *s = pool->solvables + p; + if (s->arch == evr) + queue_push(&plist, p); + else + wp = 0; + } + break; + case REL_MULTIARCH: + if (evr != ARCH_ANY) + break; + /* XXX : need to check for Multi-Arch: allowed! */ + wp = pool_whatprovides(pool, name); + break; + case REL_KIND: + /* package kind filtering */ + if (!name) + { + FOR_POOL_SOLVABLES(p) + { + Solvable *s = pool->solvables + p; + if (s->repo != pool->installed && !pool_installable(pool, s)) + continue; + if (pool_is_kind(pool, s->name, evr)) + queue_push(&plist, p); + } + break; + } + wp = pool_whatprovides(pool, name); + pp = pool->whatprovidesdata + wp; + while ((p = *pp++) != 0) + { + Solvable *s = pool->solvables + p; + if (pool_is_kind(pool, s->name, evr)) + queue_push(&plist, p); + else + wp = 0; + } + break; + case REL_FILECONFLICT: + pp = pool_whatprovides_ptr(pool, name); + while ((p = *pp++) != 0) + { + Id origd = MAKERELDEP(d); + Solvable *s = pool->solvables + p; + if (!s->provides) + continue; + pidp = s->repo->idarraydata + s->provides; + while ((pid = *pidp++) != 0) + if (pid == origd) + break; + if (pid) + queue_push(&plist, p); + } + break; +#ifdef ENABLE_CONDA + case REL_CONDA: + wp = pool_addrelproviders_conda(pool, name, evr, &plist); + break; +#endif + default: + break; + } + if (wp) + { + /* we can reuse an existing entry */ + queue_free(&plist); + pool->whatprovides_rel[d] = wp; + return wp; + } + } + else if (flags) + { + Id *ppaux = 0; + int eqmagic = 0; + /* simple version comparison relation */ +#if 0 + POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: what provides %s?\n", pool_dep2str(pool, name)); +#endif + pp = pool_whatprovides_ptr(pool, name); + if (!ISRELDEP(name) && (Offset)name < pool->whatprovidesauxoff) + ppaux = pool->whatprovidesaux[name] ? pool->whatprovidesauxdata + pool->whatprovidesaux[name] : 0; + while (ISRELDEP(name)) + { + rd = GETRELDEP(pool, name); + name = rd->name; + } + while ((p = *pp++) != 0) + { + Solvable *s = pool->solvables + p; + if (ppaux) + { + pid = *ppaux++; + if (pid && pid != 1) + { +#if 0 + POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: aux hit %d %s\n", p, pool_dep2str(pool, pid)); +#endif + if (!ISRELDEP(pid)) + { + if (pid != name) + continue; /* wrong provides name */ + if (pool->disttype == DISTTYPE_DEB) + continue; /* unversioned provides can never match versioned deps */ + } + else + { + prd = GETRELDEP(pool, pid); + if (prd->name != name) + continue; /* wrong provides name */ + if (!eqmagic) + eqmagic = rpmeqmagic_init(pool, flags, evr); + if (eqmagic > 0 && prd->flags == REL_EQ && !ISRELDEP(prd->evr) && + rpmeqmagic_cantmatch(pool, prd->flags, prd->evr, eqmagic)) + continue; + /* right package, both deps are rels. check flags/evr */ + if (!pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr)) + continue; + } + queue_push(&plist, p); + continue; + } + } + if (!s->provides || s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) + { + /* no provides or src rpm - check nevr */ + if (pool_match_nevr_rel(pool, s, MAKERELDEP(d))) + queue_push(&plist, p); + continue; + } + /* solvable p provides name in some rels */ + if (!eqmagic) + eqmagic = rpmeqmagic_init(pool, flags, evr); + pidp = s->repo->idarraydata + s->provides; + while ((pid = *pidp++) != 0) + { + if (!ISRELDEP(pid)) + { + if (pid != name) + continue; /* wrong provides name */ + if (pool->disttype == DISTTYPE_DEB) + continue; /* unversioned provides can never match versioned deps */ + break; + } + prd = GETRELDEP(pool, pid); + if (prd->name != name) + continue; /* wrong provides name */ + /* right package, both deps are rels. check flags/evr */ + if (eqmagic > 0 && prd->flags == REL_EQ && !ISRELDEP(prd->evr) && + rpmeqmagic_cantmatch(pool, prd->flags, prd->evr, eqmagic)) + continue; + if (pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr)) + break; /* matches */ + } + if (!pid) + continue; /* none of the providers matched */ + queue_push(&plist, p); + } + /* make our system solvable provide all unknown rpmlib() stuff */ + if (plist.count == 0 && !strncmp(pool_id2str(pool, name), "rpmlib(", 7)) + queue_push(&plist, SYSTEMSOLVABLE); + } + /* add providers to whatprovides */ +#if 0 + POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: adding %d packages to %d\n", plist.count, d); +#endif + pool->whatprovides_rel[d] = pool_queuetowhatprovides(pool, &plist); + queue_free(&plist); + + return pool->whatprovides_rel[d]; +} + +void +pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr) +{ + int nrels = pool->nrels; + Id d; + Reldep *rd; + + if (!pool->whatprovides_rel) + return; + for (d = 1, rd = pool->rels + d; d < nrels; d++, rd++) + { + if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS) + continue; + if (ns && rd->name != ns) + continue; + if (evr && rd->evr != evr) + continue; + if (pool->whatprovides_rel[d]) + pool_set_whatprovides(pool, MAKERELDEP(d), 0); + } +} + +void +pool_set_whatprovides(Pool *pool, Id id, Id providers) +{ + int d, nrels = pool->nrels; + Reldep *rd; + Map m; + + /* set new entry */ + if (ISRELDEP(id)) + { + d = GETRELID(id); + pool->whatprovides_rel[d] = providers; + d++; + } + else + { + pool->whatprovides[id] = providers; + if ((Offset)id < pool->whatprovidesauxoff) + pool->whatprovidesaux[id] = 0; /* sorry */ + d = 1; + } + if (!pool->whatprovides_rel) + return; + /* clear cache of all rels that use it */ + map_init(&m, 0); + for (rd = pool->rels + d; d < nrels; d++, rd++) + { + if (rd->name == id || rd->evr == id || + (m.size && ISRELDEP(rd->name) && MAPTST(&m, GETRELID(rd->name))) || + (m.size && ISRELDEP(rd->evr) && MAPTST(&m, GETRELID(rd->evr)))) + { + pool->whatprovides_rel[d] = 0; /* clear cache */ + if (!m.size) + map_init(&m, nrels); + MAPSET(&m, d); + } + } + map_free(&m); +} + +void +pool_add_new_provider(Pool *pool, Id id, Id p) +{ + Queue q; + Id *pp; + + /* find whatprovides entry */ + while (ISRELDEP(id)) + { + Reldep *rd = GETRELDEP(pool, id); + id = rd->name; + } + + /* add new provider to existing list keeping it sorted */ + queue_init(&q); + for (pp = pool->whatprovidesdata + pool->whatprovides[id]; *pp; pp++) + { + if (*pp == p) + { + queue_free(&q); /* already have it */ + return; + } + if (*pp > p) + { + queue_push(&q, p); + p = 0; + } + queue_push(&q, *pp); + } + if (p) + queue_push(&q, p); + pool_set_whatprovides(pool, id, pool_queuetowhatprovides(pool, &q)); + queue_free(&q); +} + +/* EOF */