diff options
Diffstat (limited to 'src-worddic/word_lookup.c')
-rw-r--r-- | src-worddic/word_lookup.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/src-worddic/word_lookup.c b/src-worddic/word_lookup.c new file mode 100644 index 0000000..6575b9b --- /dev/null +++ b/src-worddic/word_lookup.c @@ -0,0 +1,673 @@ +/* + * Word Dictionary + * ファイルの辞書のインターフェース、存在するデータは + * キャッシュされるのでここでは存在しない単語の + * サーチを高速にする必要がある。 + * + * anthy_gang_fill_seq_ent()が中心となる関数である + * 指定したword_dicから指定した文字列をインデックスとしてもつエントリに + * 語尾を付加してseq_entに追加する + * + * a)辞書の形式とb)辞書アクセスの高速化c)辞書ファイルのエンコーディング + * このソース中で扱ってるのでかなり複雑化してます. + * + * Copyright (C) 2000-2007 TABATA Yusuke + * Copyright (C) 2005-2006 YOSHIDA Yuichi + * Copyright (C) 2001-2002 TAKAI Kosuke + * + */ +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "config.h" +#include <anthy/anthy.h> +#include <anthy/alloc.h> +#include <anthy/dic.h> +#include <anthy/word_dic.h> +#include <anthy/logger.h> +#include <anthy/xstr.h> +#include <anthy/diclib.h> + +#include "dic_main.h" +#include "dic_ent.h" + +#define NO_WORD -1 + +static allocator word_dic_ator; + +struct lookup_context { + struct gang_elm **array; + int nr; + int nth; + int is_reverse; +}; + +/* 1バイト目を見て、文字が何バイトあるかを返す */ +static int +mb_fragment_len(const char *str) +{ + unsigned char c = *((const unsigned char *)str); + if (c < 0x80) { + return 1; + } + if (c < 0xe0) { + return 2; + } + if (c < 0xf0) { + return 3; + } + if (c < 0xf8) { + return 4; + } + if (c < 0xfc) { + return 5; + } + return 6; +} + +static int +is_printable(char *str) +{ + unsigned char *tmp = (unsigned char *)str; + if (*tmp > 31 && *tmp < 127) { + return 1; + } + if (mb_fragment_len(str) > 1) { + return 1; + } + return 0; +} + +/* 辞書のエンコーディングからxcharを作る */ +static xchar +form_mb_char(const char *str) +{ + xchar xc; + anthy_utf8_to_ucs4_xchar(str, &xc); + return xc; +} + +static int +hash(xstr *x) +{ + return anthy_xstr_hash(x)& + (YOMI_HASH_ARRAY_SIZE*YOMI_HASH_ARRAY_BITS-1); +} + +static int +check_hash_ent(struct word_dic *wdic, xstr *xs) +{ + int val = hash(xs); + int idx = (val>>YOMI_HASH_ARRAY_SHIFT)&(YOMI_HASH_ARRAY_SIZE-1); + int bit = val & ((1<<YOMI_HASH_ARRAY_SHIFT)-1); + return wdic->hash_ent[idx] & (1<<bit); +} + +static int +wtype_str_len(const char *str) +{ + int i; + for (i = 0; str[i] && str[i]!= ' '; i++); + return i; +} + +/* 辞書の行中をスキャンするための状態保持 */ +struct wt_stat { + wtype_t wt; + const char *wt_name; + int feature; + int freq; + int order_bonus;/* 辞書中の順序による頻度のボーナス */ + int offset;/* 文字列中のオフセット */ + const char *line; + int encoding; +}; +/* + * #XX*123 というCannadicの形式をパーズする + * #XX + * #XX*123 + * #XX,x*123 + */ +static const char * +parse_wtype_str(struct wt_stat *ws) +{ + int len; + char *buf; + char *freq_part; + char *feature_part; + const char *wt_name; + /* バッファへコピーする */ + len = wtype_str_len(&ws->line[ws->offset]); + buf = alloca(len + 1); + strncpy(buf, &ws->line[ws->offset], len); + buf[len] = 0; + + /* 素性(未使用) */ + feature_part = strchr(buf, ','); + if (feature_part) { + ws->feature = 1; + } else { + ws->feature = 0; + } + + /* 頻度をparseする */ + freq_part = strchr(buf, '*'); + if (freq_part) { + *freq_part = 0; + freq_part ++; + ws->freq = atoi(freq_part) * FREQ_RATIO; + } else { + ws->freq = FREQ_RATIO - 2; + } + + /**/ + wt_name = anthy_type_to_wtype(buf, &ws->wt); + if (!wt_name) { + ws->wt = anthy_wt_none; + } + ws->offset += len; + return wt_name; +} + + +static int +normalize_freq(struct wt_stat* ws) +{ + if (ws->freq < 0) { + ws->freq *= -1; + } + return ws->freq + ws->order_bonus; +} + +/* '\\'によるエスケープに対応したコピー */ +static void +copy_to_buf(char *buf, const char *src, int char_count) +{ + int pos; + int i; + pos = 0; + for (i = 0; i < char_count; i++){ + if (src[i] == '\\') { + if (src[i + 1] == ' ') { + i ++; + } else if (src[i + 1] == '\\') { + i ++; + } + } + buf[pos] = src[i]; + pos ++; + } + buf[pos] = 0; +} + +/** seq_entにdic_entを追加する */ +static int +add_dic_ent(struct seq_ent *seq, struct wt_stat *ws, + xstr* yomi, int is_reverse) +{ + int i; + /* 辞書ファイル中のバイト数 */ + int char_count; + char *buf; + xstr *xs; + int freq; + wtype_t w = ws->wt; + const char *s = &ws->line[ws->offset]; + + /* 単語の文字数を計算 */ + for (i = 0, char_count = 0; + s[i] && (s[i] != ' ') && (s[i] != '#'); i++) { + char_count ++; + if (s[i] == '\\') { + char_count++; + i++; + } + } + + /* 品詞が定義されていないので無視 */ + if (!ws->wt_name) { + return char_count; + } + + /* freqが負なのは逆変換用 */ + if (!is_reverse && ws->freq < 0) { + return char_count; + } + + /* bufに単語をコピー */ + buf = alloca(char_count+1); + copy_to_buf(buf, s, char_count); + + xs = anthy_cstr_to_xstr(buf, ws->encoding); + + /* freqが正なのは順変換用 */ + if (is_reverse && ws->freq > 0) { + /* 再変換の際に、変換済みの部分と未変換の部分が混じっていた場合に対応する為に、 + 平仮名のみからなる部分は順辞書にその読みを持つ単語があればdic_entを生成する。 + */ + if (anthy_get_xstr_type(yomi) & XCT_HIRA) { + freq = normalize_freq(ws); + anthy_mem_dic_push_back_dic_ent(seq, 0, yomi, w, + ws->wt_name, freq, 0); + } + anthy_free_xstr(xs); + return char_count; + } + + freq = normalize_freq(ws); + + anthy_mem_dic_push_back_dic_ent(seq, 0, xs, w, ws->wt_name, freq, 0); + if (anthy_wtype_get_meisi(w)) { + /* 連用形が名詞化するやつは名詞化したものも追加 */ + w = anthy_get_wtype_with_ct(w, CT_MEISIKA); + anthy_mem_dic_push_back_dic_ent(seq, 0, xs, w, ws->wt_name, freq, 0); + } + anthy_free_xstr(xs); + return char_count; +} + +static int +add_compound_ent(struct seq_ent *seq, struct wt_stat *ws, + xstr* yomi, + int is_reverse) +{ + int len = wtype_str_len(&ws->line[ws->offset]); + char *buf = alloca(len); + xstr *xs; + int freq; + + (void)yomi; + + /* freqが負なのは逆変換用 */ + if (!is_reverse && ws->freq < 0) { + /* 普段の変換では要らない */ + return len; + } + + /* freqが正なのは順変換用 */ + if (is_reverse && ws->freq > 0) { + + /* 再変換の際に、変換済みの部分と未変換の部分が混じっていた場合に対応する為に、 + 平仮名のみからなる部分は順辞書にその読みを持つ単語があればdic_entを生成する。 + */ + /* + yomiに#_等を付加した文字列を作る必要がある + if (anthy_get_xstr_type(yomi) & (XCT_HIRA | XCT_KATA)) { + freq = normalize_freq(ws); + anthy_mem_dic_push_back_compound_ent(seq, xs, ws->wt, freq); + } + */ + return len; + } + + strncpy(buf, &ws->line[ws->offset + 1], len - 1); + buf[len - 1] = 0; + xs = anthy_cstr_to_xstr(buf, ws->encoding); + + freq = normalize_freq(ws); + anthy_mem_dic_push_back_dic_ent(seq, 1, xs, ws->wt, + ws->wt_name, freq, 0); + anthy_free_xstr(xs); + + return len; +} + +static void +init_wt_stat(struct wt_stat *ws, char *line) +{ + ws->wt_name = NULL; + ws->freq = 0; + ws->feature = 0; + ws->order_bonus = 0; + ws->offset = 0; + ws->line = line; + ws->encoding = ANTHY_EUC_JP_ENCODING; + if (*(ws->line) == 'u') { + ws->encoding = ANTHY_UTF8_ENCODING; + ws->line ++; + } +} + +/** 辞書のエントリの情報を元にseq_entをうめる */ +static void +fill_dic_ent(char *line, struct seq_ent *seq, + xstr* yomi, int is_reverse) +{ + struct wt_stat ws; + init_wt_stat(&ws, line); + + while (ws.line[ws.offset]) { + if (ws.line[ws.offset] == '#') { + if (isalpha(ws.line[ws.offset + 1])) { + /* 品詞*頻度 */ + ws.wt_name = parse_wtype_str(&ws); + /**/ + ws.order_bonus = FREQ_RATIO - 1; + } else { + /* 複合語候補 */ + ws.offset += add_compound_ent(seq, &ws, + yomi, + is_reverse); + } + } else { + /* 単語 */ + ws.offset += add_dic_ent(seq, &ws, yomi, + is_reverse); + if (ws.order_bonus > 0) { + ws.order_bonus --; + } + } + if (ws.line[ws.offset] == ' ') { + ws.offset++; + } + } +} + +/* + * sに書かれた文字列によってxを変更する + * 返り値は読み進めたバイト数 + */ +static int +mkxstr(char *s, xstr *x) +{ + int i, len; + /* s[0]には巻き戻しの文字数 */ + x->len -= (s[0] - 1); + for (i = 1; is_printable(&s[i]); i ++) { + len = mb_fragment_len(&s[i]); + if (len > 1) { + /* マルチバイト */ + x->str[x->len] = form_mb_char(&s[i]); + x->len ++; + i += (len - 1); + } else { + /* 1バイト文字 */ + x->str[x->len] = s[i]; + x->len ++; + } + } + return i; +} + +static int +set_next_idx(struct lookup_context *lc) +{ + lc->nth ++; + while (lc->nth < lc->nr) { + if (lc->array[lc->nth]->tmp.idx != NO_WORD) { + return 1; + } + lc->nth ++; + } + return 0; +} + +/** ページ中の単語の場所を調べる */ +static void +search_words_in_page(struct lookup_context *lc, int page, char *s) +{ + int o = 0; + xchar *buf; + xstr xs; + int nr = 0; + /* このページ中にあるもっとも長い単語を格納しうる長さ */ + buf = alloca(sizeof(xchar)*strlen(s)/2); + xs.str = buf; + xs.len = 0; + + while (*s) { + int r; + s += mkxstr(s, &xs); + r = anthy_xstrcmp(&xs, &lc->array[lc->nth]->xs); + if (!r) { + lc->array[lc->nth]->tmp.idx = o + page * WORDS_PER_PAGE; + nr ++; + if (!set_next_idx(lc)) { + return ; + } + /* 同じページ内で次の単語を探す */ + } + o ++; + } + if (nr == 0) { + /* このページで1語も見つからなかったら、この単語は無い */ + lc->array[lc->nth]->tmp.idx = NO_WORD; + set_next_idx(lc); + } + /* 現在の単語は次の呼び出しで探す */ +} + +/**/ +static int +compare_page_index(struct word_dic *wdic, const char *key, int page) +{ + char buf[100]; + char *s = &wdic->page[anthy_dic_ntohl(wdic->page_index[page])]; + int i; + s++; + for (i = 0; is_printable(&s[i]);) { + int j, l = mb_fragment_len(&s[i]); + for (j = 0; j < l; j++) { + buf[i+j] = s[i+j]; + } + i += l; + } + buf[i] = 0; + return strcmp(key ,buf); +} + +/* 再帰的にバイナリサーチをする */ +static int +get_page_index_search(struct word_dic *wdic, const char *key, int f, int t) +{ + /* anthy_xstrcmpが-1で無くなったところを探す */ + int c,p; + c = (f+t)/2; + if (f+1==t) { + return c; + } else { + p = compare_page_index(wdic, key, c); + if (p < 0) { + return get_page_index_search(wdic, key, f, c); + } else { + /* c<= <t */ + return get_page_index_search(wdic, key, c, t); + } + } +} + +/** keyを含む可能性のあるページの番号を得る、 + * 範囲チェックをしてバイナリサーチを行うget_page_index_searchを呼ぶ + */ +static int +get_page_index(struct word_dic *wdic, struct lookup_context *lc) +{ + int page; + const char *key = lc->array[lc->nth]->key; + /* 最初のページの読みよりも小さい */ + if (compare_page_index(wdic, key, 0) < 0) { + return -1; + } + /* 最後のページの読みよりも大きいので、最後のページに含まれる可能性がある */ + if (compare_page_index(wdic, key, wdic->nr_pages-1) >= 0) { + return wdic->nr_pages-1; + } + /* 検索する */ + page = get_page_index_search(wdic, key, 0, wdic->nr_pages); + return page; +} + +static int +get_nr_page(struct word_dic *h) +{ + int i; + for (i = 1; anthy_dic_ntohl(h->page_index[i]); i++); + return i; +} + +static char * +get_section(struct word_dic *wdic, int section) +{ + int *p = (int *)wdic->dic_file; + int offset = anthy_dic_ntohl(p[section]); + return &wdic->dic_file[offset]; +} + +/** 辞書ファイルをmmapして、word_dic中の各セクションのポインタを取得する */ +static int +get_word_dic_sections(struct word_dic *wdic) +{ + wdic->entry_index = (int *)get_section(wdic, 2); + wdic->entry = (char *)get_section(wdic, 3); + wdic->page = (char *)get_section(wdic, 4); + wdic->page_index = (int *)get_section(wdic, 5); + wdic->uc_section = (char *)get_section(wdic, 6); + wdic->hash_ent = (unsigned char *)get_section(wdic, 7); + + return 0; +} + +/** 指定された単語の辞書中のインデックスを調べる */ +static void +search_yomi_index(struct word_dic *wdic, struct lookup_context *lc) +{ + int p; + int page_number; + + /* すでに無いことが分かっている */ + if (lc->array[lc->nth]->tmp.idx == NO_WORD) { + set_next_idx(lc); + return ; + } + + p = get_page_index(wdic, lc); + if (p == -1) { + lc->array[lc->nth]->tmp.idx = NO_WORD; + set_next_idx(lc); + return ; + } + + page_number = anthy_dic_ntohl(wdic->page_index[p]); + search_words_in_page(lc, p, &wdic->page[page_number]); +} + +static void +find_words(struct word_dic *wdic, struct lookup_context *lc) +{ + int i; + /* 検索前に除去 */ + for (i = 0; i < lc->nr; i++) { + lc->array[i]->tmp.idx = NO_WORD; + if (lc->array[i]->xs.len > 31) { + /* 32文字以上単語には未対応 */ + continue; + } + /* hashにないなら除去 */ + if (!check_hash_ent(wdic, &lc->array[i]->xs)) { + continue; + } + /* NO_WORDでない値を設定することで検索対象とする */ + lc->array[i]->tmp.idx = 0; + } + /* 検索する */ + lc->nth = 0; + while (lc->nth < lc->nr) { + search_yomi_index(wdic, lc); + } +} + +static void +load_words(struct word_dic *wdic, struct lookup_context *lc) +{ + int i; + for (i = 0; i < lc->nr; i++) { + int yomi_index; + yomi_index = lc->array[i]->tmp.idx; + if (yomi_index != NO_WORD) { + int entry_index; + struct seq_ent *seq; + seq = anthy_cache_get_seq_ent(&lc->array[i]->xs, + lc->is_reverse); + entry_index = anthy_dic_ntohl(wdic->entry_index[yomi_index]); + fill_dic_ent(&wdic->entry[entry_index], + seq, + &lc->array[i]->xs, + lc->is_reverse); + anthy_validate_seq_ent(seq, &lc->array[i]->xs, lc->is_reverse); + } + } +} + +/** word_dicから単語を検索する + * 辞書キャッシュから呼ばれる + * (gang lookupにすることを検討する) + */ +void +anthy_gang_fill_seq_ent(struct word_dic *wdic, + struct gang_elm **array, int nr, + int is_reverse) +{ + struct lookup_context lc; + lc.array = array; + lc.nr = nr; + lc.is_reverse = is_reverse; + + /* 各単語の場所を探す */ + find_words(wdic, &lc); + /* 単語の情報を読み込む */ + load_words(wdic, &lc); +} + +struct word_dic * +anthy_create_word_dic(void) +{ + struct word_dic *wdic; + char *p; + + wdic = anthy_smalloc(word_dic_ator); + memset(wdic, 0, sizeof(*wdic)); + + /* 辞書ファイルをマップする */ + wdic->dic_file = anthy_file_dic_get_section("word_dic"); + + /* 各セクションのポインタを取得する */ + if (get_word_dic_sections(wdic) == -1) { + anthy_sfree(word_dic_ator, wdic); + return 0; + } + wdic->nr_pages = get_nr_page(wdic); + + /* 用例辞書をマップする */ + p = wdic->uc_section; + return wdic; +} + +void +anthy_release_word_dic(struct word_dic *wdic) +{ + anthy_sfree(word_dic_ator, wdic); +} + +void +anthy_init_word_dic(void) +{ + word_dic_ator = anthy_create_allocator(sizeof(struct word_dic), NULL); +} |