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 }