1 /*
2  * mnode.c - mnode interfaces.
3  *
4  * Written By:  MURAOKA Taro <koron@tka.att.ne.jp>
5  * Last Change: 04-May-2004.
6  */
7 module migemo_d.mnode;
8 
9 
10 private static import core.memory;
11 private static import core.stdc.ctype;
12 private static import core.stdc.stdio;
13 private static import migemo_d.wordbuf;
14 private static import migemo_d.wordlist;
15 
16 /* ツリーオブジェクト */
17 public alias mnode = ._mnode;
18 
19 extern (C)
20 public struct _mnode
21 {
22 	uint attr;
23 	.mnode* next;
24 	.mnode* child;
25 	migemo_d.wordlist.wordlist_p list;
26 }
27 
28 public enum MNODE_MASK_CH = 0x000000FF;
29 
30 pragma(inline, true)
31 pure nothrow @trusted @nogc @live
32 public char MNODE_GET_CH(.mnode* p)
33 
34 	in
35 	{
36 		assert(p != null);
37 	}
38 
39 	do
40 	{
41 		return cast(char)(p.attr);
42 	}
43 
44 pragma(inline, true)
45 pure nothrow @trusted @nogc @live
46 public void MNODE_SET_CH(.mnode* p, uint c)
47 
48 	in
49 	{
50 		assert(p != null);
51 	}
52 
53 	do
54 	{
55 		p.attr = c;
56 	}
57 
58 /* for mnode_traverse() */
59 public alias mnode_traverse_proc = extern (C) nothrow @nogc void function(.mnode* node, void* data);
60 public alias MNODE_TRAVERSE_PROC = .mnode_traverse_proc;
61 
62 public int n_mnode_new = 0;
63 public int n_mnode_delete = 0;
64 
65 enum MTREE_MNODE_N = 1024;
66 
67 extern (C)
68 struct _mtree_t
69 {
70 	.mtree_p active;
71 	int used;
72 	.mnode[.MTREE_MNODE_N] nodes;
73 	.mtree_p next;
74 }
75 
76 public alias mtree_t = ._mtree_t;
77 public alias mtree_p = ._mtree_t*;
78 
79 enum MNODE_BUFSIZE = 16384;
80 
81 pragma(inline, true)
82 nothrow @nogc
83 package .mnode* mnode_new(.mtree_p mtree)
84 
85 	in
86 	{
87 		assert(mtree != null);
88 	}
89 
90 	do
91 	{
92 		.mtree_p active = mtree.active;
93 
94 		if (active.used >= .MTREE_MNODE_N) {
95 			active.next = cast(.mtree_p)(core.memory.pureCalloc(1, (*active.next).sizeof));
96 			/* TODO: エラー処理 */
97 			mtree.active = active.next;
98 			active = active.next;
99 		}
100 
101 		++.n_mnode_new;
102 
103 		return &active.nodes[active.used++];
104 	}
105 
106 nothrow @nogc
107 package void mnode_delete(.mnode* p)
108 
109 	do
110 	{
111 		while (p != null) {
112 			.mnode* child = p.child;
113 
114 			if (p.list) {
115 				migemo_d.wordlist.wordlist_close(p.list);
116 				p.list = null;
117 			}
118 
119 			if (p.next != null) {
120 				.mnode_delete(p.next);
121 				p.next = null;
122 			}
123 
124 			/*core.memory.pureFree(p);*/
125 			p = child;
126 			++.n_mnode_delete;
127 		}
128 	}
129 
130 nothrow @nogc
131 void mnode_print_stub(.mnode* vp, char* p)
132 
133 	do
134 	{
135 		static char[256] buf;
136 
137 		if (vp == null) {
138 			return;
139 		}
140 
141 		if (p == null) {
142 			p = &(buf[0]);
143 		}
144 
145 		p[0] = .MNODE_GET_CH(vp);
146 		p[1] = '\0';
147 
148 		if (vp.list) {
149 			core.stdc.stdio.printf("%s (list=%p)\n", &(buf[0]), vp.list);
150 		}
151 
152 		if (vp.child != null) {
153 			.mnode_print_stub(vp.child, p + 1);
154 		}
155 
156 		if (vp.next != null) {
157 			.mnode_print_stub(vp.next, p);
158 		}
159 	}
160 
161 extern (C)
162 nothrow @nogc
163 public void mnode_print(.mtree_p mtree, char* p)
164 
165 	do
166 	{
167 		if ((mtree != null) && (mtree.used > 0)) {
168 			.mnode_print_stub(&mtree.nodes[0], p);
169 		}
170 	}
171 
172 extern (C)
173 nothrow @nogc
174 public void mnode_close(.mtree_p mtree)
175 
176 	in
177 	{
178 	}
179 
180 	do
181 	{
182 		if (mtree != null) {
183 			if (mtree.used > 0) {
184 				.mnode_delete(&mtree.nodes[0]);
185 			}
186 
187 			while (mtree != null) {
188 				.mtree_p next = mtree.next;
189 				core.memory.pureFree(mtree);
190 				mtree = next;
191 			}
192 		}
193 	}
194 
195 //pragma(inline, true)
196 nothrow @nogc
197 package .mnode* search_or_new_mnode(.mtree_p mtree, migemo_d.wordbuf.wordbuf_p buf)
198 
199 	in
200 	{
201 		assert(mtree != null);
202 		assert(buf != null);
203 	}
204 
205 	do
206 	{
207 		/* To suppress warning for GCC */
208 		.mnode** res = null;
209 
210 		char* word = migemo_d.wordbuf.WORDBUF_GET(buf);
211 		.mnode* root = (mtree.used > 0) ? (&mtree.nodes[0]) : (null);
212 		.mnode** ppnext = &root;
213 
214 		/* ラベル単語が決定したら検索木に追加 */
215 		int ch;
216 
217 		while ((ch = *word) != 0) {
218 			res = ppnext;
219 
220 			if (!*res) {
221 				*res = .mnode_new(mtree);
222 				.MNODE_SET_CH(*res, ch);
223 			} else if (.MNODE_GET_CH(*res) != ch) {
224 				ppnext = &(*res).next;
225 
226 				continue;
227 			}
228 
229 			ppnext = &(*res).child;
230 			++word;
231 		}
232 
233 		assert(*res != null);
234 
235 		return *res;
236 	}
237 
238 /**
239  * 既存のノードにファイルからデータをまとめて追加する。
240  */
241 extern (C)
242 nothrow @nogc
243 public .mtree_p mnode_load(.mtree_p mtree, core.stdc.stdio.FILE* fp)
244 
245 	do
246 	{
247 		/* To suppress warning for GCC */
248 		migemo_d.wordlist.wordlist_p* ppword = null;
249 
250 		/* 読み込みバッファ用変数 */
251 		char[.MNODE_BUFSIZE] cache;
252 		char* cache_ptr = &(cache[0]);
253 		char* cache_tail = &(cache[0]);
254 
255 		migemo_d.wordbuf.wordbuf_p buf = migemo_d.wordbuf.wordbuf_open();
256 		migemo_d.wordbuf.wordbuf_p prevlabel = migemo_d.wordbuf.wordbuf_open();
257 
258 		scope (exit) {
259 			if (buf != null) {
260 				migemo_d.wordbuf.wordbuf_close(buf);
261 				buf = null;
262 			}
263 
264 			if (prevlabel != null) {
265 				migemo_d.wordbuf.wordbuf_close(prevlabel);
266 				prevlabel = null;
267 			}
268 		}
269 
270 		if ((fp == null) || (buf == null) || (prevlabel == null)) {
271 			return mtree;
272 		}
273 
274 		.mnode* pp = null;
275 		int mode = 0;
276 		int ch;
277 
278 		/*
279 		 * EOFの処理が曖昧。不正な形式のファイルが入った場合を考慮していない。各
280 		 * モードからEOFの道を用意しないと正しくないが…面倒なのでやらない。デー
281 		 * タファイルは絶対に間違っていないという前提を置く。
282 		 */
283 		do {
284 			if (cache_ptr >= cache_tail) {
285 				cache_ptr = &(cache[0]);
286 				cache_tail = &(cache[0]) + core.stdc.stdio.fread(&(cache[0]), 1, .MNODE_BUFSIZE, fp);
287 				ch = (((cache_tail <= &(cache[0])) && (core.stdc.stdio.feof(fp)))) ? (core.stdc.stdio.EOF) : (*cache_ptr);
288 			} else {
289 				ch = *cache_ptr;
290 			}
291 
292 			++cache_ptr;
293 
294 			/* 状態:modeのオートマトン */
295 			switch (mode) {
296 				case 0: /* ラベル単語検索モード */
297 					/* 空白はラベル単語になりえません */
298 					if ((core.stdc.ctype.isspace(ch)) || (ch == core.stdc.stdio.EOF)) {
299 						continue;
300 					}
301 					/* コメントラインチェック */
302 					else if (ch == ';') {
303 						/* 行末まで食い潰すモード へ移行 */
304 						mode = 2;
305 
306 						continue;
307 					} else {
308 						/* ラベル単語の読込モード へ移行*/
309 						mode = 1;
310 
311 						migemo_d.wordbuf.wordbuf_reset(buf);
312 						migemo_d.wordbuf.wordbuf_add(buf, cast(char)(ch));
313 					}
314 
315 					break;
316 
317 				case 1: /* ラベル単語の読込モード */
318 					/* ラベルの終了を検出 */
319 					switch (ch) {
320 						default:
321 							migemo_d.wordbuf.wordbuf_add(buf, cast(char)(ch));
322 
323 							break;
324 
325 						case '\t':
326 							pp = .search_or_new_mnode(mtree, buf);
327 							migemo_d.wordbuf.wordbuf_reset(buf);
328 
329 							/* 単語前空白読飛ばしモード へ移行 */
330 							mode = 3;
331 
332 							break;
333 					}
334 
335 					break;
336 
337 				case 2: /* 行末まで食い潰すモード */
338 					if (ch == '\n') {
339 						migemo_d.wordbuf.wordbuf_reset(buf);
340 
341 						/* ラベル単語検索モード へ戻る */
342 						mode = 0;
343 					}
344 
345 					break;
346 
347 				case 3: /* 単語前空白読み飛ばしモード */
348 					if (ch == '\n') {
349 						migemo_d.wordbuf.wordbuf_reset(buf);
350 
351 						/* ラベル単語検索モード へ戻る */
352 						mode = 0;
353 					} else if (ch != '\t') {
354 						/* 単語バッファリセット */
355 						migemo_d.wordbuf.wordbuf_reset(buf);
356 						migemo_d.wordbuf.wordbuf_add(buf, cast(char)(ch));
357 						/* 単語リストの最後を検索(同一ラベルが複数時) */
358 						ppword = &pp.list;
359 
360 						while (*ppword != null) {
361 							ppword = &(*ppword).next;
362 						}
363 
364 						/* 単語の読み込みモード へ移行 */
365 						mode = 4;
366 					}
367 
368 					break;
369 
370 				case 4: /* 単語の読み込みモード */
371 					switch (ch) {
372 						case '\t':
373 						case '\n':
374 							/* 単語を記憶 */
375 							*ppword = migemo_d.wordlist.wordlist_open_len(migemo_d.wordbuf.WORDBUF_GET(buf), migemo_d.wordbuf.WORDBUF_LEN(buf));
376 							migemo_d.wordbuf.wordbuf_reset(buf);
377 
378 							if (ch == '\t') {
379 								ppword = &(*ppword).next;
380 
381 								/* 単語前空白読み飛ばしモード へ戻る */
382 								mode = 3;
383 							} else {
384 								ppword = null;
385 
386 								/* ラベル単語検索モード へ戻る */
387 								mode = 0;
388 							}
389 
390 							break;
391 
392 						default:
393 							migemo_d.wordbuf.wordbuf_add(buf, cast(char)(ch));
394 
395 							break;
396 					}
397 
398 					break;
399 
400 				default:
401 					break;
402 			}
403 		} while (ch != core.stdc.stdio.EOF);
404 
405 		return mtree;
406 	}
407 
408 extern (C)
409 nothrow @nogc
410 public .mtree_p mnode_open(core.stdc.stdio.FILE* fp)
411 
412 	in
413 	{
414 	}
415 
416 	do
417 	{
418 		.mtree_p mtree = cast(.mtree_p)(core.memory.pureCalloc(1, (*.mtree_p).sizeof));
419 		mtree.active = mtree;
420 
421 		if ((mtree != null) && (fp != null)) {
422 			.mnode_load(mtree, fp);
423 		}
424 
425 		return mtree;
426 	}
427 
428 version (none) {
429 	pure nothrow @trusted @nogc @live
430 	package int mnode_size(.mnode* p)
431 
432 		do
433 		{
434 			return (p != null) ? (.mnode_size(p.child) + .mnode_size(p.next) + 1) : (0);
435 		}
436 }
437 
438 pure nothrow @trusted @nogc @live
439 package .mnode* mnode_query_stub(.mnode* node, const (char)* query)
440 
441 	in
442 	{
443 		assert(node != null);
444 		assert(query != null);
445 	}
446 
447 	do
448 	{
449 		while (true) {
450 			if (*query == .MNODE_GET_CH(node)) {
451 				return (*++query == '\0') ? (node) : ((node.child != null) ? (.mnode_query_stub(node.child, query)) : (null));
452 			}
453 
454 			node = node.next;
455 
456 			if (node == null) {
457 				break;
458 			}
459 		}
460 
461 		return null;
462 	}
463 
464 extern (C)
465 pure nothrow @trusted @nogc @live
466 public .mnode* mnode_query(.mtree_p mtree, const (char)* query)
467 
468 	do
469 	{
470 		return ((query != null) && (*query != '\0') && (mtree != null)) ? (.mnode_query_stub(&mtree.nodes[0], query)) : (null);
471 	}
472 
473 nothrow @nogc
474 package void mnode_traverse_stub(.mnode* node, .MNODE_TRAVERSE_PROC proc, void* data)
475 
476 	in
477 	{
478 		assert(node != null);
479 	}
480 
481 	do
482 	{
483 		while (true) {
484 			if (node.child != null) {
485 				.mnode_traverse_stub(node.child, proc, data);
486 			}
487 
488 			proc(node, data);
489 			node = node.next;
490 
491 			if (node == null) {
492 				break;
493 			}
494 		}
495 	}
496 
497 extern (C)
498 nothrow @nogc
499 public void mnode_traverse(.mnode* node, .MNODE_TRAVERSE_PROC proc, void* data)
500 
501 	do
502 	{
503 		if ((node != null) && (proc != null)) {
504 			proc(node, data);
505 
506 			if (node.child != null) {
507 				.mnode_traverse_stub(node.child, proc, data);
508 			}
509 		}
510 	}