xref: /plan9/sys/src/cmd/ndb/mkhash.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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*
16 syserr(void)
17 {
18 	static char buf[ERRLEN];
19 
20 	errstr(buf);
21 	return buf;
22 }
23 
24 void
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
67 main(int argc, char **argv)
68 {
69 	Ndbtuple *t, *nt;
70 	int n;
71 	Dir d;
72 	uchar buf[8];
73 	char file[3*NAMELEN+2];
74 	int fd;
75 	ulong off;
76 	uchar *p;
77 
78 	if(argc != 3){
79 		fprint(2, "mkhash: usage 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 				break;
99 			}
100 		}
101 		ndbfree(nt);
102 	}
103 
104 	/* allocate an array large enough for worst case */
105 	hlen = 2*n+1;
106 	n = hlen*NDBPLEN + hlen*2*NDBPLEN;
107 	ht = malloc(n);
108 	if(ht == 0){
109 		fprint(2, "mkhash: not enough memory\n");
110 		exits(syserr());
111 	}
112 	for(p = ht; p < &ht[n]; p += NDBPLEN)
113 		NDBPUTP(NDBNAP, p);
114 	nextchain = hlen*NDBPLEN;
115 
116 	/* create the in core hash table */
117 	Bseek(&db->b, 0, 0);
118 	off = 0;
119 	while(nt = ndbparse(db)){
120 		for(t = nt; t; t = t->entry){
121 			if(strcmp(t->attr, argv[2]) == 0)
122 				enter(t->val, off);
123 		}
124 		ndbfree(nt);
125 		off = Boffset(&db->b);
126 	}
127 
128 	/* create the hash file */
129 	snprint(file, sizeof(file), "%s.%s", argv[1], argv[2]);
130 	fd = create(file, ORDWR, 0664);
131 	if(fd < 0){
132 		fprint(2, "mkhash: can't create %s\n", file);
133 		exits(syserr());
134 	}
135 	NDBPUTUL(db->mtime, buf);
136 	NDBPUTUL(hlen, buf+NDBULLEN);
137 	if(write(fd, buf, NDBHLEN) != NDBHLEN){
138 		fprint(2, "mkhash: writing %s\n", file);
139 		exits(syserr());
140 	}
141 	if(write(fd, ht, nextchain) != nextchain){
142 		fprint(2, "mkhash: writing %s\n", file);
143 		exits(syserr());
144 	}
145 	close(fd);
146 
147 	/* make sure file didn't change while we were making the hash */
148 	if(dirstat(argv[1], &d) < 0 || d.qid.path != db->qid.path
149 	   || d.qid.vers != db->qid.vers){
150 		fprint(2, "mkhash: %s changed underfoot\n", argv[1]);
151 		remove(file);
152 		exits("changed");
153 	}
154 
155 	exits(0);
156 }
157