1 /*
2  * Word Dictionary
3  * ファイルの辞書のインターフェース、存在するデータは
4  * キャッシュされるのでここでは存在しない単語の
5  * サーチを高速にする必要がある。
6  *
7  * anthy_gang_fill_seq_ent()が中心となる関数である
8  *  指定したword_dicから指定した文字列をインデックスとしてもつエントリに
9  *  語尾を付加してseq_entに追加する
10  *
11  * a)辞書の形式とb)辞書アクセスの高速化c)辞書ファイルのエンコーディング
12  *  このソース中で扱ってるのでかなり複雑化してます.
13  *
14  * Copyright (C) 2000-2007 TABATA Yusuke
15  * Copyright (C) 2005-2006 YOSHIDA Yuichi
16  * Copyright (C) 2001-2002 TAKAI Kosuke
17  *
18  */
19 /*
20   This library is free software; you can redistribute it and/or
21   modify it under the terms of the GNU Lesser General Public
22   License as published by the Free Software Foundation; either
23   version 2 of the License, or (at your option) any later version.
24 
25   This library is distributed in the hope that it will be useful,
26   but WITHOUT ANY WARRANTY; without even the implied warranty of
27   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28   Lesser General Public License for more details.
29 
30   You should have received a copy of the GNU Lesser General Public
31   License along with this library; if not, write to the Free Software
32   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 
40 #include "config.h"
41 #include <anthy/anthy.h>
42 #include <anthy/alloc.h>
43 #include <anthy/dic.h>
44 #include <anthy/word_dic.h>
45 #include <anthy/logger.h>
46 #include <anthy/xstr.h>
47 #include <anthy/diclib.h>
48 
49 #include "dic_main.h"
50 #include "dic_ent.h"
51 
52 #define NO_WORD -1
53 
54 static allocator word_dic_ator;
55 
56 struct lookup_context {
57   struct gang_elm **array;
58   int nr;
59   int nth;
60   int is_reverse;
61 };
62 
63 /* 1バイト目を見て、文字が何バイトあるかを返す */
64 static int
mb_fragment_len(const char * str)65 mb_fragment_len(const char *str)
66 {
67   unsigned char c = *((const unsigned char *)str);
68   if (c < 0x80) {
69     return 1;
70   }
71   if (c < 0xe0) {
72     return 2;
73   }
74   if (c < 0xf0) {
75     return 3;
76   }
77   if (c < 0xf8) {
78     return 4;
79   }
80   if (c < 0xfc) {
81     return 5;
82   }
83   return 6;
84 }
85 
86 static int
is_printable(char * str)87 is_printable(char *str)
88 {
89   unsigned char *tmp = (unsigned char *)str;
90   if (*tmp > 31 && *tmp < 127) {
91     return 1;
92   }
93   if (mb_fragment_len(str) > 1) {
94     return 1;
95   }
96   return 0;
97 }
98 
99 /* 辞書のエンコーディングからxcharを作る */
100 static xchar
form_mb_char(const char * str)101 form_mb_char(const char *str)
102 {
103   xchar xc;
104   anthy_utf8_to_ucs4_xchar(str, &xc);
105   return xc;
106 }
107 
108 static int
hash(xstr * x)109 hash(xstr *x)
110 {
111   return anthy_xstr_hash(x)&
112     (YOMI_HASH_ARRAY_SIZE*YOMI_HASH_ARRAY_BITS-1);
113 }
114 
115 static int
check_hash_ent(struct word_dic * wdic,xstr * xs)116 check_hash_ent(struct word_dic *wdic, xstr *xs)
117 {
118   int val = hash(xs);
119   int idx = (val>>YOMI_HASH_ARRAY_SHIFT)&(YOMI_HASH_ARRAY_SIZE-1);
120   int bit = val & ((1<<YOMI_HASH_ARRAY_SHIFT)-1);
121   return wdic->hash_ent[idx] & (1<<bit);
122 }
123 
124 static int
wtype_str_len(const char * str)125 wtype_str_len(const char *str)
126 {
127   int i;
128   for (i = 0; str[i] && str[i]!= ' '; i++);
129   return i;
130 }
131 
132 /* 辞書の行中をスキャンするための状態保持 */
133 struct wt_stat {
134   wtype_t wt;
135   const char *wt_name;
136   int feature;
137   int freq;
138   int order_bonus;/* 辞書中の順序による頻度のボーナス */
139   int offset;/* 文字列中のオフセット */
140   const char *line;
141   int encoding;
142 };
143 /*
144  * #XX*123 というCannadicの形式をパーズする
145  *  #XX
146  *  #XX*123
147  *  #XX,x*123
148  */
149 static const char *
parse_wtype_str(struct wt_stat * ws)150 parse_wtype_str(struct wt_stat *ws)
151 {
152   int len;
153   char *buf;
154   char *freq_part;
155   char *feature_part;
156   const char *wt_name;
157   /* バッファへコピーする */
158   len = wtype_str_len(&ws->line[ws->offset]);
159   buf = alloca(len + 1);
160   strncpy(buf, &ws->line[ws->offset], len);
161   buf[len] = 0;
162 
163   /* 素性(未使用) */
164   feature_part = strchr(buf, ',');
165   if (feature_part) {
166     ws->feature = 1;
167   } else {
168     ws->feature = 0;
169   }
170 
171   /* 頻度をparseする */
172   freq_part = strchr(buf, '*');
173   if (freq_part) {
174     *freq_part = 0;
175     freq_part ++;
176     ws->freq = atoi(freq_part) * FREQ_RATIO;
177   } else {
178     ws->freq = FREQ_RATIO - 2;
179   }
180 
181   /**/
182   wt_name = anthy_type_to_wtype(buf, &ws->wt);
183   if (!wt_name) {
184     ws->wt = anthy_wt_none;
185   }
186   ws->offset += len;
187   return wt_name;
188 }
189 
190 
191 static int
normalize_freq(struct wt_stat * ws)192 normalize_freq(struct wt_stat* ws)
193 {
194   if (ws->freq < 0) {
195     ws->freq *= -1;
196   }
197   return ws->freq + ws->order_bonus;
198 }
199 
200 /* '\\'によるエスケープに対応したコピー */
201 static void
copy_to_buf(char * buf,const char * src,int char_count)202 copy_to_buf(char *buf, const char *src, int char_count)
203 {
204   int pos;
205   int i;
206   pos = 0;
207   for (i = 0; i < char_count; i++){
208     if (src[i] == '\\') {
209       if (src[i + 1] == ' ') {
210 	i ++;
211       } else if (src[i + 1] == '\\') {
212 	i ++;
213       }
214     }
215     buf[pos] = src[i];
216     pos ++;
217   }
218   buf[pos] = 0;
219 }
220 
221 /** seq_entにdic_entを追加する */
222 static int
add_dic_ent(struct seq_ent * seq,struct wt_stat * ws,xstr * yomi,int is_reverse)223 add_dic_ent(struct seq_ent *seq, struct wt_stat *ws,
224 	    xstr* yomi, int is_reverse)
225 {
226   int i;
227   /* 辞書ファイル中のバイト数 */
228   int char_count;
229   char *buf;
230   xstr *xs;
231   int freq;
232   wtype_t w = ws->wt;
233   const char *s = &ws->line[ws->offset];
234 
235   /* 単語の文字数を計算 */
236   for (i = 0, char_count = 0;
237        s[i] && (s[i] != ' ') && (s[i] != '#'); i++) {
238     char_count ++;
239     if (s[i] == '\\') {
240       char_count++;
241       i++;
242     }
243   }
244 
245   /* 品詞が定義されていないので無視 */
246   if (!ws->wt_name) {
247     return char_count;
248   }
249 
250   /* freqが負なのは逆変換用 */
251   if (!is_reverse && ws->freq < 0) {
252     return char_count;
253   }
254 
255   /* bufに単語をコピー */
256   buf = alloca(char_count+1);
257   copy_to_buf(buf, s, char_count);
258 
259   xs = anthy_cstr_to_xstr(buf, ws->encoding);
260 
261   /* freqが正なのは順変換用 */
262   if (is_reverse && ws->freq > 0) {
263     /* 再変換の際に、変換済みの部分と未変換の部分が混じっていた場合に対応する為に、
264        平仮名のみからなる部分は順辞書にその読みを持つ単語があればdic_entを生成する。
265     */
266     if (anthy_get_xstr_type(yomi) & XCT_HIRA) {
267       freq = normalize_freq(ws);
268       anthy_mem_dic_push_back_dic_ent(seq, 0, yomi, w,
269 				      ws->wt_name, freq, 0);
270     }
271     anthy_free_xstr(xs);
272     return char_count;
273   }
274 
275   freq = normalize_freq(ws);
276 
277   anthy_mem_dic_push_back_dic_ent(seq, 0, xs, w, ws->wt_name, freq, 0);
278   anthy_free_xstr(xs);
279   return char_count;
280 }
281 
282 static int
add_compound_ent(struct seq_ent * seq,struct wt_stat * ws,xstr * yomi,int is_reverse)283 add_compound_ent(struct seq_ent *seq, struct wt_stat *ws,
284 		 xstr* yomi,
285 		 int is_reverse)
286 {
287   int len = wtype_str_len(&ws->line[ws->offset]);
288   char *buf = alloca(len);
289   xstr *xs;
290   int freq;
291 
292   (void)yomi;
293 
294   /* freqが負なのは逆変換用 */
295   if (!is_reverse && ws->freq < 0) {
296     /* 普段の変換では要らない */
297     return len;
298   }
299 
300   /* freqが正なのは順変換用 */
301   if (is_reverse && ws->freq > 0) {
302 
303     /* 再変換の際に、変換済みの部分と未変換の部分が混じっていた場合に対応する為に、
304        平仮名のみからなる部分は順辞書にその読みを持つ単語があればdic_entを生成する。
305     */
306     /*
307       yomiに#_等を付加した文字列を作る必要がある
308     if (anthy_get_xstr_type(yomi) & (XCT_HIRA | XCT_KATA)) {
309       freq = normalize_freq(ws);
310       anthy_mem_dic_push_back_compound_ent(seq, xs, ws->wt, freq);
311     }
312     */
313     return len;
314   }
315 
316   strncpy(buf, &ws->line[ws->offset + 1], len - 1);
317   buf[len - 1] = 0;
318   xs = anthy_cstr_to_xstr(buf, ws->encoding);
319 
320   freq = normalize_freq(ws);
321   anthy_mem_dic_push_back_dic_ent(seq, 1, xs, ws->wt,
322 				  ws->wt_name, freq, 0);
323   anthy_free_xstr(xs);
324 
325   return len;
326 }
327 
328 static void
init_wt_stat(struct wt_stat * ws,char * line)329 init_wt_stat(struct wt_stat *ws, char *line)
330 {
331   ws->wt_name = NULL;
332   ws->freq = 0;
333   ws->feature = 0;
334   ws->order_bonus = 0;
335   ws->offset = 0;
336   ws->line = line;
337   ws->encoding = ANTHY_EUC_JP_ENCODING;
338   if (*(ws->line) == 'u') {
339     ws->encoding = ANTHY_UTF8_ENCODING;
340     ws->line ++;
341   }
342 }
343 
344 /** 辞書のエントリの情報を元にseq_entをうめる */
345 static void
fill_dic_ent(char * line,struct seq_ent * seq,xstr * yomi,int is_reverse)346 fill_dic_ent(char *line, struct seq_ent *seq,
347 	     xstr* yomi, int is_reverse)
348 {
349   struct wt_stat ws;
350   init_wt_stat(&ws, line);
351 
352   while (ws.line[ws.offset]) {
353     if (ws.line[ws.offset] == '#') {
354       if (isalpha(ws.line[ws.offset + 1])) {
355 	/* 品詞*頻度 */
356 	ws.wt_name = parse_wtype_str(&ws);
357 	/**/
358 	ws.order_bonus = FREQ_RATIO - 1;
359       } else {
360 	/* 複合語候補 */
361 	ws.offset += add_compound_ent(seq, &ws,
362 				      yomi,
363 				      is_reverse);
364       }
365     } else {
366       /* 単語 */
367       ws.offset += add_dic_ent(seq, &ws, yomi,
368 			       is_reverse);
369       if (ws.order_bonus > 0) {
370 	ws.order_bonus --;
371       }
372     }
373     if (ws.line[ws.offset] == ' ') {
374       ws.offset++;
375     }
376   }
377 }
378 
379 /*
380  * sに書かれた文字列によってxを変更する
381  * 返り値は読み進めたバイト数
382  */
383 static int
mkxstr(char * s,xstr * x)384 mkxstr(char *s, xstr *x)
385 {
386   int i, len;
387   /* s[0]には巻き戻しの文字数 */
388   x->len -= (s[0] - 1);
389   for (i = 1; is_printable(&s[i]); i ++) {
390     len = mb_fragment_len(&s[i]);
391     if (len > 1) {
392       /* マルチバイト */
393       x->str[x->len] = form_mb_char(&s[i]);
394       x->len ++;
395       i += (len - 1);
396     } else {
397       /* 1バイト文字 */
398       x->str[x->len] = s[i];
399       x->len ++;
400     }
401   }
402   return i;
403 }
404 
405 static int
set_next_idx(struct lookup_context * lc)406 set_next_idx(struct lookup_context *lc)
407 {
408   lc->nth ++;
409   while (lc->nth < lc->nr) {
410     if (lc->array[lc->nth]->tmp.idx != NO_WORD) {
411       return 1;
412     }
413     lc->nth ++;
414   }
415   return 0;
416 }
417 
418 /** ページ中の単語の場所を調べる */
419 static void
search_words_in_page(struct lookup_context * lc,int page,char * s)420 search_words_in_page(struct lookup_context *lc, int page, char *s)
421 {
422   int o = 0;
423   xchar *buf;
424   xstr xs;
425   int nr = 0;
426   /* このページ中にあるもっとも長い単語を格納しうる長さ */
427   buf = alloca(sizeof(xchar)*strlen(s)/2);
428   xs.str = buf;
429   xs.len = 0;
430 
431   while (*s) {
432     int r;
433     s += mkxstr(s, &xs);
434     r = anthy_xstrcmp(&xs, &lc->array[lc->nth]->xs);
435     if (!r) {
436       lc->array[lc->nth]->tmp.idx = o + page * WORDS_PER_PAGE;
437       nr ++;
438       if (!set_next_idx(lc)) {
439 	return ;
440       }
441       /* 同じページ内で次の単語を探す */
442     }
443     o ++;
444   }
445   if (nr == 0) {
446     /* このページで1語も見つからなかったら、この単語は無い */
447     lc->array[lc->nth]->tmp.idx = NO_WORD;
448     set_next_idx(lc);
449   }
450   /* 現在の単語は次の呼び出しで探す */
451 }
452 
453 /**/
454 static int
compare_page_index(struct word_dic * wdic,const char * key,int page)455 compare_page_index(struct word_dic *wdic, const char *key, int page)
456 {
457   char buf[100];
458   char *s = &wdic->page[anthy_dic_ntohl(wdic->page_index[page])];
459   int i;
460   s++;
461   for (i = 0; is_printable(&s[i]);) {
462     int j, l = mb_fragment_len(&s[i]);
463     for (j = 0; j < l; j++) {
464       buf[i+j] = s[i+j];
465     }
466     i += l;
467   }
468   buf[i] = 0;
469   return strcmp(key ,buf);
470 }
471 
472 /* 再帰的にバイナリサーチをする */
473 static int
get_page_index_search(struct word_dic * wdic,const char * key,int f,int t)474 get_page_index_search(struct word_dic *wdic, const char *key, int f, int t)
475 {
476   /* anthy_xstrcmpが-1で無くなったところを探す */
477   int c,p;
478   c = (f+t)/2;
479   if (f+1==t) {
480     return c;
481   } else {
482     p = compare_page_index(wdic, key, c);
483     if (p < 0) {
484       return get_page_index_search(wdic, key, f, c);
485     } else {
486       /* c<= <t */
487       return get_page_index_search(wdic, key, c, t);
488     }
489   }
490 }
491 
492 /** keyを含む可能性のあるページの番号を得る、
493  * 範囲チェックをしてバイナリサーチを行うget_page_index_searchを呼ぶ
494  */
495 static int
get_page_index(struct word_dic * wdic,struct lookup_context * lc)496 get_page_index(struct word_dic *wdic, struct lookup_context *lc)
497 {
498   int page;
499   const char *key = lc->array[lc->nth]->key;
500   /* 最初のページの読みよりも小さい */
501   if (compare_page_index(wdic, key, 0) < 0) {
502     return -1;
503   }
504   /* 最後のページの読みよりも大きいので、最後のページに含まれる可能性がある */
505   if (compare_page_index(wdic, key, wdic->nr_pages-1) >= 0) {
506     return wdic->nr_pages-1;
507   }
508   /* 検索する */
509   page = get_page_index_search(wdic, key, 0, wdic->nr_pages);
510   return page;
511 }
512 
513 static int
get_nr_page(struct word_dic * h)514 get_nr_page(struct word_dic *h)
515 {
516   int i;
517   for (i = 1; anthy_dic_ntohl(h->page_index[i]); i++);
518   return i;
519 }
520 
521 static char *
get_section(struct word_dic * wdic,int section)522 get_section(struct word_dic *wdic, int section)
523 {
524   int *p = (int *)wdic->dic_file;
525   int offset = anthy_dic_ntohl(p[section]);
526   return &wdic->dic_file[offset];
527 }
528 
529 /** 辞書ファイルをmmapして、word_dic中の各セクションのポインタを取得する */
530 static int
get_word_dic_sections(struct word_dic * wdic)531 get_word_dic_sections(struct word_dic *wdic)
532 {
533   wdic->entry_index = (int *)get_section(wdic, 2);
534   wdic->entry = (char *)get_section(wdic, 3);
535   wdic->page = (char *)get_section(wdic, 4);
536   wdic->page_index = (int *)get_section(wdic, 5);
537   wdic->uc_section = (char *)get_section(wdic, 6);
538   wdic->hash_ent = (unsigned char *)get_section(wdic, 7);
539 
540   return 0;
541 }
542 
543 /** 指定された単語の辞書中のインデックスを調べる */
544 static void
search_yomi_index(struct word_dic * wdic,struct lookup_context * lc)545 search_yomi_index(struct word_dic *wdic, struct lookup_context *lc)
546 {
547   int p;
548   int page_number;
549 
550   /* すでに無いことが分かっている */
551   if (lc->array[lc->nth]->tmp.idx == NO_WORD) {
552     set_next_idx(lc);
553     return ;
554   }
555 
556   p = get_page_index(wdic, lc);
557   if (p == -1) {
558     lc->array[lc->nth]->tmp.idx = NO_WORD;
559     set_next_idx(lc);
560     return ;
561   }
562 
563   page_number = anthy_dic_ntohl(wdic->page_index[p]);
564   search_words_in_page(lc, p, &wdic->page[page_number]);
565 }
566 
567 static void
find_words(struct word_dic * wdic,struct lookup_context * lc)568 find_words(struct word_dic *wdic, struct lookup_context *lc)
569 {
570   int i;
571   /* 検索前に除去 */
572   for (i = 0; i < lc->nr; i++) {
573     lc->array[i]->tmp.idx = NO_WORD;
574     if (lc->array[i]->xs.len > 31) {
575       /* 32文字以上単語には未対応 */
576       continue;
577     }
578     /* hashにないなら除去 */
579     if (!check_hash_ent(wdic, &lc->array[i]->xs)) {
580       continue;
581     }
582     /* NO_WORDでない値を設定することで検索対象とする */
583     lc->array[i]->tmp.idx = 0;
584   }
585   /* 検索する */
586   lc->nth = 0;
587   while (lc->nth < lc->nr) {
588     search_yomi_index(wdic, lc);
589   }
590 }
591 
592 static void
load_words(struct word_dic * wdic,struct lookup_context * lc)593 load_words(struct word_dic *wdic, struct lookup_context *lc)
594 {
595   int i;
596   for (i = 0; i < lc->nr; i++) {
597     int yomi_index;
598     yomi_index = lc->array[i]->tmp.idx;
599     if (yomi_index != NO_WORD) {
600       int entry_index;
601       struct seq_ent *seq;
602       seq = anthy_cache_get_seq_ent(&lc->array[i]->xs,
603 				    lc->is_reverse);
604       entry_index = anthy_dic_ntohl(wdic->entry_index[yomi_index]);
605       fill_dic_ent(&wdic->entry[entry_index],
606 		   seq,
607 		   &lc->array[i]->xs,
608 		   lc->is_reverse);
609       anthy_validate_seq_ent(seq, &lc->array[i]->xs, lc->is_reverse);
610     }
611   }
612 }
613 
614 /** word_dicから単語を検索する
615  * 辞書キャッシュから呼ばれる
616  * (gang lookupにすることを検討する)
617  */
618 void
anthy_gang_fill_seq_ent(struct word_dic * wdic,struct gang_elm ** array,int nr,int is_reverse)619 anthy_gang_fill_seq_ent(struct word_dic *wdic,
620 			struct gang_elm **array, int nr,
621 			int is_reverse)
622 {
623   struct lookup_context lc;
624   lc.array = array;
625   lc.nr = nr;
626   lc.is_reverse = is_reverse;
627 
628   /* 各単語の場所を探す */
629   find_words(wdic, &lc);
630   /* 単語の情報を読み込む */
631   load_words(wdic, &lc);
632 }
633 
634 struct word_dic *
anthy_create_word_dic(void)635 anthy_create_word_dic(void)
636 {
637   struct word_dic *wdic;
638 
639   wdic = anthy_smalloc(word_dic_ator);
640   memset(wdic, 0, sizeof(*wdic));
641 
642   /* 辞書ファイルをマップする */
643   wdic->dic_file = anthy_file_dic_get_section("word_dic");
644 
645   /* 各セクションのポインタを取得する */
646   if (get_word_dic_sections(wdic) == -1) {
647     anthy_sfree(word_dic_ator, wdic);
648     return 0;
649   }
650   wdic->nr_pages = get_nr_page(wdic);
651 
652   /* 用例辞書をマップする */
653   return wdic;
654 }
655 
656 void
anthy_release_word_dic(struct word_dic * wdic)657 anthy_release_word_dic(struct word_dic *wdic)
658 {
659   anthy_sfree(word_dic_ator, wdic);
660 }
661 
662 void
anthy_init_word_dic(void)663 anthy_init_word_dic(void)
664 {
665   word_dic_ator = anthy_create_allocator(sizeof(struct word_dic), NULL);
666 }
667