From: Michael Schroeder Date: Mon, 10 Sep 2018 09:53:22 +0000 (+0200) Subject: Rewrite addfileprovides X-Git-Tag: 0.7.0~51 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=110c3c2a1f833ac8e0b8e2162f2b3c30fd8a0b40;p=thirdparty%2Flibsolv.git Rewrite addfileprovides It should now mirror what repo_lookup_filelist_repodata does. It should also be quite a bit faster than the old code. --- diff --git a/src/fileprovides.c b/src/fileprovides.c index 11ff4f5f..ec808311 100644 --- a/src/fileprovides.c +++ b/src/fileprovides.c @@ -22,6 +22,7 @@ #include "util.h" #include "bitmap.h" + struct searchfiles { Id *ids; int nfiles; @@ -101,188 +102,450 @@ struct addfileprovides_cbdata { Id *ids; char **dirs; char **names; - Id *dids; - Map providedids; + Map *providedids; + int provstart; + int provend; - Map useddirs; + Map *todo; + int todo_start; + int todo_end; }; -static int -addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value) +/* split filelist dep into basename and dirname */ +static void +create_dirs_names_array(struct addfileprovides_cbdata *cbd, Pool *pool) { - struct addfileprovides_cbdata *cbd = cbdata; int i; + cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *)); + cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *)); + for (i = 0; i < cbd->nfiles; i++) + { + char *s = solv_strdup(pool_id2str(pool, cbd->ids[i])); + cbd->dirs[i] = s; + s = strrchr(s, '/'); + *s = 0; + cbd->names[i] = s + 1; + } +} - if (!cbd->useddirs.size) +static void +free_dirs_names_array(struct addfileprovides_cbdata *cbd) +{ + int i; + if (cbd->dirs) { - map_init(&cbd->useddirs, data->dirpool.ndirs + 1); - if (!cbd->dirs) - { - cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *)); - cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *)); - for (i = 0; i < cbd->nfiles; i++) - { - char *s = solv_strdup(pool_id2str(data->repo->pool, cbd->ids[i])); - cbd->dirs[i] = s; - s = strrchr(s, '/'); - *s = 0; - cbd->names[i] = s + 1; - } - } for (i = 0; i < cbd->nfiles; i++) - { - Id did; - if (MAPTST(&cbd->providedids, cbd->ids[i])) - { - cbd->dids[i] = 0; - continue; - } - did = repodata_str2dir(data, cbd->dirs[i], 0); - cbd->dids[i] = did; - if (did) - MAPSET(&cbd->useddirs, did); - } - repodata_free_dircache(data); + solv_free(cbd->dirs[i]); + cbd->dirs = solv_free(cbd->dirs); + cbd->names = solv_free(cbd->names); } - if (value->id >= data->dirpool.ndirs || !MAPTST(&cbd->useddirs, value->id)) +} + +static void +prune_todo_range(Repo *repo, struct addfileprovides_cbdata *cbd) +{ + int start = cbd->todo_start, end = cbd->todo_end; + while (start < end && !MAPTST(cbd->todo, start - repo->start)) + start++; + while (end > start && !MAPTST(cbd->todo, end - 1 - repo->start)) + end--; + cbd->todo_start = start; + cbd->todo_end = end; +} + +static int +repodata_intersects_todo(Repodata *data, struct addfileprovides_cbdata *cbd) +{ + Repo *repo; + int p, start = data->start, end = data->end; + if (start >= cbd->todo_end || end <= cbd->todo_start) return 0; - for (i = 0; i < cbd->nfiles; i++) - if (cbd->dids[i] == value->id && !strcmp(cbd->names[i], value->str)) - s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER); + repo = data->repo; + if (start < cbd->todo_start) + start = cbd->todo_start; + if (end > cbd->todo_end) + end = cbd->todo_end; + for (p = start; p < end; p++) + if (MAPTST(cbd->todo, p - repo->start)) + return 1; return 0; } +/* forward declaration */ +static void repodata_addfileprovides_search(Repodata *data, struct addfileprovides_cbdata *cbd); + +/* search a subset of the todo range */ static void -pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly) +repodata_addfileprovides_search_limited(Repodata *data, struct addfileprovides_cbdata *cbd, int start, int end) { - Id p; - Repodata *data; - Repo *repo; - Queue fileprovidesq; - int i, j, repoid, repodataid; - int provstart, provend; - Map donemap; - int ndone, incomplete; - if (!pool->urepos) + int old_todo_start = cbd->todo_start; + int old_todo_end = cbd->todo_end; + if (start < cbd->todo_start) + start = cbd->todo_start; + if (end > cbd->todo_end) + end = cbd->todo_end; + if (start >= end) return; + cbd->todo_start = start; + cbd->todo_end = end; + repodata_addfileprovides_search(data, cbd); + cbd->todo_start = old_todo_start; + cbd->todo_end = old_todo_end; + prune_todo_range(data->repo, cbd); +} - cbd->nfiles = sf->nfiles; - cbd->ids = sf->ids; - cbd->dirs = 0; - cbd->names = 0; - cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id)); - map_init(&cbd->providedids, pool->ss.nstrings); - - repoid = 1; - repo = repoonly ? repoonly : pool->repos[repoid]; - map_init(&donemap, pool->nsolvables); - queue_init(&fileprovidesq); - provstart = provend = 0; - for (;;) +static void +repodata_addfileprovides_search(Repodata *data, struct addfileprovides_cbdata *cbd) +{ + Repo *repo = data->repo; + int i, p, start, end; + Map useddirs; + Map *providedids = 0; + + /* make it available */ + if (data->state == REPODATA_STUB) + repodata_load(data); + if (data->state != REPODATA_AVAILABLE) + return; + if (!data->incoredata || !data->dirpool.ndirs) + return; + + start = cbd->todo_start > data->start ? cbd->todo_start : data->start; + end = cbd->todo_end > data->end ? data->end : cbd->todo_end; + + if (start >= end) + return; + + /* deal with provideids overlap */ + if (cbd->providedids) { - if (!repo || repo->disabled) + if (start >= cbd->provstart && end <= cbd->provend) + providedids = cbd->providedids; /* complete overlap */ + else if (start < cbd->provend && end > cbd->provstart) { - if (repoonly || ++repoid == pool->nrepos) - break; - repo = pool->repos[repoid]; + /* partial overlap, need to split search */ + if (start < cbd->provstart) + { + repodata_addfileprovides_search_limited(data, cbd, start, cbd->provstart); + start = cbd->provstart; + } + if (end > cbd->provend) + { + repodata_addfileprovides_search_limited(data, cbd, cbd->provend, end); + end = cbd->provend; + } + if (start < end) + repodata_addfileprovides_search_limited(data, cbd, start, end); + return; + } + } + + /* set up dirs and names array if not already done */ + if (!cbd->dirs) + create_dirs_names_array(cbd, repo->pool); + + /* set up useddirs map and the cbd->dids array */ + map_init(&useddirs, data->dirpool.ndirs); + for (i = 0; i < cbd->nfiles; i++) + { + Id did; + if (providedids && MAPTST(providedids, cbd->ids[i])) + { + cbd->dids[i] = 0; /* already included, do not add again */ continue; } - ndone = 0; - FOR_REPODATAS(repo, repodataid, data) + cbd->dids[i] = did = repodata_str2dir(data, cbd->dirs[i], 0); + if (did) + MAPSET(&useddirs, did); + } + repodata_free_dircache(data); /* repodata_str2dir created it */ + + for (p = start; p < end; p++) + { + const unsigned char *dp; + Solvable *s; + if (!MAPTST(cbd->todo, p - repo->start)) + continue; + dp = repodata_lookup_packed_dirstrarray(data, p, SOLVABLE_FILELIST); + if (!dp) + continue; + /* now iterate through the packed array */ + s = repo->pool->solvables + p; + MAPCLR(cbd->todo, p - repo->start); /* this entry is done */ + for (;;) { - if (ndone >= repo->nsolvables) + Id did = 0; + int c; + while ((c = *dp++) & 0x80) + did = (did << 7) ^ c ^ 0x80; + did = (did << 6) | (c & 0x3f); + if ((unsigned int)did < (unsigned int)data->dirpool.ndirs && MAPTST(&useddirs, did)) + { + /* there is at least one entry with that did */ + for (i = 0; i < cbd->nfiles; i++) + if (cbd->dids[i] == did && !strcmp(cbd->names[i], (const char *)dp)) + s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER); + } + if (!(c & 0x40)) break; + dp += strlen((const char *)dp) + 1; + } + } + map_free(&useddirs); + prune_todo_range(repo, cbd); +} + +static void +repo_addfileprovides_search_filtered(Repo *repo, struct addfileprovides_cbdata *cbd, int filteredid, Map *postpone) +{ + Repodata *data = repo->repodata + filteredid; + Map *providedids = cbd->providedids; + int rdid; + int start, end, p, i; + Map old_todo; + int old_todo_start, old_todo_end; + + start = cbd->todo_start > data->start ? cbd->todo_start : data->start; + end = cbd->todo_end > data->end ? data->end : cbd->todo_end; - if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq)) + if (providedids) + { + /* check if all solvables are in the provide range */ + if (start < cbd->provstart || end > cbd->provend) + { + /* unclear, check each solvable */ + for (p = start; p < end; p++) { - map_empty(&cbd->providedids); - for (i = 0; i < fileprovidesq.count; i++) - MAPSET(&cbd->providedids, fileprovidesq.elements[i]); - provstart = data->start; - provend = data->end; - for (i = 0; i < cbd->nfiles; i++) - if (!MAPTST(&cbd->providedids, cbd->ids[i])) - break; - if (i == cbd->nfiles) + if (p >= cbd->provstart && p < cbd->provend) + continue; + if (data->incoreoffset[p - data->start] && MAPTST(cbd->todo, p - repo->start)) { - /* great! no need to search files */ - for (p = data->start; p < data->end; p++) - if (pool->solvables[p].repo == repo) - { - if (MAPTST(&donemap, p)) - continue; - MAPSET(&donemap, p); - ndone++; - } - continue; + providedids = 0; /* nope, cannot prune with providedids */ + break; } } + } + } - if (!repodata_has_keyname(data, SOLVABLE_FILELIST)) - continue; + /* check if the filtered files are enough */ + for (i = 0; i < cbd->nfiles; i++) + { + if (providedids && MAPTST(providedids, cbd->ids[i])) /* this one is already provided */ + continue; + if (!repodata_filelistfilter_matches(data, pool_id2str(repo->pool, cbd->ids[i]))) + break; + } + if (i < cbd->nfiles) + { + /* nope, need to search the extensions as well. postpone. */ + for (p = start; p < end; p++) + { + if (data->incoreoffset[p - data->start] && MAPTST(cbd->todo, p - repo->start)) + { + if (!postpone->size) + map_grow(postpone, repo->nsolvables); + MAPSET(postpone, p - repo->start); + MAPCLR(cbd->todo, p - repo->start); + } + } + prune_todo_range(repo, cbd); + return; + } + + /* now check if there is no data marked withour EXTENSION */ + /* limit todo to the solvables in this repodata */ + old_todo_start = cbd->todo_start; + old_todo_end = cbd->todo_end; + old_todo = *cbd->todo; + map_init(cbd->todo, repo->nsolvables); + for (p = start; p < end; p++) + if (data->incoreoffset[p - data->start] && MAPTST(&old_todo, p - repo->start)) + { + MAPCLR(&old_todo, p - repo->start); + MAPSET(cbd->todo, p - repo->start); + } + prune_todo_range(repo, cbd); - if (data->start < provstart || data->end > provend) + /* do the check */ + for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > filteredid ; rdid--, data--) + { + if (data->filelisttype == REPODATA_FILELIST_EXTENSION) + continue; + if (data->start >= cbd->todo_end || data->end <= cbd->todo_start) + continue; + if (!repodata_has_keyname(data, SOLVABLE_FILELIST)) + continue; + if (!repodata_intersects_todo(data, cbd)) + continue; + /* oh no, this filelist data is not tagged with REPODATA_FILELIST_EXTENSION! */ + /* postpone entries that have filelist data */ + start = cbd->todo_start > data->start ? cbd->todo_start : data->start; + end = cbd->todo_end > data->end ? data->end : cbd->todo_end; + for (p = start; p < end; p++) + if (MAPTST(cbd->todo, p - repo->start)) + if (repodata_lookup_type(data, p, SOLVABLE_FILELIST)) { - map_empty(&cbd->providedids); - provstart = provend = 0; + if (!postpone->size) + map_grow(postpone, repo->nsolvables); + MAPSET(postpone, p - repo->start); + MAPCLR(cbd->todo, p - repo->start); } + prune_todo_range(repo, cbd); + if (cbd->todo_start >= cbd->todo_end) + break; + } + + /* do the search over the filtered file list with the remaining entries*/ + if (cbd->todo_start < cbd->todo_end) + repodata_addfileprovides_search(repo->repodata + filteredid, cbd); + + /* restore todo map */ + map_free(cbd->todo); + *cbd->todo = old_todo; + cbd->todo_start = old_todo_start; + cbd->todo_end = old_todo_end; + prune_todo_range(repo, cbd); +} + +static void +repo_addfileprovides_search(Repo *repo, struct addfileprovides_cbdata *cbd, struct searchfiles *sf) +{ + Repodata *data; + int rdid, p, i; + int provstart, provend; + Map todo; + Map providedids; + + if (repo->end <= repo->start || !repo->nsolvables || !sf->nfiles) + return; - /* check if the data is incomplete */ - incomplete = 0; - if (data->state == REPODATA_AVAILABLE) + /* update search data if changed */ + if (cbd->nfiles != sf->nfiles || cbd->ids != sf->ids) + { + free_dirs_names_array(cbd); + cbd->nfiles = sf->nfiles; + cbd->ids = sf->ids; + cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id)); + } + + /* create todo map and range */ + map_init(&todo, repo->end - repo->start); + for (p = repo->start; p < repo->end; p++) + if (repo->pool->solvables[p].repo == repo) + MAPSET(&todo, p - repo->start); + cbd->todo = &todo; + cbd->todo_start = repo->start; + cbd->todo_end = repo->end; + prune_todo_range(repo, cbd); + + provstart = provend = 0; + map_init(&providedids, 0); + data = repo_lookup_repodata(repo, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES); + if (data) + { + Queue fileprovidesq; + queue_init(&fileprovidesq); + if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq)) + { + map_grow(&providedids, repo->pool->ss.nstrings); + cbd->providedids = &providedids; + provstart = data->start; + provend = data->end; + for (i = 0; i < fileprovidesq.count; i++) + MAPSET(&providedids, fileprovidesq.elements[i]); + for (i = 0; i < cbd->nfiles; i++) + if (!MAPTST(&providedids, cbd->ids[i])) + break; + if (i == cbd->nfiles) { - for (j = 1; j < data->nkeys; j++) - if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST) - break; - if (j < data->nkeys) + /* all included, clear entries from todo list */ + if (provstart <= cbd->todo_start && provend >= cbd->todo_end) + cbd->todo_end = cbd->todo_start; /* clear complete range */ + else { -#if 0 - for (i = 0; i < cbd->nfiles; i++) - if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i]))) - printf("need complete filelist because of %s\n", pool_id2str(pool, cbd->ids[i])); -#endif - for (i = 0; i < cbd->nfiles; i++) - if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i]))) - break; - if (i < cbd->nfiles) - incomplete = 1; + for (p = provstart; p < provend; p++) + MAPCLR(&todo, p - repo->start); + prune_todo_range(repo, cbd); } } - - /* do the search */ - map_init(&cbd->useddirs, 0); - for (p = data->start; p < data->end; p++) - if (pool->solvables[p].repo == repo) - { - if (MAPTST(&donemap, p)) - continue; - repodata_search(data, p, SOLVABLE_FILELIST, 0, addfileprovides_cb, cbd); - if (!incomplete) - { - MAPSET(&donemap, p); - ndone++; - } - } - map_free(&cbd->useddirs); } + queue_free(&fileprovidesq); + } - if (repoonly || ++repoid == pool->nrepos) - break; - repo = pool->repos[repoid]; + if (cbd->todo_start >= cbd->todo_end) + { + map_free(&todo); + cbd->todo = 0; + map_free(&providedids); + cbd->providedids = 0; + return; } - map_free(&donemap); - queue_free(&fileprovidesq); - map_free(&cbd->providedids); - if (cbd->dirs) + + /* this is similar to repo_lookup_filelist_repodata in repo.c */ + + for (rdid = 1, data = repo->repodata + rdid; rdid < repo->nrepodata; rdid++, data++) + if (data->filelisttype == REPODATA_FILELIST_FILTERED) + break; + for (; rdid < repo->nrepodata; rdid++, data++) + if (data->filelisttype == REPODATA_FILELIST_EXTENSION) + break; + + if (rdid < repo->nrepodata) { - for (i = 0; i < cbd->nfiles; i++) - solv_free(cbd->dirs[i]); - cbd->dirs = solv_free(cbd->dirs); - cbd->names = solv_free(cbd->names); + /* have at least one repodata with REPODATA_FILELIST_FILTERED followed by REPODATA_FILELIST_EXTENSION */ + Map postpone; + map_init(&postpone, 0); + for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--) + { + if (data->filelisttype != REPODATA_FILELIST_FILTERED) + continue; + if (!repodata_intersects_todo(data, cbd)) + continue; + if (data->state != REPODATA_AVAILABLE) + { + if (data->state != REPODATA_STUB) + continue; + repodata_load(data); + if (data->state != REPODATA_AVAILABLE || data->filelisttype != REPODATA_FILELIST_FILTERED) + continue; + } + repo_addfileprovides_search_filtered(repo, cbd, rdid, &postpone); + } + if (postpone.size) + { + /* add postponed entries back to todo */ + map_or(&todo, &postpone); + cbd->todo_start = repo->start; + cbd->todo_end = repo->end; + prune_todo_range(repo, cbd); + } + map_free(&postpone); + } + + /* search remaining entries in the standard way */ + if (cbd->todo_start < cbd->todo_end) + { + for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--) + { + if (data->start >= cbd->todo_end || data->end <= cbd->todo_start) + continue; + if (!repodata_has_keyname(data, SOLVABLE_FILELIST)) + continue; + if (!repodata_intersects_todo(data, cbd)) + continue; + repodata_addfileprovides_search(data, cbd); + if (cbd->todo_start >= cbd->todo_end) + break; + } } + + map_free(&todo); + cbd->todo = 0; + map_free(&providedids); + cbd->providedids = 0; } void @@ -297,6 +560,7 @@ pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst) installed = pool->installed; now = solv_timems(0); + memset(&cbd, 0, sizeof(cbd)); memset(&sf, 0, sizeof(sf)); map_init(&sf.seen, pool->ss.nstrings + pool->nrels); memset(&isf, 0, sizeof(isf)); @@ -328,23 +592,22 @@ pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst) if (s->enhances) pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp); } + map_free(&sf.seen); map_free(&isf.seen); POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles); - cbd.dids = 0; if (sf.nfiles) { #if 0 for (i = 0; i < sf.nfiles; i++) POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i])); #endif - pool_addfileprovides_search(pool, &cbd, &sf, 0); + FOR_REPOS(i, repo) + repo_addfileprovides_search(repo, &cbd, &sf); if (idq) - for (i = 0; i < sf.nfiles; i++) - queue_push(idq, sf.ids[i]); + queue_insertn(idq, idq->count, sf.nfiles, sf.ids); if (idqinst) - for (i = 0; i < sf.nfiles; i++) - queue_push(idqinst, sf.ids[i]); + queue_insertn(idqinst, idqinst->count, sf.nfiles, sf.ids); solv_free(sf.ids); } if (isf.nfiles) @@ -354,12 +617,13 @@ pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst) POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i])); #endif if (installed) - pool_addfileprovides_search(pool, &cbd, &isf, installed); + repo_addfileprovides_search(installed, &cbd, &isf); if (installed && idqinst) for (i = 0; i < isf.nfiles; i++) queue_pushunique(idqinst, isf.ids[i]); solv_free(isf.ids); } + free_dirs_names_array(&cbd); solv_free(cbd.dids); pool_freewhatprovides(pool); /* as we have added provides */ POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now));