xref: /plan9/sys/src/cmd/ndb/mkhash.c (revision 1206f3fc1b0aab3e32fa15899d31a9b5bfa82d9f)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ndb.h>
5 
6 /*
7  *  make the hash table completely in memory and then write as a file
8  */
9 
10 uchar *ht;
11 ulong hlen;
12 Ndb *db;
13 ulong nextchain;
14 
15 char*
syserr(void)16 syserr(void)
17 {
18 	static char buf[ERRMAX];
19 
20 	errstr(buf, sizeof buf);
21 	return buf;
22 }
23 
24 void
enter(char * val,ulong dboff)25 enter(char *val, ulong dboff)
26 {
27 	ulong h;
28 	uchar *last;
29 	ulong ptr;
30 
31 	h = ndbhash(val, hlen);
32 	h *= NDBPLEN;
33 	last = &ht[h];
34 	ptr = NDBGETP(last);
35 	if(ptr == NDBNAP){
36 		NDBPUTP(dboff, last);
37 		return;
38 	}
39 
40 	if(ptr & NDBCHAIN){
41 		/* walk the chain to the last entry */
42 		for(;;){
43 			ptr &= ~NDBCHAIN;
44 			last = &ht[ptr+NDBPLEN];
45 			ptr = NDBGETP(last);
46 			if(ptr == NDBNAP){
47 				NDBPUTP(dboff, last);
48 				return;
49 			}
50 			if(!(ptr & NDBCHAIN)){
51 				NDBPUTP(nextchain|NDBCHAIN, last);
52 				break;
53 			}
54 		}
55 	} else
56 		NDBPUTP(nextchain|NDBCHAIN, last);
57 
58 	/* add a chained entry */
59 	NDBPUTP(ptr, &ht[nextchain]);
60 	NDBPUTP(dboff, &ht[nextchain + NDBPLEN]);
61 	nextchain += 2*NDBPLEN;
62 }
63 
64 uchar nbuf[16*1024];
65 
66 void
main(int argc,char ** argv)67 main(int argc, char **argv)
68 {
69 	Ndbtuple *t, *nt;
70 	int n;
71 	Dir *d;
72 	uchar buf[8];
73 	char file[128];
74 	int fd;
75 	ulong off;
76 	uchar *p;
77 
78 	if(argc != 3){
79 		fprint(2, "usage: mkhash file attribute\n");
80 		exits("usage");
81 	}
82 	db = ndbopen(argv[1]);
83 	if(db == 0){
84 		fprint(2, "mkhash: can't open %s\n", argv[1]);
85 		exits(syserr());
86 	}
87 
88 	/* try a bigger than normal buffer */
89 	Binits(&db->b, Bfildes(&db->b), OREAD, nbuf, sizeof(nbuf));
90 
91 	/* count entries to calculate hash size */
92 	n = 0;
93 
94 	while(nt = ndbparse(db)){
95 		for(t = nt; t; t = t->entry){
96 			if(strcmp(t->attr, argv[2]) == 0)
97 				n++;
98 		}
99 		ndbfree(nt);
100 	}
101 
102 	/* allocate an array large enough for worst case */
103 	hlen = 2*n+1;
104 	n = hlen*NDBPLEN + hlen*2*NDBPLEN;
105 	ht = mallocz(n, 1);
106 	if(ht == 0){
107 		fprint(2, "mkhash: not enough memory\n");
108 		exits(syserr());
109 	}
110 	for(p = ht; p < &ht[n]; p += NDBPLEN)
111 		NDBPUTP(NDBNAP, p);
112 	nextchain = hlen*NDBPLEN;
113 
114 	/* create the in core hash table */
115 	Bseek(&db->b, 0, 0);
116 	off = 0;
117 	while(nt = ndbparse(db)){
118 		for(t = nt; t; t = t->entry){
119 			if(strcmp(t->attr, argv[2]) == 0)
120 				enter(t->val, off);
121 		}
122 		ndbfree(nt);
123 		off = Boffset(&db->b);
124 	}
125 
126 	/* create the hash file */
127 	snprint(file, sizeof(file), "%s.%s", argv[1], argv[2]);
128 	fd = create(file, ORDWR, 0664);
129 	if(fd < 0){
130 		fprint(2, "mkhash: can't create %s\n", file);
131 		exits(syserr());
132 	}
133 	NDBPUTUL(db->mtime, buf);
134 	NDBPUTUL(hlen, buf+NDBULLEN);
135 	if(write(fd, buf, NDBHLEN) != NDBHLEN){
136 		fprint(2, "mkhash: writing %s\n", file);
137 		exits(syserr());
138 	}
139 	if(write(fd, ht, nextchain) != nextchain){
140 		fprint(2, "mkhash: writing %s\n", file);
141 		exits(syserr());
142 	}
143 	close(fd);
144 
145 	/* make sure file didn't change while we were making the hash */
146 	d = dirstat(argv[1]);
147 	if(d == nil || d->qid.path != db->qid.path
148 	   || d->qid.vers != db->qid.vers){
149 		fprint(2, "mkhash: %s changed underfoot\n", argv[1]);
150 		remove(file);
151 		exits("changed");
152 	}
153 
154 	exits(0);
155 }
156