xref: /plan9/sys/src/ape/cmd/pdksh/table.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier /*
2*7dd7cddfSDavid du Colombier  * dynamic hashed associative table for commands and variables
3*7dd7cddfSDavid du Colombier  */
4*7dd7cddfSDavid du Colombier 
5*7dd7cddfSDavid du Colombier #include "sh.h"
6*7dd7cddfSDavid du Colombier 
7*7dd7cddfSDavid du Colombier #define	INIT_TBLS	8	/* initial table size (power of 2) */
8*7dd7cddfSDavid du Colombier 
9*7dd7cddfSDavid du Colombier static void     texpand     ARGS((struct table *tp, int nsize));
10*7dd7cddfSDavid du Colombier static int      tnamecmp    ARGS((void *p1, void *p2));
11*7dd7cddfSDavid du Colombier 
12*7dd7cddfSDavid du Colombier 
13*7dd7cddfSDavid du Colombier unsigned int
hash(n)14*7dd7cddfSDavid du Colombier hash(n)
15*7dd7cddfSDavid du Colombier 	register const char * n;
16*7dd7cddfSDavid du Colombier {
17*7dd7cddfSDavid du Colombier 	register unsigned int h = 0;
18*7dd7cddfSDavid du Colombier 
19*7dd7cddfSDavid du Colombier 	while (*n != '\0')
20*7dd7cddfSDavid du Colombier 		h = 2*h + *n++;
21*7dd7cddfSDavid du Colombier 	return h * 32821;	/* scatter bits */
22*7dd7cddfSDavid du Colombier }
23*7dd7cddfSDavid du Colombier 
24*7dd7cddfSDavid du Colombier void
tinit(tp,ap,tsize)25*7dd7cddfSDavid du Colombier tinit(tp, ap, tsize)
26*7dd7cddfSDavid du Colombier 	register struct table *tp;
27*7dd7cddfSDavid du Colombier 	register Area *ap;
28*7dd7cddfSDavid du Colombier 	int tsize;
29*7dd7cddfSDavid du Colombier {
30*7dd7cddfSDavid du Colombier 	tp->areap = ap;
31*7dd7cddfSDavid du Colombier 	tp->tbls = NULL;
32*7dd7cddfSDavid du Colombier 	tp->size = tp->nfree = 0;
33*7dd7cddfSDavid du Colombier 	if (tsize)
34*7dd7cddfSDavid du Colombier 		texpand(tp, tsize);
35*7dd7cddfSDavid du Colombier }
36*7dd7cddfSDavid du Colombier 
37*7dd7cddfSDavid du Colombier static void
texpand(tp,nsize)38*7dd7cddfSDavid du Colombier texpand(tp, nsize)
39*7dd7cddfSDavid du Colombier 	register struct table *tp;
40*7dd7cddfSDavid du Colombier 	int nsize;
41*7dd7cddfSDavid du Colombier {
42*7dd7cddfSDavid du Colombier 	register int i;
43*7dd7cddfSDavid du Colombier 	register struct tbl *tblp, **p;
44*7dd7cddfSDavid du Colombier 	register struct tbl **ntblp, **otblp = tp->tbls;
45*7dd7cddfSDavid du Colombier 	int osize = tp->size;
46*7dd7cddfSDavid du Colombier 
47*7dd7cddfSDavid du Colombier 	ntblp = (struct tbl**) alloc(sizeofN(struct tbl *, nsize), tp->areap);
48*7dd7cddfSDavid du Colombier 	for (i = 0; i < nsize; i++)
49*7dd7cddfSDavid du Colombier 		ntblp[i] = NULL;
50*7dd7cddfSDavid du Colombier 	tp->size = nsize;
51*7dd7cddfSDavid du Colombier 	tp->nfree = 8*nsize/10;	/* table can get 80% full */
52*7dd7cddfSDavid du Colombier 	tp->tbls = ntblp;
53*7dd7cddfSDavid du Colombier 	if (otblp == NULL)
54*7dd7cddfSDavid du Colombier 		return;
55*7dd7cddfSDavid du Colombier 	for (i = 0; i < osize; i++)
56*7dd7cddfSDavid du Colombier 		if ((tblp = otblp[i]) != NULL)
57*7dd7cddfSDavid du Colombier 			if ((tblp->flag&DEFINED)) {
58*7dd7cddfSDavid du Colombier 				for (p = &ntblp[hash(tblp->name)
59*7dd7cddfSDavid du Colombier 					  & (tp->size-1)];
60*7dd7cddfSDavid du Colombier 				     *p != NULL; p--)
61*7dd7cddfSDavid du Colombier 					if (p == ntblp) /* wrap */
62*7dd7cddfSDavid du Colombier 						p += tp->size;
63*7dd7cddfSDavid du Colombier 				*p = tblp;
64*7dd7cddfSDavid du Colombier 				tp->nfree--;
65*7dd7cddfSDavid du Colombier 			} else if (!(tblp->flag & FINUSE)) {
66*7dd7cddfSDavid du Colombier 				afree((void*)tblp, tp->areap);
67*7dd7cddfSDavid du Colombier 			}
68*7dd7cddfSDavid du Colombier 	afree((void*)otblp, tp->areap);
69*7dd7cddfSDavid du Colombier }
70*7dd7cddfSDavid du Colombier 
71*7dd7cddfSDavid du Colombier struct tbl *
tsearch(tp,n,h)72*7dd7cddfSDavid du Colombier tsearch(tp, n, h)
73*7dd7cddfSDavid du Colombier 	register struct table *tp;	/* table */
74*7dd7cddfSDavid du Colombier 	register const char *n;		/* name to enter */
75*7dd7cddfSDavid du Colombier 	unsigned int h;			/* hash(n) */
76*7dd7cddfSDavid du Colombier {
77*7dd7cddfSDavid du Colombier 	register struct tbl **pp, *p;
78*7dd7cddfSDavid du Colombier 
79*7dd7cddfSDavid du Colombier 	if (tp->size == 0)
80*7dd7cddfSDavid du Colombier 		return NULL;
81*7dd7cddfSDavid du Colombier 
82*7dd7cddfSDavid du Colombier 	/* search for name in hashed table */
83*7dd7cddfSDavid du Colombier 	for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
84*7dd7cddfSDavid du Colombier 		if (*p->name == *n && strcmp(p->name, n) == 0
85*7dd7cddfSDavid du Colombier 		    && (p->flag&DEFINED))
86*7dd7cddfSDavid du Colombier 			return p;
87*7dd7cddfSDavid du Colombier 		if (pp == tp->tbls) /* wrap */
88*7dd7cddfSDavid du Colombier 			pp += tp->size;
89*7dd7cddfSDavid du Colombier 	}
90*7dd7cddfSDavid du Colombier 
91*7dd7cddfSDavid du Colombier 	return NULL;
92*7dd7cddfSDavid du Colombier }
93*7dd7cddfSDavid du Colombier 
94*7dd7cddfSDavid du Colombier struct tbl *
tenter(tp,n,h)95*7dd7cddfSDavid du Colombier tenter(tp, n, h)
96*7dd7cddfSDavid du Colombier 	register struct table *tp;	/* table */
97*7dd7cddfSDavid du Colombier 	register const char *n;		/* name to enter */
98*7dd7cddfSDavid du Colombier 	unsigned int h;			/* hash(n) */
99*7dd7cddfSDavid du Colombier {
100*7dd7cddfSDavid du Colombier 	register struct tbl **pp, *p;
101*7dd7cddfSDavid du Colombier 	register int len;
102*7dd7cddfSDavid du Colombier 
103*7dd7cddfSDavid du Colombier 	if (tp->size == 0)
104*7dd7cddfSDavid du Colombier 		texpand(tp, INIT_TBLS);
105*7dd7cddfSDavid du Colombier   Search:
106*7dd7cddfSDavid du Colombier 	/* search for name in hashed table */
107*7dd7cddfSDavid du Colombier 	for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
108*7dd7cddfSDavid du Colombier 		if (*p->name == *n && strcmp(p->name, n) == 0)
109*7dd7cddfSDavid du Colombier 			return p; 	/* found */
110*7dd7cddfSDavid du Colombier 		if (pp == tp->tbls) /* wrap */
111*7dd7cddfSDavid du Colombier 			pp += tp->size;
112*7dd7cddfSDavid du Colombier 	}
113*7dd7cddfSDavid du Colombier 
114*7dd7cddfSDavid du Colombier 	if (tp->nfree <= 0) {	/* too full */
115*7dd7cddfSDavid du Colombier 		texpand(tp, 2*tp->size);
116*7dd7cddfSDavid du Colombier 		goto Search;
117*7dd7cddfSDavid du Colombier 	}
118*7dd7cddfSDavid du Colombier 
119*7dd7cddfSDavid du Colombier 	/* create new tbl entry */
120*7dd7cddfSDavid du Colombier 	len = strlen(n) + 1;
121*7dd7cddfSDavid du Colombier 	p = (struct tbl *) alloc(offsetof(struct tbl, name[0]) + len,
122*7dd7cddfSDavid du Colombier 				 tp->areap);
123*7dd7cddfSDavid du Colombier 	p->flag = 0;
124*7dd7cddfSDavid du Colombier 	p->type = 0;
125*7dd7cddfSDavid du Colombier 	p->areap = tp->areap;
126*7dd7cddfSDavid du Colombier 	p->u2.field = 0;
127*7dd7cddfSDavid du Colombier 	p->u.array = (struct tbl *)0;
128*7dd7cddfSDavid du Colombier 	memcpy(p->name, n, len);
129*7dd7cddfSDavid du Colombier 
130*7dd7cddfSDavid du Colombier 	/* enter in tp->tbls */
131*7dd7cddfSDavid du Colombier 	tp->nfree--;
132*7dd7cddfSDavid du Colombier 	*pp = p;
133*7dd7cddfSDavid du Colombier 	return p;
134*7dd7cddfSDavid du Colombier }
135*7dd7cddfSDavid du Colombier 
136*7dd7cddfSDavid du Colombier void
tdelete(p)137*7dd7cddfSDavid du Colombier tdelete(p)
138*7dd7cddfSDavid du Colombier 	register struct tbl *p;
139*7dd7cddfSDavid du Colombier {
140*7dd7cddfSDavid du Colombier 	p->flag = 0;
141*7dd7cddfSDavid du Colombier }
142*7dd7cddfSDavid du Colombier 
143*7dd7cddfSDavid du Colombier void
twalk(ts,tp)144*7dd7cddfSDavid du Colombier twalk(ts, tp)
145*7dd7cddfSDavid du Colombier 	struct tstate *ts;
146*7dd7cddfSDavid du Colombier 	struct table *tp;
147*7dd7cddfSDavid du Colombier {
148*7dd7cddfSDavid du Colombier 	ts->left = tp->size;
149*7dd7cddfSDavid du Colombier 	ts->next = tp->tbls;
150*7dd7cddfSDavid du Colombier }
151*7dd7cddfSDavid du Colombier 
152*7dd7cddfSDavid du Colombier struct tbl *
tnext(ts)153*7dd7cddfSDavid du Colombier tnext(ts)
154*7dd7cddfSDavid du Colombier 	struct tstate *ts;
155*7dd7cddfSDavid du Colombier {
156*7dd7cddfSDavid du Colombier 	while (--ts->left >= 0) {
157*7dd7cddfSDavid du Colombier 		struct tbl *p = *ts->next++;
158*7dd7cddfSDavid du Colombier 		if (p != NULL && (p->flag&DEFINED))
159*7dd7cddfSDavid du Colombier 			return p;
160*7dd7cddfSDavid du Colombier 	}
161*7dd7cddfSDavid du Colombier 	return NULL;
162*7dd7cddfSDavid du Colombier }
163*7dd7cddfSDavid du Colombier 
164*7dd7cddfSDavid du Colombier static int
tnamecmp(p1,p2)165*7dd7cddfSDavid du Colombier tnamecmp(p1, p2)
166*7dd7cddfSDavid du Colombier 	void *p1, *p2;
167*7dd7cddfSDavid du Colombier {
168*7dd7cddfSDavid du Colombier 	return strcmp(((struct tbl *)p1)->name, ((struct tbl *)p2)->name);
169*7dd7cddfSDavid du Colombier }
170*7dd7cddfSDavid du Colombier 
171*7dd7cddfSDavid du Colombier struct tbl **
tsort(tp)172*7dd7cddfSDavid du Colombier tsort(tp)
173*7dd7cddfSDavid du Colombier 	register struct table *tp;
174*7dd7cddfSDavid du Colombier {
175*7dd7cddfSDavid du Colombier 	register int i;
176*7dd7cddfSDavid du Colombier 	register struct tbl **p, **sp, **dp;
177*7dd7cddfSDavid du Colombier 
178*7dd7cddfSDavid du Colombier 	p = (struct tbl **)alloc(sizeofN(struct tbl *, tp->size+1), ATEMP);
179*7dd7cddfSDavid du Colombier 	sp = tp->tbls;		/* source */
180*7dd7cddfSDavid du Colombier 	dp = p;			/* dest */
181*7dd7cddfSDavid du Colombier 	for (i = 0; i < tp->size; i++)
182*7dd7cddfSDavid du Colombier 		if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) ||
183*7dd7cddfSDavid du Colombier 					      ((*dp)->flag&ARRAY)))
184*7dd7cddfSDavid du Colombier 			dp++;
185*7dd7cddfSDavid du Colombier 	i = dp - p;
186*7dd7cddfSDavid du Colombier 	qsortp((void**)p, (size_t)i, tnamecmp);
187*7dd7cddfSDavid du Colombier 	p[i] = NULL;
188*7dd7cddfSDavid du Colombier 	return p;
189*7dd7cddfSDavid du Colombier }
190*7dd7cddfSDavid du Colombier 
191*7dd7cddfSDavid du Colombier #ifdef PERF_DEBUG /* performance debugging */
192*7dd7cddfSDavid du Colombier 
193*7dd7cddfSDavid du Colombier void tprintinfo ARGS((struct table *tp));
194*7dd7cddfSDavid du Colombier 
195*7dd7cddfSDavid du Colombier void
tprintinfo(tp)196*7dd7cddfSDavid du Colombier tprintinfo(tp)
197*7dd7cddfSDavid du Colombier 	struct table *tp;
198*7dd7cddfSDavid du Colombier {
199*7dd7cddfSDavid du Colombier 	struct tbl *te;
200*7dd7cddfSDavid du Colombier 	char *n;
201*7dd7cddfSDavid du Colombier 	unsigned int h;
202*7dd7cddfSDavid du Colombier 	int ncmp;
203*7dd7cddfSDavid du Colombier 	int totncmp = 0, maxncmp = 0;
204*7dd7cddfSDavid du Colombier 	int nentries = 0;
205*7dd7cddfSDavid du Colombier 	struct tstate ts;
206*7dd7cddfSDavid du Colombier 
207*7dd7cddfSDavid du Colombier 	shellf("table size %d, nfree %d\n", tp->size, tp->nfree);
208*7dd7cddfSDavid du Colombier 	shellf("    Ncmp name\n");
209*7dd7cddfSDavid du Colombier 	twalk(&ts, tp);
210*7dd7cddfSDavid du Colombier 	while ((te = tnext(&ts))) {
211*7dd7cddfSDavid du Colombier 		register struct tbl **pp, *p;
212*7dd7cddfSDavid du Colombier 
213*7dd7cddfSDavid du Colombier 		h = hash(n = te->name);
214*7dd7cddfSDavid du Colombier 		ncmp = 0;
215*7dd7cddfSDavid du Colombier 
216*7dd7cddfSDavid du Colombier 		/* taken from tsearch() and added counter */
217*7dd7cddfSDavid du Colombier 		for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) {
218*7dd7cddfSDavid du Colombier 			ncmp++;
219*7dd7cddfSDavid du Colombier 			if (*p->name == *n && strcmp(p->name, n) == 0
220*7dd7cddfSDavid du Colombier 			    && (p->flag&DEFINED))
221*7dd7cddfSDavid du Colombier 				break; /* return p; */
222*7dd7cddfSDavid du Colombier 			if (pp == tp->tbls) /* wrap */
223*7dd7cddfSDavid du Colombier 				pp += tp->size;
224*7dd7cddfSDavid du Colombier 		}
225*7dd7cddfSDavid du Colombier 		shellf("    %4d %s\n", ncmp, n);
226*7dd7cddfSDavid du Colombier 		totncmp += ncmp;
227*7dd7cddfSDavid du Colombier 		nentries++;
228*7dd7cddfSDavid du Colombier 		if (ncmp > maxncmp)
229*7dd7cddfSDavid du Colombier 			maxncmp = ncmp;
230*7dd7cddfSDavid du Colombier 	}
231*7dd7cddfSDavid du Colombier 	if (nentries)
232*7dd7cddfSDavid du Colombier 		shellf("  %d entries, worst ncmp %d, avg ncmp %d.%02d\n",
233*7dd7cddfSDavid du Colombier 			nentries, maxncmp,
234*7dd7cddfSDavid du Colombier 			totncmp / nentries,
235*7dd7cddfSDavid du Colombier 			(totncmp % nentries) * 100 / nentries);
236*7dd7cddfSDavid du Colombier }
237*7dd7cddfSDavid du Colombier #endif /* PERF_DEBUG */
238