#include <stdlib.h>
#include <string.h>
#include "config.h"
typedef int (*vec_cmp_func)(const void *, int, int);
#define TAILHSIZE 128
#define PVHASH(i) ((i) & (TAILHSIZE - 1))
#define LOCHASH(l) (((long)(l) >> 2) & (TAILHSIZE - 1))
struct tails {
struct tails *t_next;
int t_ends_at;
};
static struct tails *tails[TAILHSIZE];
static int locspace;
static int pvecspace;
static int longest_pvec;
static void packdevi(void);
static void packlocs(void);
static void packpvec(void);
static void addparents(struct devi *src, struct devi *dst);
static int nparents(struct devi **, struct devbase *, int);
static int sameas(struct devi *, struct devi *);
static int findvec(const void *, int, int, vec_cmp_func, int);
static int samelocs(const void *, int, int);
static int addlocs(const char **, int);
static int loclencmp(const void *, const void *);
static int samepv(const void *, int, int);
static int addpv(short *, int);
static int pvlencmp(const void *, const void *);
static void resettails(void);
void
pack(void)
{
struct devi *i;
int n;
packdevi();
locspace = pvecspace = 0;
for (i = alldevi; i != NULL; i = i->i_next) {
if (i->i_collapsed)
continue;
locspace += i->i_atattr->a_loclen;
n = i->i_pvlen + 1;
if (n > longest_pvec)
longest_pvec = n;
pvecspace += n;
}
locators.vec = ereallocarray(NULL, locspace, sizeof(*locators.vec));
locators.used = 0;
packlocs();
parents.vec = ereallocarray(NULL, pvecspace, sizeof(*parents.vec));
parents.used = 0;
packpvec();
}
void
packdevi(void)
{
struct devi *i, *l, *p;
struct deva *d;
int j, m, n;
packed = ereallocarray(NULL, ndevi + 1, sizeof *packed);
n = 0;
for (d = alldevas; d != NULL; d = d->d_next) {
for (i = d->d_ihead; i != NULL; i = i->i_asame) {
m = n;
for (l = i; l != NULL; l = l->i_alias) {
if (l->i_cfindex >= 0)
continue;
l->i_pvlen = 0;
l->i_pvoff = -1;
l->i_locoff = -1;
for (j = m; j < n; j++) {
p = packed[j];
if (sameas(l, p)) {
l->i_collapsed = 1;
l->i_cfindex = p->i_cfindex;
goto nextalias;
}
}
l->i_collapsed = 0;
l->i_cfindex = n;
l->i_parents = emalloc(sizeof(*l->i_parents));
l->i_parents[0] = NULL;
packed[n++] = l;
nextalias:;
}
}
}
npacked = n;
packed[n] = NULL;
for (i = alldevi; i != NULL; i = i->i_next)
addparents(i, packed[i->i_cfindex]);
}
static int
sameas(struct devi *i1, struct devi *i2)
{
const char **p1, **p2;
if (i1->i_atattr != i2->i_atattr)
return (0);
if (i1->i_cfflags != i2->i_cfflags)
return (0);
for (p1 = i1->i_locs, p2 = i2->i_locs; *p1 == *p2; p2++)
if (*p1++ == 0)
return (1);
return 0;
}
static void
addparents(struct devi *src, struct devi *dst)
{
struct nvlist *nv;
struct devi *i, **p, **q;
int j, n, old, new, ndup;
if (dst->i_collapsed)
panic("addparents() i_collapsed");
if (src->i_at == NULL)
return;
if (src->i_atdev != NULL) {
n = nparents(NULL, src->i_atdev, src->i_atunit);
p = ereallocarray(NULL, n, sizeof *p);
if (n == 0)
return;
(void)nparents(p, src->i_atdev, src->i_atunit);
} else {
n = 0;
for (nv = src->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
n += nparents(NULL, nv->nv_ptr, src->i_atunit);
if (n == 0)
return;
p = ereallocarray(NULL, n, sizeof *p);
n = 0;
for (nv = src->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
n += nparents(p + n, nv->nv_ptr, src->i_atunit);
}
ndup = 0;
for (j = 0; j < n; j++) {
i = p[j];
for (q = dst->i_parents; *q != NULL; q++) {
if (*q == i) {
ndup++;
p[j] = NULL;
break;
}
}
}
old = dst->i_pvlen;
new = old + (n - ndup);
if (old > new)
panic("addparents() old > new");
if (old == new) {
free(p);
return;
}
dst->i_parents = q = ereallocarray(dst->i_parents, new + 1, sizeof(*q));
dst->i_pvlen = new;
q[new] = NULL;
q += old;
for (j = 0; j < n; j++)
if (p[j] != NULL)
*q++ = p[j];
free(p);
}
static int
nparents(struct devi **p, struct devbase *dev, int unit)
{
struct devi *i, *l;
int n;
n = 0;
for (i = dev->d_ihead; i != NULL; i = i->i_bsame) {
for (l = i; l != NULL; l = l->i_alias) {
if (!l->i_collapsed &&
(unit == WILD || unit == l->i_unit)) {
if (p != NULL)
*p++ = l;
n++;
}
}
}
return (n);
}
static void
packlocs(void)
{
struct devi **p, *i;
int l, o;
qsort(packed, npacked, sizeof *packed, loclencmp);
for (p = packed; (i = *p) != NULL; p++) {
if ((l = i->i_atattr->a_loclen) > 0) {
o = findvec(i->i_locs, LOCHASH(i->i_locs[l - 1]), l,
samelocs, locators.used);
i->i_locoff = o < 0 ? addlocs(i->i_locs, l) : o;
} else
i->i_locoff = -1;
}
resettails();
}
static void
packpvec(void)
{
struct devi **p, *i, **par;
int l, v, o;
short *vec;
vec = ereallocarray(NULL, longest_pvec, sizeof(*vec));
qsort(packed, npacked, sizeof *packed, pvlencmp);
for (p = packed; (i = *p) != NULL; p++) {
l = i->i_pvlen;
if (l > longest_pvec)
panic("packpvec");
par = i->i_parents;
for (v = 0; v < l; v++)
vec[v] = par[v]->i_cfindex;
if (l == 0 || (o = findvec(vec, PVHASH(vec[l - 1]), l,
samepv, parents.used)) < 0)
o = addpv(vec, l);
i->i_pvoff = o;
}
free(vec);
resettails();
}
static int
findvec(const void *ptr, int hash, int len, vec_cmp_func cmp, int nextplace)
{
struct tails *t, **hp;
int off;
hp = &tails[hash];
for (t = *hp; t != NULL; t = t->t_next) {
off = t->t_ends_at - len;
if (off >= 0 && (*cmp)(ptr, off, len))
return (off);
}
t = emalloc(sizeof(*t));
t->t_next = *hp;
*hp = t;
t->t_ends_at = nextplace + len;
return (-1);
}
static int
samelocs(const void *ptr, int off, int len)
{
const char **p, **q;
for (p = &locators.vec[off], q = (const char **)ptr; --len >= 0;)
if (*p++ != *q++)
return (0);
return (1);
}
static int
addlocs(const char **locs, int len)
{
const char **p;
int ret;
ret = locators.used;
if ((locators.used = ret + len) > locspace)
panic("addlocs: overrun");
for (p = &locators.vec[ret]; --len >= 0;)
*p++ = *locs++;
return (ret);
}
static int
loclencmp(const void *a, const void *b)
{
int l1, l2;
l1 = (*(struct devi **)a)->i_atattr->a_loclen;
l2 = (*(struct devi **)b)->i_atattr->a_loclen;
return (l2 - l1);
}
static int
samepv(const void *ptr, int off, int len)
{
short *p, *q;
for (p = &parents.vec[off], q = (short *)ptr; --len >= 0;)
if (*p++ != *q++)
return (0);
return (1);
}
static int
addpv(short *pv, int len)
{
short *p;
int ret;
static int firstend = -1;
if (len == 0 && firstend >= 0)
return (firstend);
len++;
ret = parents.used;
if ((parents.used = ret + len) > pvecspace)
panic("addpv: overrun");
for (p = &parents.vec[ret]; --len > 0;)
*p++ = *pv++;
*p = -1;
if (firstend < 0)
firstend = parents.used - 1;
return (ret);
}
static int
pvlencmp(const void *a, const void *b)
{
int l1, l2;
l1 = (*(struct devi **)a)->i_pvlen;
l2 = (*(struct devi **)b)->i_pvlen;
return (l2 - l1);
}
static void
resettails(void)
{
struct tails **p, *t, *next;
int i;
for (p = tails, i = TAILHSIZE; --i >= 0; p++) {
for (t = *p; t != NULL; t = next) {
next = t->t_next;
free(t);
}
*p = NULL;
}
}