static Hashtable growhash(Hashtable map, Hashval *mapnp) { Hashval mapn = *mapnp; Hashval newn = (mapn + 1) * 2 - 1; Hashval i, h, hh; Hashtable m; Id hx, qx; m = solv_calloc(newn + 1, 2 * sizeof(Id)); for (i = 0; i <= mapn; i++) { hx = map[2 * i]; if (!hx) continue; h = hx & newn; hh = HASHCHAIN_START; for (;;) { qx = m[2 * h]; if (!qx) break; h = HASHCHAIN_NEXT(h, hh, newn); } m[2 * h] = hx; m[2 * h + 1] = map[2 * i + 1]; } solv_free(map); *mapnp = newn; return m; }
/* * map a directory (containing a trailing /) into a number. * for unifywithstat this is the offset to the 16 byte stat result. * for unifywithcanon this is the offset to the normailzed dir. */ static Id normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create) { Hashval h, hh; Id qx; Id nspaceoff; int mycnt; if (!hx) hx = dirl + 1; h = hx & cbdata->normapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->normap[2 * h]; if (!qx) break; if (qx == hx) { Id off = cbdata->normap[2 * h + 1]; char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off]; if (!strncmp(dp, dir, dirl) && dp[dirl] == 0) return cbdata->norq.elements[off + 1]; } h = HASHCHAIN_NEXT(h, hh, cbdata->normapn); } if (!create) return 0; /* new dir. work. */ if (dir >= (const char *)cbdata->filesspace && dir < (const char *)cbdata->filesspace + cbdata->filesspacen) { /* can happen when called from unifywithcanon */ Id off = dir - (const char *)cbdata->filesspace; nspaceoff = addfilesspace(cbdata, dirl + 1); dir = (const char *)cbdata->filesspace + off; } else nspaceoff = addfilesspace(cbdata, dirl + 1); if (dirl) memcpy(cbdata->filesspace + nspaceoff, dir, dirl); cbdata->filesspace[nspaceoff + dirl] = 0; mycnt = cbdata->norq.count; queue_push2(&cbdata->norq, nspaceoff, -1); /* -1: in progress */ cbdata->normap[2 * h] = hx; cbdata->normap[2 * h + 1] = mycnt; if (++cbdata->normapused * 2 > cbdata->normapn) cbdata->normap = growhash(cbdata->normap, &cbdata->normapn); /* unify */ if (cbdata->usestat) nspaceoff = unifywithstat(cbdata, nspaceoff, dirl); else nspaceoff = unifywithcanon(cbdata, nspaceoff, dirl); cbdata->norq.elements[mycnt + 1] = nspaceoff; /* patch in result */ #if 0 if (!cbdata->usestat) printf("%s normalized to %d: %s\n", cbdata->filesspace + cbdata->norq.elements[mycnt], nspaceoff, cbdata->filesspace + nspaceoff); #endif return nspaceoff; }
static Id unifywithstat(struct cbdata *cbdata, Id diroff, int dirl) { struct stat stb; int i; Hashval h, hh; Id hx, qx; Id nspaceoff; unsigned char statdata[16 + sizeof(stb.st_dev) + sizeof(stb.st_ino)]; if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == '/') cbdata->filesspace[diroff + dirl - 1] = 0; cbdata->statsmade++; i = stat((char *)cbdata->filesspace + diroff, &stb); if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == 0) cbdata->filesspace[diroff + dirl - 1] = '/'; if (i) return diroff; memset(statdata, 0, 16); memcpy(statdata + 8, &stb.st_dev, sizeof(stb.st_dev)); memcpy(statdata, &stb.st_ino, sizeof(stb.st_ino)); hx = 0; for (i = 15; i >= 0; i--) hx = (unsigned int)hx * 13 + statdata[i]; h = hx & cbdata->statmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->statmap[2 * h]; if (!qx) break; if (qx == hx) { Id off = cbdata->statmap[2 * h + 1]; char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off]; if (!memcmp(dp, statdata, 16)) return cbdata->norq.elements[off + 1]; } h = HASHCHAIN_NEXT(h, hh, cbdata->statmapn); } /* new stat result. work. */ nspaceoff = addfilesspace(cbdata, 16); memcpy(cbdata->filesspace + nspaceoff, statdata, 16); queue_push2(&cbdata->norq, nspaceoff, nspaceoff); cbdata->statmap[2 * h] = hx; cbdata->statmap[2 * h + 1] = cbdata->norq.count - 2; if (++cbdata->statmapused * 2 > cbdata->statmapn) cbdata->statmap = growhash(cbdata->statmap, &cbdata->statmapn); return nspaceoff; }
static Hashtable joinhash_init(Repo *repo, Hashval *hmp) { Hashval hm = mkmask(repo->nsolvables); Hashtable ht = solv_calloc(hm + 1, sizeof(*ht)); Hashval h, hh; Solvable *s; int i; FOR_REPO_SOLVABLES(repo, i, s) { hh = HASHCHAIN_START; h = s->name & hm; while (ht[h]) h = HASHCHAIN_NEXT(h, hh, hm); ht[h] = i; }
static void finddirs_cb(void *cbdatav, const char *fn, struct filelistinfo *info) { struct cbdata *cbdata = cbdatav; Hashval h, hh; Id hx, qx; Id oidx, idx = cbdata->idx; hx = strhash(fn); if (!hx) hx = strlen(fn) + 1; h = hx & cbdata->dirmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->dirmap[2 * h]; if (!qx) break; if (qx == hx) break; h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn); } if (!qx) { /* a miss */ if (!cbdata->create) return; cbdata->dirmap[2 * h] = hx; cbdata->dirmap[2 * h + 1] = idx; if (++cbdata->dirmapused * 2 > cbdata->dirmapn) cbdata->dirmap = growhash(cbdata->dirmap, &cbdata->dirmapn); return; } oidx = cbdata->dirmap[2 * h + 1]; if (oidx == idx) return; /* found a conflict, this dir may be used in multiple packages */ if (oidx != -1) { MAPSET(&cbdata->idxmap, oidx); cbdata->dirmap[2 * h + 1] = -1; cbdata->dirconflicts++; } MAPSET(&cbdata->idxmap, idx); }
static inline int isindirmap(struct cbdata *cbdata, Id hx) { Hashval h, hh; Id qx; h = hx & cbdata->dirmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->dirmap[2 * h]; if (!qx) return 0; if (qx == hx) return cbdata->dirmap[2 * h + 1] == -1 ? 1 : 0; h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn); } }
static void findfileconflicts_alias_cb(void *cbdatav, const char *fn, struct filelistinfo *info) { int isdir = S_ISDIR(info->mode); struct cbdata *cbdata = cbdatav; const char *dp; Id idx, dirid; Id hx, qx; Hashval h, hh; idx = cbdata->idx; if (!info->dirlen) return; dp = fn + info->dirlen; if (info->diridx != cbdata->lastdiridx) { cbdata->lastdiridx = info->diridx; cbdata->lastdirhash = 0; } dp = fn + info->dirlen; hx = strhash(dp); if (!hx) hx = strlen(fn) + 1; h = hx & cbdata->cflmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->cflmap[2 * h]; if (!qx) break; if (qx == hx) break; h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn); } if (!qx || cbdata->cflmap[2 * h + 1] != -1) return; if (!cbdata->lastdirhash) cbdata->lastdirhash = strnhash(fn, dp - fn); dirid = normalizedir(cbdata, fn, dp - fn, cbdata->lastdirhash, 1); queue_push2(&cbdata->lookat, hx, idx); queue_push2(&cbdata->lookat, cbdata->lastdirhash, isdir ? -dirid : dirid); }
Id pool_rel2id(Pool *pool, Id name, Id evr, int flags, int create) { Hashval h, hh, hashmask; int i; Id id; Hashtable hashtbl; Reldep *ran; hashmask = pool->relhashmask; hashtbl = pool->relhashtbl; ran = pool->rels; /* extend hashtable if needed */ if (pool->nrels * 2 > hashmask) { solv_free(pool->relhashtbl); pool->relhashmask = hashmask = mkmask(pool->nrels + REL_BLOCK); pool->relhashtbl = hashtbl = solv_calloc(hashmask + 1, sizeof(Id)); /* rehash all rels into new hashtable */ for (i = 1; i < pool->nrels; i++) { h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask; hh = HASHCHAIN_START; while (hashtbl[h]) h = HASHCHAIN_NEXT(h, hh, hashmask); hashtbl[h] = i; } } /* compute hash and check for match */ h = relhash(name, evr, flags) & hashmask; hh = HASHCHAIN_START; while ((id = hashtbl[h]) != 0) { if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags) break; h = HASHCHAIN_NEXT(h, hh, hashmask); } if (id) return MAKERELDEP(id); if (!create) return ID_NULL; id = pool->nrels++; /* extend rel space if needed */ pool->rels = solv_extend(pool->rels, id, 1, sizeof(Reldep), REL_BLOCK); hashtbl[h] = id; ran = pool->rels + id; ran->name = name; ran->evr = evr; ran->flags = flags; /* extend whatprovides_rel if needed */ if (pool->whatprovides_rel && (id & WHATPROVIDES_BLOCK) == 0) { pool->whatprovides_rel = solv_realloc2(pool->whatprovides_rel, id + (WHATPROVIDES_BLOCK + 1), sizeof(Offset)); memset(pool->whatprovides_rel + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset)); } return MAKERELDEP(id); }
/* * Optimization for packages with an excessive amount of provides/requires: * if the number of deps exceed a threshold, we build a hash of the already * seen ids. */ static Offset repo_addid_dep_hash(Repo *repo, Offset olddeps, Id id, Id marker, int size) { Id oid, *oidp; int before; Hashval h, hh; Id hid; before = 0; if (marker) { if (marker < 0) { marker = -marker; before = 1; } if (marker == id) marker = 0; } /* maintain hash and lastmarkerpos */ if (repo->lastidhash_idarraysize != repo->idarraysize || size * 2 > repo->lastidhash_mask || repo->lastmarker != marker) { repo->lastmarkerpos = 0; if (size * 2 > repo->lastidhash_mask) { repo->lastidhash_mask = mkmask(size < REPO_ADDID_DEP_HASHMIN ? REPO_ADDID_DEP_HASHMIN : size); repo->lastidhash = solv_realloc2(repo->lastidhash, repo->lastidhash_mask + 1, sizeof(Id)); } memset(repo->lastidhash, 0, (repo->lastidhash_mask + 1) * sizeof(Id)); for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++) { h = oid & repo->lastidhash_mask; hh = HASHCHAIN_START; while (repo->lastidhash[h] != 0) h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask); repo->lastidhash[h] = oid; if (marker && oid == marker) repo->lastmarkerpos = oidp - repo->idarraydata; } repo->lastmarker = marker; repo->lastidhash_idarraysize = repo->idarraysize; } /* check the hash! */ h = id & repo->lastidhash_mask; hh = HASHCHAIN_START; while ((hid = repo->lastidhash[h]) != 0 && hid != id) h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask); /* put new element in hash */ if (!hid) repo->lastidhash[h] = id; else if (marker == SOLVABLE_FILEMARKER && (!before || !repo->lastmarkerpos)) return olddeps; if (marker && !before && !repo->lastmarkerpos) { /* we have to add the marker first */ repo->lastmarkerpos = repo->idarraysize - 1; olddeps = repo_addid(repo, olddeps, marker); /* now put marker in hash */ h = marker & repo->lastidhash_mask; hh = HASHCHAIN_START; while (repo->lastidhash[h] != 0) h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask); repo->lastidhash[h] = marker; repo->lastidhash_idarraysize = repo->idarraysize; } if (!hid) { /* new entry, insert in correct position */ if (marker && before && repo->lastmarkerpos) { /* need to add it before the marker */ olddeps = repo_addid(repo, olddeps, id); /* dummy to make room */ memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (repo->idarraysize - repo->lastmarkerpos - 2) * sizeof(Id)); repo->idarraydata[repo->lastmarkerpos++] = id; } else { /* just append it to the end */ olddeps = repo_addid(repo, olddeps, id); } repo->lastidhash_idarraysize = repo->idarraysize; return olddeps; } /* we already have it in the hash */ if (!marker) return olddeps; if (marker == SOLVABLE_FILEMARKER) { /* check if it is in the wrong half */ /* (we already made sure that "before" and "lastmarkerpos" are set, see above) */ for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++) if (oid == id) break; if (!oid) return olddeps; /* yes, wrong half. copy it over */ memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (oidp - (repo->idarraydata + repo->lastmarkerpos)) * sizeof(Id)); repo->idarraydata[repo->lastmarkerpos++] = id; return olddeps; } if (before) return olddeps; /* check if it is in the correct half */ for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++) if (oid == id) return olddeps; /* nope, copy it over */ for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++) if (oid == id) break; if (!oid) return olddeps; /* should not happen */ memmove(oidp, oidp + 1, (repo->idarraydata + repo->idarraysize - oidp - 2) * sizeof(Id)); repo->idarraydata[repo->idarraysize - 2] = id; repo->lastmarkerpos--; /* marker has been moved */ return olddeps; }
/* before calling the expensive findfileconflicts_cb we check if any of * the files match. This only makes sense when cbdata->create is off. */ static int precheck_solvable_files(struct cbdata *cbdata, Pool *pool, Id p) { Dataiterator di; Id hx, qx; Hashval h, hh; int found = 0; int aliases = cbdata->aliases; unsigned int lastdirid = -1; Hashval lastdirhash = 0; int lastdirlen = 0; int checkthisdir = 0; Repodata *lastrepodata = 0; dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST); while (dataiterator_step(&di)) { if (aliases) { /* hash just the basename */ hx = strhash(di.kv.str); if (!hx) hx = strlen(di.kv.str) + 1; } else { /* hash the full path */ if (di.data != lastrepodata || di.kv.id != lastdirid) { const char *dir; lastrepodata = di.data; lastdirid = di.kv.id; dir = repodata_dir2str(lastrepodata, lastdirid, ""); lastdirlen = strlen(dir); lastdirhash = strhash(dir); checkthisdir = isindirmap(cbdata, lastdirhash ? lastdirhash : lastdirlen + 1); } if (!checkthisdir) continue; hx = strhash_cont(di.kv.str, lastdirhash); if (!hx) hx = lastdirlen + strlen(di.kv.str) + 1; } h = hx & cbdata->cflmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->cflmap[2 * h]; if (!qx) break; if (qx == hx) { found = 1; break; } h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn); } if (found) break; } dataiterator_free(&di); return found; }
/* same as findfileconflicts_cb, but * - hashes with just the basename * - sets idx in a map instead of pushing to lookat * - sets the hash element to -1 if there may be a conflict */ static void findfileconflicts_basename_cb(void *cbdatav, const char *fn, struct filelistinfo *info) { struct cbdata *cbdata = cbdatav; int isdir = S_ISDIR(info->mode); const char *dp; Id idx, oidx; Id hx, qx; Hashval h, hh; idx = cbdata->idx; if (!info->dirlen) return; dp = fn + info->dirlen; hx = strhash(dp); if (!hx) hx = strlen(fn) + 1; h = hx & cbdata->cflmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->cflmap[2 * h]; if (!qx) break; if (qx == hx) break; h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn); } if (!qx) { /* a miss */ if (!cbdata->create) return; cbdata->cflmap[2 * h] = hx; cbdata->cflmap[2 * h + 1] = (isdir ? -idx - 2 : idx); if (++cbdata->cflmapused * 2 > cbdata->cflmapn) cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn); return; } oidx = cbdata->cflmap[2 * h + 1]; if (oidx < -1) { int i; if (isdir) { /* both are directories. delay the conflict, keep oidx in slot */ queue_push2(&cbdata->lookat_dir, hx, idx); return; } oidx = -idx - 2; /* now have file, had directories before. */ cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */ /* dump all delayed directory hits for hx */ for (i = 0; i < cbdata->lookat_dir.count; i += 2) if (cbdata->lookat_dir.elements[i] == hx) MAPSET(&cbdata->idxmap, cbdata->lookat_dir.elements[i + 1]); } else if (oidx == idx) return; /* no conflicts with ourself, please */ if (oidx >= 0) MAPSET(&cbdata->idxmap, oidx); MAPSET(&cbdata->idxmap, idx); if (oidx != -1) cbdata->cflmap[2 * h + 1] = -1; }
static void findfileconflicts_cb(void *cbdatav, const char *fn, struct filelistinfo *info) { struct cbdata *cbdata = cbdatav; int isdir = S_ISDIR(info->mode); const char *dp; Id idx, oidx; Id hx, qx; Hashval h, hh, dhx; idx = cbdata->idx; if (!info->dirlen) return; dp = fn + info->dirlen; if (info->diridx != cbdata->lastdiridx) { cbdata->lastdiridx = info->diridx; cbdata->lastdirhash = strnhash(fn, dp - fn); } dhx = cbdata->lastdirhash; /* this mirrors the "if (!hx) hx = strlen(fn) + 1" in finddirs_cb */ if (!isindirmap(cbdata, dhx ? dhx : dp - fn + 1)) return; hx = strhash_cont(dp, dhx); if (!hx) hx = strlen(fn) + 1; h = hx & cbdata->cflmapn; hh = HASHCHAIN_START; for (;;) { qx = cbdata->cflmap[2 * h]; if (!qx) break; if (qx == hx) break; h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn); } if (!qx) { /* a miss */ if (!cbdata->create) return; cbdata->cflmap[2 * h] = hx; cbdata->cflmap[2 * h + 1] = (isdir ? ~idx : idx); if (++cbdata->cflmapused * 2 > cbdata->cflmapn) cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn); return; } oidx = cbdata->cflmap[2 * h + 1]; if (oidx < 0) { int i; if (isdir) { /* both are directories. delay the conflict, keep oidx in slot */ queue_push2(&cbdata->lookat_dir, hx, idx); return; } oidx = ~oidx; /* now have file, had directories before. */ cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */ /* dump all delayed directory hits for hx */ for (i = 0; i < cbdata->lookat_dir.count; i += 2) if (cbdata->lookat_dir.elements[i] == hx) { queue_push2(&cbdata->lookat, hx, cbdata->lookat_dir.elements[i + 1]); queue_push2(&cbdata->lookat, 0, 0); } } else if (oidx == idx) return; /* no conflicts with ourself, please */ queue_push2(&cbdata->lookat, hx, oidx); queue_push2(&cbdata->lookat, 0, 0); queue_push2(&cbdata->lookat, hx, idx); queue_push2(&cbdata->lookat, 0, 0); }