Logo Search packages:      
Sourcecode: yaskkserv version File versions  Download package

skk_jisyo.hpp

/*
  Copyright (C) 2005, 2006, 2007, 2008 Tadashi Watanabe <wac@umiushi.org>

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#ifndef SKK_JISYO_H
#define SKK_JISYO_H




#include "skk_architecture.hpp"
#include "skk_utility.hpp"
#include "skk_mmap.hpp"




/// SKK 辞書を扱うクラスです。
/**
 *
 * \section memo メモ
 *
 * 辞書生成メソッドではバッファリングによる高速化を期待するため I/O に
 * cstdio を使用します。見出しに対するエントリを取得する Search() メソッ
 * ドではバッファリングする必要がないので mmap を使用します。
 *
 * 以上のように、本クラスでは潤沢なリソースを要求します。
 *
 *
 * \section aboutdictionary 辞書について
 *
 * yaskkserv では辞書を SKK の辞書から専用形式に変換して使用します。変
 * 換は内部的に 2 段階ありますが、一般にユーザが 1 段階目の辞書に触れる
 * ことはありません。
 *
 * 1 段階目
 *
 * \li class SkkJisyo 用
 *
 * \li createDictionaryForClassSkkJisyo() を用い作成
 *
 * 2 段階目
 *
 * \li class SkkDictionary 用
 *
 * \li createDictionaryForClassSkkDictionary() を用い作成
 *
 *
 * 変換の内容は以下の通りです。
 *
 * \li コメントは全て取り除かれる
 *
 * \li 「見出し」は「ひらがなエンコード」される
 *
 * \li 「通常エントリ」と「特殊エントリ」に分けられる
 *
 * \li それぞれ「送りあり」と「送りなし」を混ぜた状態で昇順ソートされる
 *
 * \li 2 段階目ではインデックスデータが付加される
 *
 * \li 末尾に辞書情報がバイナリで付加される
 *
 * \subsection special 特殊エントリ
 * 
 * 先頭一文字が「ひらがな(0xa4)」または「ASCII(0x21-0x7e)」でない文字は
 * 特殊エントリとして扱われます。
 *
 *
 * \seciton structure 辞書の構造
 *
 * \verbatim
 +---------------+------------------------------------+ <-- SEEK_TOP (STATE_NORMAL)
 |               | normal                             |
 |               | 通常エントリ                       |
 |               | (昇順ソート。存在しない場合もある) | <-- SEEK_BOTTOM (STATE_NORMAL)
 | dictionary    +------------------------------------+ <-- SEEK_TOP (STATE_SPECIAL)
 |               | special                            |
 |               | 特殊エントリ                       |
 |               | (昇順ソート。存在しない場合もある) | <-- SEEK_BOTTOM (STATE_SPECIAL)
 +---------------+------------------------------------+
 |               | terminator and alignment           |
 | index data    | 1 - 4 バイトの 0 ターミネート領域  |
 |               +------------------------------------+ <-- インデックスデータオフセット
 | (1 段階目では | struct IndexDataHeader             |
 |  存在しない)  |              +                     |
 |               | index data  インデックスデータ     |
 +---------------+------------------------------------+
 |               | terminator and alignment           |
 |               | 1 - 4 バイトの 0 ターミネート領域  |
 | information   +------------------------------------+ <--- ファイルサイズ - sizeof(Information)
 |               |                                    |
 |               | struct Information 辞書情報        |
 |               |                                    |
 +---------------+------------------------------------+
\endverbatim
 *
 * \subsection information 辞書情報
 *
 * \verbatim

struct Information

 object[0]  : ビットフラグ
 object[1]  : リザーブ
 object[2]  : リザーブ
 object[3]  : リザーブ
 object[4]  : リザーブ
 object[5]  : リザーブ
 object[6]  : インデックスデータへのオフセット
 object[7]  : インデックスデータサイズ
 object[8]  : special 行数
 object[9]  : special サイズ
 object[10] : normal 行数
 object[11] : normal サイズ
 object[12] : ブロックアラインメントサイズ
 object[13] : バージョン
 object[14] : object サイズ
 object[15] : identifier

\endverbatim
 *
 * \section aboutdictionaryindex インデックスデータについて
 *
 * インデックスデータは以下の要素から構成されます。(参考数値は 2005年10
 * 月12 日(水) あたりの SKK-JISYO.total+zipcode の値です。)
 *
 * \li struct FixedArray の固定長配列 (256 個)
 *
 * \li struct Block/BlockShort の可変長配列 (block_size/length : 4k/3564, 8k/1840, 16k/971)
 *
 * \li 文字列格納領域 (block_size/length : 4k/28k, 8k/14k, 16k/8k)
 *
 * Block/BlockShort の可変長配列と文字列格納領域は、通常エントリと特殊
 * エントリで共用します。特殊エントリのものは通常エントリの後に追加され
 * ます。 FixedArray は通常エントリだけが使用します。
 *
 * \verbatim
 +----------------------------------------------------+
 |                                                    |
 |  struct IndexDataHeader                            |
 |                                                    |
 |          header[0]  : ビットフラグ                 |
 |          header[1]  : header サイズ                |
 |          header[2]  : ブロックサイズ               |
 |          header[3]  : normal_block_length          |
 |          header[4]  : special_block_length         |
 |          header[5]  : normal_string_length         |
 |          header[6]  : special_string_length        |
 |          header[7]  : 特殊エントリへのオフセット   |
 |                                                    |
 +----------------------------------------------------+
 |                                                    |
 |  FixedArray fixed_array[256]                       |
 |                                                    |
 |     通常エントリの探索ヒント情報です。             |
 |                                                    |
 +----------------------------------------------------+
 |                                                    |
 |  Block block[block_length]                         |
 |  BlockShort block[block_length]                    |
 |                                                    |
 |     ブロック情報を保持します。                     |
 |     アラインメントされた辞書では BlockShort を     |
 |     使用することでサイズを 1/4 にすることが        |
 |     できます。                                     |
 |                                                    |
 +----------------------------------------------------+
 |                                                    |
 |  char string[string_length]                        |
 |                                                    |
 |     可変長文字列です。終端コードとしてスペースを   |
 |     使用します。つまり "AA BBB CCCC DDDDD " と     |
 |     いった文字列になります。                       |
 |                                                    |
 |     文字列は block 配列と同じ数(block_length 個)   |
 |     存在します。                                   |
 |                                                    |
 |     この文字列は対応するブロックの最後の見出し     |
 |     です。これによりブロック内に探索文字列が       |
 |     存在するかどうかを調べます。                   |
 |                                                    |
 +----------------------------------------------------+
\endverbatim
 *
 */
00199 class SkkJisyo
{
        SkkJisyo(SkkJisyo &source);
        SkkJisyo& operator=(SkkJisyo &source);




public:
        enum State
        {
                STATE_NORMAL,
                STATE_SPECIAL,

                STATE_LENGTH
        };


        enum JisyoType
        {
                JISYO_TYPE_UNKNOWN,
                JISYO_TYPE_SKK_RAW,
                JISYO_TYPE_CLASS_SKK_JISYO,
                JISYO_TYPE_CLASS_SKK_DICTIONARY
        };


        enum SeekPosition
        {
                SEEK_POSITION_TOP,
                SEEK_POSITION_BOTTOM,
                SEEK_POSITION_NEXT,
                SEEK_POSITION_PREVIOUS,
                SEEK_POSITION_BEGINNING_OF_LINE
        };


        enum
        {
                IDENTIFIER = 0x7fedc000
        };




/// array[0] は常にビットフラグを示すことに注意が必要です。
00245         template<int N> struct ArrayInt32
        {
                enum
                {
                        BIT_FLAG_BYTE_ORDER_VAX = 0x1 << 0,
                };


                static int getSize()
                {
                        return sizeof(int32_t) * N;
                }




                ArrayInt32(int bit_flag = BIT_FLAG_BYTE_ORDER_VAX)
                {
                        array[0] = bit_flag;
                        for (int i = 1; i != N; ++i)
                        {
                                array[i] = 0;
                        }
                }


                bool initialize(const void *p)
                {
                        DEBUG_ASSERT_POINTER(p);
                        DEBUG_ASSERT_POINTER_ALIGN(p,
                                                   4);

                        const int32_t *tmp = reinterpret_cast<const int32_t*>(p);
                        for (int i = 0; i != N; ++i)
                        {
                                array[i] = *tmp++;
                        }

                        return true;
                }


                int32_t get(int index)
                {
#ifdef YASKKSERV_ARCHITECTURE_BYTE_ORDER_NETWORK
                        if (array[0] & BIT_FLAG_BYTE_ORDER_VAX)
                        {
                                return ((array[index] << 24) & 0xff000000) |
                                        ((array[index] << 8) & 0x00ff0000) |
                                        ((array[index] >> 8) & 0x0000ff00) |
                                        ((array[index] >> 24) & 0x000000ff);
                        }
                        else
                        {
                                return array[index];
                        }
#endif  // YASKKSERV_ARCHITECTURE_BYTE_ORDER_NETWORK


#ifdef YASKKSERV_ARCHITECTURE_BYTE_ORDER_VAX
                        if (array[0] & BIT_FLAG_BYTE_ORDER_VAX)
                        {
                                return array[index];
                        }
                        else
                        {
                                return ((array[index] << 24) & 0xff000000) |
                                        ((array[index] << 8) & 0x00ff0000) |
                                        ((array[index] >> 8) & 0x0000ff00) |
                                        ((array[index] >> 24) & 0x000000ff);
                        }
#endif  // YASKKSERV_ARCHITECTURE_BYTE_ORDER_VAX
                }


                void set(int index,
                         int32_t scalar)
                {
#ifdef YASKKSERV_ARCHITECTURE_BYTE_ORDER_NETWORK
                        if (array[0] & BIT_FLAG_BYTE_ORDER_VAX)
                        {
                                array[index] = ((scalar << 24) & 0xff000000) |
                                        ((scalar << 8) & 0x00ff0000) |
                                        ((scalar >> 8) & 0x0000ff00) |
                                        ((scalar >> 24) & 0x000000ff);
                        }
                        else
                        {
                                array[index] = scalar;
                        }
#endif  // YASKKSERV_ARCHITECTURE_BYTE_ORDER_NETWORK


#ifdef YASKKSERV_ARCHITECTURE_BYTE_ORDER_VAX
                        if (array[0] & BIT_FLAG_BYTE_ORDER_VAX)
                        {
                                array[index] = scalar;
                        }
                        else
                        {
                                array[index] = ((scalar << 24) & 0xff000000) |
                                        ((scalar << 8) & 0x00ff0000) |
                                        ((scalar >> 8) & 0x0000ff00) |
                                        ((scalar >> 24) & 0x000000ff);
                        }
#endif  // YASKKSERV_ARCHITECTURE_BYTE_ORDER_VAX
                }




                int32_t array[N];
        };


        struct Information
        {
                enum Id
                {
                        ID_BIT_FLAG,

                        ID_RESERVE_1,
                        ID_RESERVE_2,
                        ID_RESERVE_3,
                        ID_RESERVE_4,
                        ID_RESERVE_5,

                        ID_INDEX_DATA_OFFSET,
                        ID_INDEX_DATA_SIZE,
                        ID_SPECIAL_LINES,
                        ID_SPECIAL_SIZE,
                        ID_NORMAL_LINES,
                        ID_NORMAL_SIZE,
                        ID_BLOCK_ALIGNMENT_SIZE,
                        ID_VERSION,
                        ID_SIZE,
                        ID_IDENTIFIER,

                        ID_LENGTH,
                };




                static int getSize()
                {
                        return sizeof(int32_t) * ID_LENGTH;
                }


                Information(int bit_flag = ArrayInt32<ID_LENGTH>::BIT_FLAG_BYTE_ORDER_VAX) :
                        object(bit_flag)
                {
                        set(ID_IDENTIFIER,
                            SkkJisyo::IDENTIFIER);
                        set(ID_SIZE,
                            getSize());
                        set(ID_VERSION,
                            (0 * 100000) +
                            (0 * 10000) +
                            (0 * 1000) +
                            (0 * 100) +
                            (0 * 10) +
                            (1 * 1));
                }


                bool initialize(const void *p)
                {
                        return object.initialize(p);
                }


                int32_t get(Id id)
                {
                        return object.get(id);
                }


                void set(Id id,
                        int32_t scalar)
                {
                        return object.set(id,
                                          scalar);
                }




                ArrayInt32<ID_LENGTH> object;
        };


        struct IndexDataHeader
        {
                enum Id
                {
                        ID_BIT_FLAG,

                        ID_SIZE,
                        ID_BLOCK_SIZE,
                        ID_NORMAL_BLOCK_LENGTH,
                        ID_SPECIAL_BLOCK_LENGTH,
                        ID_NORMAL_STRING_SIZE,
                        ID_SPECIAL_STRING_SIZE,
                        ID_SPECIAL_ENTRY_OFFSET,

                        ID_LENGTH
                };


                enum
                {
                        BIT_FLAG_BLOCK_SHORT = 0x1 << 31
                };




                static int getSize()
                {
                        return sizeof(int32_t) * ID_LENGTH;
                }


                IndexDataHeader(int bit_flag = ArrayInt32<ID_LENGTH>::BIT_FLAG_BYTE_ORDER_VAX) :
                        object(bit_flag)
                {
                        set(ID_SIZE,
                            getSize());
                }


                bool initialize(const void *p)
                {
                        return object.initialize(p);
                }


                int32_t get(Id id)
                {
                        return object.get(id);
                }


                void set(Id id,
                        int32_t scalar)
                {
                        return object.set(id,
                                          scalar);
                }




                ArrayInt32<ID_LENGTH> object;
        };


        struct FixedArray
        {
                FixedArray () :
                        start_block(0),
                        block_length(0),
                        string_data_offset(0)
                {
                }




                int16_t start_block;
                int16_t block_length;
                int32_t string_data_offset;
        };


        struct Block
        {
                Block () :
                        offset(0),
                        line_length_and_data_size(0)
                {
                }


                int getOffset()
                {
                        return offset;
                }


                void setOffset(int scalar)
                {
                        offset = scalar;
                }


                int getDataSize()
                {
                        return line_length_and_data_size & 0xfffff;
                }


                void setLineLengthAndDataSize(int line_length,
                                              int data_size)
                {
                        line_length_and_data_size = (line_length << 20) | data_size;
                }




                int32_t offset;
                int32_t line_length_and_data_size;
        };


        struct BlockShort
        {
                BlockShort () :
                        data_size(0)
                {
                }


                int getDataSize()
                {
                        return data_size;
                }


                void setDataSize(int size)
                {
                        data_size = size;
                }




                int16_t data_size;
        };




private:
/// okuri_ari_index と okuri_nasi_index を求めます。失敗した場合は偽を返します。
00593         static bool get_index(const char *buffer,
                              int filesize,
                              int &okuri_ari_index,
                              int &okuri_nasi_index)
        {
                DEBUG_ASSERT_POINTER(buffer);

                for (;;)
                {
//
// okuri_ari_index と okuri_nasi_index を以下のような流れで求めます。
//
// ================ SKK 辞書の構造とインデックス ==================
// ;; okuri-ari entries.
// うううu /.../
// いいいi /.../
// あああa /.../ ← この行頭が okuri_ari_index
// ;; okuri-nasi entries.
// あああ /.../ ← この行頭が okuri_nasi_index
// いいい /.../
// ううう /.../
// ================================================================
//
// 1. まず ";; okuri-nasi entries.\n" を検索
//
// 2. 見付かったら、その 1 行上が「送りありエントリの先頭候補」、
//    1 行下が「送りなしエントリの先頭候補」
//    (SKK 辞書では送りありエントリは逆方向にソートされていることに注意)
//
// 3. それぞれの先頭候補で空行とコメントをスキップし、真の先頭を設定
//
                        if ((*(buffer + okuri_nasi_index) == ';') &&
                           ((filesize + 1 - okuri_nasi_index) >= SkkUtility::getStringOkuriNasiLength()) &&
                            SkkUtility::isStringOkuriNasi(buffer + okuri_nasi_index))
                        {
// get candidate index
                                okuri_ari_index = SkkUtility::getPreviousLineIndex(buffer,
                                                                                   okuri_nasi_index,
                                                                                   filesize);
                                okuri_nasi_index = SkkUtility::getNextLineIndex(buffer,
                                                                                okuri_nasi_index,
                                                                                filesize);
// skip empty/comment line
                                while (okuri_ari_index > 0)
                                {
                                        if (*(buffer + okuri_ari_index) == '\n')
                                        {
                                                --okuri_ari_index;
                                                okuri_ari_index = SkkUtility::getBeginningOfLineIndex(buffer,
                                                                                                      okuri_ari_index,
                                                                                                      filesize);
                                        }
                                        else if (*(buffer + okuri_ari_index) == ';')
                                        {
                                                okuri_ari_index = SkkUtility::getPreviousLineIndex(buffer,
                                                                                                   okuri_ari_index,
                                                                                                   filesize);
                                        }
                                        else
                                        {
                                                break;
                                        }
                                }
                                while (okuri_nasi_index >= 0)
                                {
                                        if (*(buffer + okuri_nasi_index) == '\n')
                                        {
                                                ++okuri_nasi_index;
                                                if (okuri_nasi_index >= filesize)
                                                {
                                                        okuri_nasi_index = -1;
                                                }
                                        }
                                        else if (*(buffer + okuri_nasi_index) == ';')
                                        {
                                                okuri_nasi_index = SkkUtility::getNextLineIndex(buffer,
                                                                                                okuri_nasi_index,
                                                                                                filesize);
                                        }
                                        else
                                        {
                                                break;
                                        }
                                }
                                break;
                        }
                        okuri_nasi_index = SkkUtility::getNextLineIndex(buffer,
                                                                        okuri_nasi_index,
                                                                        filesize);
                        if (okuri_nasi_index < 0)
                        {
                                return false;
                        }
                }

                return true;
        }

        static bool append_terminator(FILE *file)
        {
                DEBUG_ASSERT_POINTER(file);

// ターミネータ兼アラインメント領域(4 bytesに揃える)
                char tmp[4];
                for (int i = 0; i != 4; ++i)
                {
                        tmp[i] = 0;
                }

// 1 or 2 or 3 or 4 バイトの 0 で埋める(すでに 4 バイトアラインならば 4 バイト)
                long current = ftell(file);
                if (fwrite(tmp,
                           4 - (current % 4),
                           1,
                           file) < 1)
                {
                        return false;
                }

                return true;
        }


        static bool append_information(FILE *file,
                                       int block_alignment_size,
                                       int normal_size,
                                       int normal_lines,
                                       int special_size,
                                       int special_lines,
                                       int index_data_size,
                                       int index_data_offset)
        {
                DEBUG_ASSERT_POINTER(file);

                Information information;

                information.set(Information::ID_BLOCK_ALIGNMENT_SIZE,
                                block_alignment_size);
                information.set(Information::ID_NORMAL_SIZE,
                                normal_size);
                information.set(Information::ID_NORMAL_LINES,
                                normal_lines);
                information.set(Information::ID_SPECIAL_SIZE,
                                special_size);
                information.set(Information::ID_SPECIAL_LINES,
                                special_lines);
                information.set(Information::ID_INDEX_DATA_SIZE,
                                index_data_size);
                information.set(Information::ID_INDEX_DATA_OFFSET,
                                index_data_offset);

                append_terminator(file);

                if (fwrite(&information,
                           sizeof(information),
                           1,
                           file) < 1)
                {
                        return false;
                }

                return true;
        }


        static bool create_dictionary_for_class_skk_jisyo_write_temporary_raw(FILE *file,
                                                                              const char *buffer,
                                                                              int index,
                                                                              int line_size)
        {
                DEBUG_ASSERT_POINTER(file);
                DEBUG_ASSERT_POINTER(buffer);
                DEBUG_ASSERT(index >= 0);
                DEBUG_ASSERT(line_size > 0);

                const int cr_size = 1;

                if (fwrite("\1",
                           1,
                           1,
                           file) < 1)
                {
                        return false;
                }
                if (fwrite(buffer + index,
                           line_size + cr_size,
                           1,
                           file) < 1)
                {
                        return false;
                }

                return true;
        }


        static bool create_dictionary_for_class_skk_jisyo_write_temporary_encoded(FILE *file,
                                                                                  const char *buffer,
                                                                                  int index,
                                                                                  int filesize,
                                                                                  const char *line_buffer,
                                                                                  int encoded_size)
        {
                DEBUG_ASSERT_POINTER(file);
                DEBUG_ASSERT_POINTER(buffer);
                DEBUG_ASSERT(index >= 0);
                DEBUG_ASSERT(filesize > 0);
                DEBUG_ASSERT_POINTER(line_buffer);
                DEBUG_ASSERT(encoded_size > 0);

                const int cr_size = 1;

                if (fwrite(line_buffer,
                           encoded_size,
                           1,
                           file) < 1)
                {
                        return false;
                }
                if (fwrite(" ",
                           1,
                           1,
                           file) < 1)
                {
                        return false;
                }
                const char *tmp = SkkUtility::getHenkanmojiretsuPointer(buffer,
                                                                        index,
                                                                        filesize);
                const int tmp_size = SkkUtility::getHenkanmojiretsuSize(buffer,
                                                                        index,
                                                                        filesize);
                if ((tmp == 0) ||
                    (tmp_size <= 0))
                {
                        return false;
                }
                if (fwrite(tmp,
                           tmp_size + cr_size,
                           1,
                           file) < 1)
                {
                        return false;
                }

                return true;
        }


        static bool create_dictionary_for_class_skk_jisyo_write_temporary(const char *buffer,
                                                                          int filesize,
                                                                          int normal_fd,
                                                                          int special_fd,
                                                                          int okuri_ari_index,
                                                                          int okuri_nasi_index,
                                                                          int &okuri_ari_lines,
                                                                          int &okuri_nasi_lines,
                                                                          int &special_okuri_nasi_lines)
        {
                DEBUG_ASSERT_POINTER(buffer);
                DEBUG_ASSERT(filesize > 0);
                DEBUG_ASSERT(normal_fd > 0);
                DEBUG_ASSERT(special_fd > 0);
                DEBUG_ASSERT(okuri_ari_index >= 0);
                DEBUG_ASSERT(okuri_nasi_index >= 0);
                DEBUG_ASSERT(okuri_ari_lines == 0);
                DEBUG_ASSERT(okuri_nasi_lines == 0);
                DEBUG_ASSERT(special_okuri_nasi_lines == 0);

                bool result;

                FILE *file_normal = fdopen(normal_fd,
                                           "wb");
                FILE *file_special = fdopen(special_fd,
                                            "wb");

                if ((file_normal == 0) ||
                    (file_special == 0))
                {
                        result = false;
                }
                else
                {
                        result = true;

                        const int cr_size = 1;
                        const int line_buffer_margin = 16;

// line_buffer_size は常識的に考えて問題のない値です。万が一この値を越
// えた場合でも、 encodeHiragana() でサイズチェックをしているため安全で
// す。
                        const int line_buffer_size = 64 * 1024;
                        char *line_buffer = new char[line_buffer_size];

                        for (;;)
                        {
                                bool copy_okuri_ari = false;
                                bool copy_okuri_nasi = false;
                                if ((okuri_ari_index > 0) &&
                                    (okuri_nasi_index >= 0))
                                {
                                        int tmp = SkkUtility::compareMidasi(buffer,
                                                                            okuri_ari_index,
                                                                            filesize,
                                                                            buffer + okuri_nasi_index);
                                        if (tmp == 0)
                                        {
// なぜか ari/nasi で同じ見出しがある (あったらおかしい)
                                                result = false;
                                                break;
                                        }
                                        else if (tmp > 0)
                                        {
// ari -> nasi
                                                copy_okuri_nasi = true;
                                        }
                                        else
                                        {
// nasi -> ari
                                                copy_okuri_ari = true;
                                        }
                                }
                                else if (okuri_ari_index > 0)
                                {
                                        copy_okuri_ari = true;
                                }
                                else if (okuri_nasi_index >= 0)
                                {
                                        copy_okuri_nasi = true;
                                }
                                else
                                {
                                        break;
                                }


                                if (copy_okuri_ari)
                                {
                                        int line_size = SkkUtility::getLineSize(buffer,
                                                                                okuri_ari_index,
                                                                                filesize);
                                        if (line_size == 0)
                                        {
                                                SkkUtility::printf("warning: found empty line\n");
                                                --okuri_ari_index;
                                                if (okuri_ari_index <= 0)
                                                {
                                                        okuri_ari_index = 0;
                                                }
                                        }
                                        else
                                        {
                                                int encoded_size = SkkUtility::encodeHiragana(buffer + okuri_ari_index,
                                                                                              line_buffer,
                                                                                              line_buffer_size - line_buffer_margin);
                                                if (encoded_size == 0)
                                                {
                                                        result = create_dictionary_for_class_skk_jisyo_write_temporary_raw(file_normal,
                                                                                                                           buffer,
                                                                                                                           okuri_ari_index,
                                                                                                                           line_size);
                                                        if (result == false)
                                                        {
                                                                break;
                                                        }
                                                }
                                                else
                                                {
                                                        result = create_dictionary_for_class_skk_jisyo_write_temporary_encoded(file_normal,
                                                                                                                               buffer,
                                                                                                                               okuri_ari_index,
                                                                                                                               filesize,
                                                                                                                               line_buffer,
                                                                                                                               encoded_size);
                                                        if (result == false)
                                                        {
                                                                break;
                                                        }
                                                }
                                                ++okuri_ari_lines;

                                                okuri_ari_index = SkkUtility::getPreviousLineIndex(buffer,
                                                                                                   okuri_ari_index,
                                                                                                   filesize);
                                        }
                                }

                                if (copy_okuri_nasi)
                                {
                                        int line_size = SkkUtility::getLineSize(buffer,
                                                                                okuri_nasi_index,
                                                                                filesize);
                                        if (line_size == 0)
                                        {
                                                SkkUtility::printf("warning: found empty line\n");
                                                ++okuri_nasi_index;
                                                if (okuri_nasi_index >= filesize)
                                                {
                                                        okuri_nasi_index = -1;
                                                }
                                        }
                                        else
                                        {
                                                int c = *(buffer + okuri_nasi_index) & 0xff;
                                                if ((c == 0xa4) ||
                                                    ((c >= 0x21) && (c <= 0x7e)))
                                                {
                                                        int encoded_size = SkkUtility::encodeHiragana(buffer + okuri_nasi_index,
                                                                                                      line_buffer,
                                                                                                      line_buffer_size - line_buffer_margin);
                                                        if (encoded_size == 0)
                                                        {
                                                                result = create_dictionary_for_class_skk_jisyo_write_temporary_raw(file_normal,
                                                                                                                                   buffer,
                                                                                                                                   okuri_nasi_index,
                                                                                                                                   line_size);
                                                                if (result == false)
                                                                {
                                                                        break;
                                                                }
                                                        }
                                                        else
                                                        {
                                                                result = create_dictionary_for_class_skk_jisyo_write_temporary_encoded(file_normal,
                                                                                                                                       buffer,
                                                                                                                                       okuri_nasi_index,
                                                                                                                                       filesize,
                                                                                                                                       line_buffer,
                                                                                                                                       encoded_size);
                                                                if (result == false)
                                                                {
                                                                        break;
                                                                }
                                                        }
                                                        ++okuri_nasi_lines;
                                                }
                                                else
                                                {
                                                        if (fwrite("\1",
                                                                   1,
                                                                   1,
                                                                   file_special) < 1)
                                                        {
                                                                result = false;
                                                                break;
                                                        }
                                                        if (fwrite(buffer + okuri_nasi_index,
                                                                   line_size + cr_size,
                                                                   1,
                                                                   file_special) < 1)
                                                        {
                                                                result = false;
                                                                break;
                                                        }
                                                        ++special_okuri_nasi_lines;
                                                }
                                                okuri_nasi_index = SkkUtility::getNextLineIndex(buffer,
                                                                                                okuri_nasi_index,
                                                                                                filesize);
                                        }
                                }

// skip comment
                                while ((okuri_ari_index > 0) &&
                                       (*(buffer + okuri_ari_index) == ';'))
                                {
                                        okuri_ari_index = SkkUtility::getPreviousLineIndex(buffer,
                                                                                           okuri_ari_index,
                                                                                           filesize);
                                }
                                while ((okuri_nasi_index >= 0) &&
                                       (*(buffer + okuri_nasi_index) == ';'))
                                {
                                        okuri_nasi_index = SkkUtility::getNextLineIndex(buffer,
                                                                                        okuri_nasi_index,
                                                                                        filesize);
                                }
                        }

                        delete[] line_buffer;
                }

                if (file_normal)
                {
                        fclose(file_normal);
                }
                if (file_special)
                {
                        fclose(file_special);
                }

                return result;
        }


        static bool create_dictionary_for_class_skk_jisyo_sort_core(FILE *file,
                                                                    char *buffer,
                                                                    int filesize,
                                                                    int lines)
        {
                DEBUG_ASSERT_POINTER(file);
                DEBUG_ASSERT_POINTER(buffer);
                DEBUG_ASSERT(filesize > 0);
                DEBUG_ASSERT(lines > 0);

                const int cr_size = 1;
                SkkUtility::SortKey *sort_key = new SkkUtility::SortKey[lines];

                bool result = true;

                int index = 0;
                for (int i = 0; i != lines; ++i)
                {
                        (sort_key + i)->index = index;
                        (sort_key + i)->i = i;
                        index = SkkUtility::getNextLineIndex(buffer,
                                                             index,
                                                             filesize);
                }
                SkkUtility::sortMidasi(buffer,
                                       filesize,
                                       sort_key,
                                       lines);

                for (int i = 0; i != lines; ++i)
                {
                        int line_size = SkkUtility::getLineSize(buffer,
                                                                (sort_key + i)->index,
                                                                filesize);
                        if (fwrite(buffer + (sort_key + i)->index,
                                   line_size + cr_size,
                                   1,
                                   file) < 1)
                        {
                                result = false;
                                break;
                        }
                }

                delete[] sort_key;

                return result;
        }


        static bool create_dictionary_for_class_skk_jisyo_sort_and_write(const char *filename_destination,
                                                                         const char *filename_normal,
                                                                         const char *filename_special,
                                                                         int okuri_ari_lines,
                                                                         int okuri_nasi_lines,
                                                                         int special_okuri_nasi_lines)
        {
                DEBUG_ASSERT_POINTER(filename_destination);
                DEBUG_ASSERT_POINTER(filename_normal);
                DEBUG_ASSERT_POINTER(filename_special);
                DEBUG_ASSERT(okuri_ari_lines >= 0);
                DEBUG_ASSERT(okuri_nasi_lines >= 0);
                DEBUG_ASSERT(special_okuri_nasi_lines >= 0);
                DEBUG_ASSERT((okuri_ari_lines + okuri_nasi_lines + special_okuri_nasi_lines) > 0);

                bool result = true;
                FILE *file = fopen(filename_destination,
                                           "wb");
                if (file == 0)
                {
                        result = false;
                }
                else
                {
                        int normal_size = 0;
                        int special_size = 0;

                        if (result &&
                            ((okuri_ari_lines + okuri_nasi_lines) > 0))
                        {
                                SkkMmap mmap;
                                char *buffer = static_cast<char*>(mmap.map(filename_normal));
                                if (buffer == 0)
                                {
                                        result = false;
                                }
                                else
                                {
                                        normal_size = mmap.getFilesize();
                                        result = create_dictionary_for_class_skk_jisyo_sort_core(file,
                                                                                                 buffer,
                                                                                                 normal_size,
                                                                                                 okuri_ari_lines + okuri_nasi_lines);
                                }
                        }

                        if (result &&
                            (special_okuri_nasi_lines > 0))
                        {
                                SkkMmap mmap;
                                char *buffer = static_cast<char*>(mmap.map(filename_special));
                                if (buffer == 0)
                                {
                                        result = false;
                                }
                                else
                                {
                                        special_size = mmap.getFilesize();
                                        result = create_dictionary_for_class_skk_jisyo_sort_core(file,
                                                                                                 buffer,
                                                                                                 special_size,
                                                                                                 special_okuri_nasi_lines);
                                }
                        }

                        if (result)
                        {
                                result = append_information(file,
                                                            0,
                                                            normal_size,
                                                            okuri_ari_lines + okuri_nasi_lines,
                                                            special_size,
                                                            special_okuri_nasi_lines,
                                                            0,
                                                            0);
                                if (!result)
                                {
                                        DEBUG_PRINTF("append_information() failed.\n");
                                }
                        }

                        fclose(file);
                }

                return result;
        }


// 辞書を作成します。失敗した場合は偽を返します。
/**
 * コメントを削除し「送りあり」と「送りなし」を混ぜてソート済みの状態で
 * 出力します。但し、「送りなし」の「ひらがな(0xa4)」と「ASCII(0x21 -
 * 0x7e)」以外の文字は special_okuri_nasi_buffer へ格納し、最後に出力し
 * ます。
 *
 * ;; okuri-ari  entries.             いろは /.../
 * いろはa /.../                      いろはa /.../
 * にほへとa /.../            →      にほへと /.../
 * ;; okuri-nasi  entries.            にほへとa /.../
 * いろは /.../                           special_okuri_nasi_buffer
 * にほへと /.../                     2しんほう /2進法/
 * 2しんほう /2進法/                イ /ヰ/
 * イ /ヰ/
 */
01242         static bool create_dictionary_for_class_skk_jisyo(const char *filename_destination,
                                                          const char *buffer,
                                                          int filesize,
                                                          int okuri_ari_index,
                                                          int okuri_nasi_index)
        {
                DEBUG_ASSERT_POINTER(filename_destination);
                DEBUG_ASSERT_POINTER(buffer);
                DEBUG_ASSERT(filesize > 0);
                DEBUG_ASSERT(okuri_ari_index >= 0);
                DEBUG_ASSERT(okuri_nasi_index >= 0);

                char tmp_filename_normal[] = "/tmp/skkjisyo_normal.XXXXXX";
                char tmp_filename_special[] = "/tmp/skkjisyo_special.XXXXXX";

                int normal_fd = mkstemp(tmp_filename_normal);
                if (normal_fd == -1)
                {
                        return false;
                }

                int special_fd = mkstemp(tmp_filename_special);
                if (special_fd == -1)
                {
                        return false;
                }

                bool result;
                int okuri_ari_lines = 0;
                int okuri_nasi_lines = 0;
                int special_okuri_nasi_lines = 0;

                result = create_dictionary_for_class_skk_jisyo_write_temporary(buffer,
                                                                               filesize,
                                                                               normal_fd,
                                                                               special_fd,
                                                                               okuri_ari_index,
                                                                               okuri_nasi_index,
                                                                               okuri_ari_lines,
                                                                               okuri_nasi_lines,
                                                                               special_okuri_nasi_lines);

                if (result)
                {
                        result = create_dictionary_for_class_skk_jisyo_sort_and_write(filename_destination,
                                                                                      tmp_filename_normal,
                                                                                      tmp_filename_special,
                                                                                      okuri_ari_lines,
                                                                                      okuri_nasi_lines,
                                                                                      special_okuri_nasi_lines);
                }

                if (unlink(tmp_filename_normal) == -1)
                {
                        result = false;
                }
                if (unlink(tmp_filename_special) == -1)
                {
                        result = false;
                }

                return result;
        }


        static bool create_dictionary_index_and_dictionary_for_class_skk_jisyo_special_core(SkkJisyo &object,
                                                                                            FILE *aligned_block_dictionary,
                                                                                            Block *block,
                                                                                            BlockShort *block_short,
                                                                                            char *string,
                                                                                            int &string_size,
                                                                                            int block_size_maximum,
                                                                                            bool alignment_flag,
                                                                                            int line,
                                                                                            int top_line,
                                                                                            int index,
                                                                                            int top_offset,
                                                                                            int block_length,
                                                                                            int top_file_offset)
        {
                if (block &&
                    block_short)
                {
// 両方のポインタが指定されていてはなりません。
                        DEBUG_PRINTF("ILLEGAL argument\n");
                        DEBUG_ASSERT(0);
                        return false;
                }

// 前のブロックの最終行の情報を得ます。
                int backup_index = object.getIndex();

                if (!object.seek(SkkJisyo::SEEK_POSITION_PREVIOUS))
                {
// ここで SEEK_POSITION_PREVIOUS が失敗するなどはありえないので不正な形式とします。
                        DEBUG_PRINTF("ILLEGAL dictionary\n");
                        DEBUG_ASSERT(0);
                        object.setIndex(backup_index);

                        return false;
                }

                if (block)
                {
                        if (aligned_block_dictionary &&
                            alignment_flag)
                        {
                                (block + block_length)->setOffset(ftell(aligned_block_dictionary) - top_file_offset);
                        }
                        else
                        {
                                (block + block_length)->setOffset(top_offset);
                        }
                        (block + block_length)->setLineLengthAndDataSize(line - top_line,
                                                                         index - top_offset);
                }
                else if (block_short)
                {
                        (block_short + block_length)->setDataSize(index - top_offset);
                }


                {
                        const int terminator_size = 1;
                        int length = object.getMidasiSize() + terminator_size;
                        if (string)
                        {
                                char *p = object.getMidasiPointer();
                                int i;
                                for (i = 0; i != length - terminator_size; ++i)
                                {
                                        *(string + string_size + i) = *(p + i);
                                }
                                *(string + string_size + i) = ' ';
                        }
                        string_size += length;
                }


                if (aligned_block_dictionary)
                {
                        int size = index - top_offset;
                        if (fwrite(object.getBuffer() + top_offset,
                                   size,
                                   1,
                                   aligned_block_dictionary) < 1)
                        {
                                object.setIndex(backup_index);
                                return false;
                        }
                        if (alignment_flag &&
                            (size < block_size_maximum))
                        {
                                fseek(aligned_block_dictionary,
                                      block_size_maximum - (size % block_size_maximum),
                                      SEEK_CUR);
                        }
                }

                object.setIndex(backup_index);

                return true;
        }


        static bool create_dictionary_index_and_dictionary_for_class_skk_jisyo_special(SkkJisyo &object,
                                                                                       FILE *aligned_block_dictionary,
                                                                                       Block *block,
                                                                                       BlockShort *block_short,
                                                                                       char *string,
                                                                                       int &block_length,
                                                                                       int &string_size,
                                                                                       int block_size_maximum,
                                                                                       bool alignment_flag)
        {
                if (block &&
                    block_short)
                {
// 両方のポインタが指定されていてはなりません。
                        DEBUG_PRINTF("ILLEGAL argument\n");
                        DEBUG_ASSERT(0);

                        return false;
                }

                {
                        SkkJisyo::Information information;
                        object.getInformation(information);
                        if (information.get(SkkJisyo::Information::ID_SPECIAL_LINES) == 0)
                        {
// special が存在しない場合は長さ 0 で正常終了。
                                block_length = 0;
                                string_size = 0;

                                return true;
                        }
                }


                int backup_index = object.getIndex();
                int top_offset = 0;
                int line = 1;
                int top_line = 1;
                int top_file_offset = 0;

                if (aligned_block_dictionary)
                {
                        top_file_offset = ftell(aligned_block_dictionary);
                }

                block_length = 0;
                string_size = 0;

                object.seek(SkkJisyo::SEEK_POSITION_TOP);

                while (object.seek(SkkJisyo::SEEK_POSITION_NEXT))
                {
                        ++line;
                        int index = object.getIndex();
                        if (index - top_offset > block_size_maximum)
                        {
// ブロックサイズを越えたので 1 行戻します。
                                object.seek(SkkJisyo::SEEK_POSITION_PREVIOUS);
                                --line;
                                index = object.getIndex();
                                if (index <= top_offset)
                                {
// 戻したにもかかわらず前と同じか小さいということは 1 要素が
// block_size_maximum より大きいこととなり、扱うことができません。
                                        DEBUG_PRINTF("ILLEGAL dictionary\n");
                                        DEBUG_ASSERT(0);
                                        object.setIndex(backup_index);

                                        return false;
                                }

                                if (!create_dictionary_index_and_dictionary_for_class_skk_jisyo_special_core(object,
                                                                                                             aligned_block_dictionary,
                                                                                                             block,
                                                                                                             block_short,
                                                                                                             string,
                                                                                                             string_size,
                                                                                                             block_size_maximum,
                                                                                                             alignment_flag,
                                                                                                             line,
                                                                                                             top_line,
                                                                                                             index,
                                                                                                             top_offset,
                                                                                                             block_length,
                                                                                                             top_file_offset))
                                {
                                        // FIXME!
                                }

                                top_line = line;
                                top_offset = index;
                                ++block_length;
                        }
                }


// 終端付近の処理です。
                {
                        object.seek(SkkJisyo::SEEK_POSITION_BOTTOM);
                        int index = object.getIndex() + 1;
                        if (index - top_offset > block_size_maximum)
                        {
// ブロックサイズを越えたので 1 行戻します。
                                object.seek(SkkJisyo::SEEK_POSITION_PREVIOUS);
                                --line;
                                index = object.getIndex();
                                if (index <= top_offset)
                                {
// 戻したにもかかわらず前と同じか小さいということは 1 要素が
// block_size_maximum より大きいこととなり、扱うことができません。
                                        DEBUG_PRINTF("ILLEGAL dictionary\n");
                                        DEBUG_ASSERT(0);
                                        object.setIndex(backup_index);

                                        return false;
                                }
                                create_dictionary_index_and_dictionary_for_class_skk_jisyo_special_core(object,
                                                                                                        aligned_block_dictionary,
                                                                                                        block,
                                                                                                        block_short,
                                                                                                        string,
                                                                                                        string_size,
                                                                                                        block_size_maximum,
                                                                                                        alignment_flag,
                                                                                                        line,
                                                                                                        top_line,
                                                                                                        index,
                                                                                                        top_offset,
                                                                                                        block_length,
                                                                                                        top_file_offset);
                                top_line = line;
                                top_offset = index;
                                ++block_length;
                                ++line;
                        }


                        object.seek(SkkJisyo::SEEK_POSITION_BOTTOM);
                        index = object.getIndex() + 1;
                        {
                                create_dictionary_index_and_dictionary_for_class_skk_jisyo_special_core(object,
                                                                                                        aligned_block_dictionary,
                                                                                                        block,
                                                                                                        block_short,
                                                                                                        string,
                                                                                                        string_size,
                                                                                                        block_size_maximum,
                                                                                                        alignment_flag,
                                                                                                        line,
                                                                                                        top_line,
                                                                                                        index,
                                                                                                        top_offset,
                                                                                                        block_length,
                                                                                                        top_file_offset);
                                top_offset = index;
                                ++block_length;
                        }
                }

                object.setIndex(backup_index);

                return true;
        }


        static bool create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal_core(SkkJisyo &object,
                                                                                           FILE *aligned_block_dictionary,
                                                                                           FixedArray *fixed_array,
                                                                                           Block *block,
                                                                                           BlockShort *block_short,
                                                                                           char *string,
                                                                                           int *count_work,
                                                                                           int &string_size,
                                                                                           int block_size_maximum,
                                                                                           bool alignment_flag,
                                                                                           int line,
                                                                                           int top_line,
                                                                                           int index,
                                                                                           int top_offset,
                                                                                           int top_fixed_array,
                                                                                           int block_length)
        {
                if (block &&
                    block_short)
                {
// 両方のポインタが指定されていてはなりません。
                        DEBUG_PRINTF("ILLEGAL argument\n");
                        DEBUG_ASSERT(0);

                        return false;
                }

// 前のブロックの最終行の情報を得ます。
                int backup_index = object.getIndex();

                if (!object.seek(SkkJisyo::SEEK_POSITION_PREVIOUS))
                {
// ここで SEEK_POSITION_PREVIOUS が失敗するなどはありえないので不正な形式とします。
                        DEBUG_PRINTF("ILLEGAL dictionary\n");
                        DEBUG_ASSERT(0);
                        object.setIndex(backup_index);

                        return false;
                }


                if (fixed_array)
                {
                        if (*(count_work + top_fixed_array) == 0)
                        {
                                (fixed_array + top_fixed_array)->start_block = block_length;
                                (fixed_array + top_fixed_array)->string_data_offset = string_size;
                        }
                        (fixed_array + top_fixed_array)->block_length = block_length - (fixed_array + top_fixed_array)->start_block + 1;
                }

                if (block)
                {
                        if (aligned_block_dictionary &&
                            alignment_flag)
                        {
                                (block + block_length)->setOffset(ftell(aligned_block_dictionary));
                        }
                        else
                        {
                                (block + block_length)->setOffset(top_offset);
                        }
                        (block + block_length)->setLineLengthAndDataSize(line - top_line,
                                                                         index - top_offset);
                }
                else if (block_short)
                {
                        (block_short + block_length)->setDataSize(index - top_offset);
                }


                {
                        const int terminator_size = 1;
                        int length = object.getMidasiSize() + terminator_size;
                        if (string)
                        {
                                char *p = object.getMidasiPointer();
                                int i;
                                for (i = 0; i != length - terminator_size; ++i)
                                {
                                        *(string + string_size + i) = *(p + i);
                                }
                                *(string + string_size + i) = ' ';
                        }
                        string_size += length;
                }


                if (aligned_block_dictionary)
                {
                        int size = index - top_offset;
                        if (fwrite(object.getBuffer() + top_offset,
                                   size,
                                   1,
                                   aligned_block_dictionary) < 1)
                        {
                                object.setIndex(backup_index);

                                return false;
                        }
                        if (alignment_flag &&
                            (size < block_size_maximum))
                        {
                                fseek(aligned_block_dictionary,
                                      block_size_maximum - (size % block_size_maximum),
                                      SEEK_CUR);
                        }
                }

                ++*(count_work + top_fixed_array);

                object.setIndex(backup_index);

                return true;
        }


/// 辞書インデックスを取得します。同時にアラインメントされた辞書を作成できます。取得に失敗した場合は偽を返します。このとき DEBUG_ASSERT が有効ならばアサートします。
/**
 * block_length と string_size に block と string で確保すべきサイズを
 * 返します。block と string に 0 を渡すと、これらのバッファには触れま
 * せんので、典型的な呼び出し方として以下のように
 * create_block_and_aligned_block_dictionary() を 2 度呼び出すといった
 * 方法があります。
 *
 * \verbatim
 create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal(object,
                                                                   0,
                                                                   fixed_array,
                                                                   0,            // block
                                                                   0,            // string
                                                                   block_length,
                                                                   string_size,
                                                                   block_size_maximum,
                                                                   alignment_flag);

 block = new Block[block_length];
 string = new char[string_size];

 create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal(object,
                                                                   0,
                                                                   fixed_array,
                                                                   block,
                                                                   string
                                                                   block_length,
                                                                   string_size,
                                                                   block_size_maximum,
                                                                   alignment_flag);

\endverbatim
 *
 * もちろん block と string に十分なサイズを与えるか、block_length と
 * string_size が既知ならば 1 度の呼び出しだけでかまいません。
 *
 * aligned_block_dictionary に書き込み可能なファイルポインタを与えると
 * 辞書を出力します。 0 ならば何もしません。
 *
 * alignment_flag が真ならば各ブロックが block_size_maximum でアライン
 * メントされた辞書になります。
 */
01732         static bool create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal(SkkJisyo &object,
                                                                                      FILE *aligned_block_dictionary,
                                                                                      FixedArray *fixed_array,
                                                                                      Block *block,
                                                                                      BlockShort *block_short,
                                                                                      char *string,
                                                                                      int &block_length,
                                                                                      int &string_size,
                                                                                      int block_size_maximum,
                                                                                      bool alignment_flag)
        {
                if (block &&
                    block_short)
                {
// 両方のポインタが指定されていてはなりません。
                        DEBUG_PRINTF("ILLEGAL argument\n");
                        DEBUG_ASSERT(0);

                        return false;
                }

                {
                        SkkJisyo::Information information;
                        object.getInformation(information);
                        if (information.get(SkkJisyo::Information::ID_NORMAL_LINES) == 0)
                        {
// normal が存在しない場合は長さ 0 で正常終了。
                                block_length = 0;
                                string_size = 0;

                                return true;
                        }
                }


                int backup_index = object.getIndex();
                int top_offset = 0;
                int count_work_table[256];
                int top_fixed_array;
                int line = 1;
                int top_line = 1;

                block_length = 0;
                string_size = 0;

                for (int i = 0; i != 256; ++i)
                {
                        count_work_table[i] = 0;
                }


                object.seek(SkkJisyo::SEEK_POSITION_TOP);
                top_fixed_array = object.getFixedArrayIndex();
                if (top_fixed_array < 0)
                {
                        DEBUG_ASSERT(0);
                        object.setIndex(backup_index);

                        return false;
                }

                while (object.seek(SkkJisyo::SEEK_POSITION_NEXT))
                {
                        ++line;
                        int next_fixed_array = object.getFixedArrayIndex();
                        if (next_fixed_array < 0)
                        {
                                DEBUG_PRINTF("ILLEGAL FIXED_ARRAY\n");
                                DEBUG_ASSERT(0);
                                object.setIndex(backup_index);

                                return false;
                        }

                        int index = object.getIndex();
                        if ((top_fixed_array != next_fixed_array) ||
                            (index - top_offset > block_size_maximum))
                        {
                                if (index - top_offset > block_size_maximum)
                                {
// ブロックサイズを越えたので 1 行戻します。
                                        object.seek(SkkJisyo::SEEK_POSITION_PREVIOUS);
                                        --line;
                                        index = object.getIndex();
                                        if (index <= top_offset)
                                        {
// 戻したにもかかわらず前と同じか小さいということは 1 要素が
// block_size_maximum より大きいこととなり、扱うことができません。
                                                SkkUtility::printf("block size error. (block size = %d  line size = %d)\n",
                                                                   block_size_maximum,
                                                                   object.getLineSize());
                                                DEBUG_PRINTF("ILLEGAL dictionary\n");
                                                DEBUG_ASSERT(0);
                                                object.setIndex(backup_index);

                                                return false;
                                        }
// 読み直したので再度 fixed_array を設定します。
                                        next_fixed_array = object.getFixedArrayIndex();
                                        if (next_fixed_array < 0)
                                        {
                                                DEBUG_PRINTF("ILLEGAL FIXED_ARRAY\n");
                                                DEBUG_ASSERT(0);
                                                object.setIndex(backup_index);

                                                return false;
                                        }
                                }

                                if (!create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal_core(object,
                                                                                                            aligned_block_dictionary,
                                                                                                            fixed_array,
                                                                                                            block,
                                                                                                            block_short,
                                                                                                            string,
                                                                                                            count_work_table,
                                                                                                            string_size,
                                                                                                            block_size_maximum,
                                                                                                            alignment_flag,
                                                                                                            line,
                                                                                                            top_line,
                                                                                                            index,
                                                                                                            top_offset,
                                                                                                            top_fixed_array,
                                                                                                            block_length))
                                {
                                        // FIXME!
                                }

                                top_line = line;
                                top_offset = index;
                                top_fixed_array = next_fixed_array;
                                ++block_length;
                        }
                }


// 終端付近の処理です。
                {
                        object.seek(SkkJisyo::SEEK_POSITION_BOTTOM);
                        int index = object.getIndex() + 1;
                        if (index - top_offset > block_size_maximum)
                        {
// ブロックサイズを越えたので 1 行戻します。
                                object.seek(SkkJisyo::SEEK_POSITION_PREVIOUS);
                                --line;
                                index = object.getIndex();
                                if (index <= top_offset)
                                {
// 戻したにもかかわらず前と同じか小さいということは 1 要素が
// block_size_maximum より大きいこととなり、扱うことができません。
                                        DEBUG_PRINTF("ILLEGAL dictionary\n");
                                        DEBUG_ASSERT(0);
                                        object.setIndex(backup_index);

                                        return false;
                                }
                                create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal_core(object,
                                                                                                       aligned_block_dictionary,
                                                                                                       fixed_array,
                                                                                                       block,
                                                                                                       block_short,
                                                                                                       string,
                                                                                                       count_work_table,
                                                                                                       string_size,
                                                                                                       block_size_maximum,
                                                                                                       alignment_flag,
                                                                                                       line,
                                                                                                       top_line,
                                                                                                       index,
                                                                                                       top_offset,
                                                                                                       top_fixed_array,
                                                                                                       block_length);
                                top_line = line;
                                top_offset = index;
                                ++block_length;
                                ++line;
                        }


                        object.seek(SkkJisyo::SEEK_POSITION_BOTTOM);
                        index = object.getIndex() + 1;
                        {
                                create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal_core(object,
                                                                                                       aligned_block_dictionary,
                                                                                                       fixed_array,
                                                                                                       block,
                                                                                                       block_short,
                                                                                                       string,
                                                                                                       count_work_table,
                                                                                                       string_size,
                                                                                                       block_size_maximum,
                                                                                                       alignment_flag,
                                                                                                       line,
                                                                                                       top_line,
                                                                                                       index,
                                                                                                       top_offset,
                                                                                                       top_fixed_array,
                                                                                                       block_length);
                                top_offset = index;
                                ++block_length;
                        }
                }

                object.setIndex(backup_index);

                return true;
        }




public:
        static bool getJisyoType(const char *filename,
                                 JisyoType &type)
        {
                DEBUG_ASSERT_POINTER(filename);

                bool result;

                FILE *file = fopen(filename,
                                   "rb");
                if (file == 0)
                {
                        type = JISYO_TYPE_UNKNOWN;
                        result = false;
                }
                else
                {
                        result = true;

                        char tmp[1024];

                        if (fread(tmp,
                                  1024,
                                  1,
                                  file) < 1)
                        {
                                type = JISYO_TYPE_UNKNOWN;
                                result = false;
                        }
                        else
                        {
                                if (tmp[0] == ';')
                                {
// 1 文字目が ; ならば高確率で SKK 辞書なので、かなりいいかげんですが
// SKK 辞書としてしまいます。
                                        type = JISYO_TYPE_SKK_RAW;
                                }
                                else
                                {
                                        if (fseek(file,
                                                  -sizeof(Information),
                                                  SEEK_END) == -1)
                                        {
                                                type = JISYO_TYPE_UNKNOWN;
                                                result = false;
                                        }
                                        else
                                        {
                                                if (ftell(file) & 0x3)
                                                {
// Information があるべき位置が 4 の倍数でなければ知らない形式です。
                                                        type = JISYO_TYPE_UNKNOWN;
                                                }
                                                else
                                                {
                                                        Information tmp_information;

                                                        if (fread(&tmp_information,
                                                                  sizeof(tmp_information),
                                                                  1,
                                                                  file) < 1)
                                                        {
                                                                type = JISYO_TYPE_UNKNOWN;
                                                                result = false;
                                                        }
                                                        else
                                                        {
                                                                if (tmp_information.get(Information::ID_IDENTIFIER) != IDENTIFIER)
                                                                {
// IDENTIFIER が一致しないので知らない形式です。
                                                                        type = JISYO_TYPE_UNKNOWN;
                                                                }
                                                                else
                                                                {
                                                                        if (tmp_information.get(Information::ID_INDEX_DATA_SIZE) == 0)
                                                                        {
// インデックスデータが存在しないので class SkkJisyo 用です。
                                                                                type = JISYO_TYPE_CLASS_SKK_JISYO;
                                                                        }
                                                                        else
                                                                        {
                                                                                type = JISYO_TYPE_CLASS_SKK_DICTIONARY;
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                        fclose(file);
                }

                return result;
        }


/// SKK 辞書を class SkkJisyo 形式の辞書へ変換します。変換に失敗した場合は偽を返します。
/**
 * class SkkJisyo で扱う形式の辞書にはインデックスデータが存在しません。
 */
02044         static bool createDictionaryForClassSkkJisyo(const char *filename_source,
                                                     const char *filename_destination)
        {
                DEBUG_ASSERT_POINTER(filename_source);
                DEBUG_ASSERT_POINTER(filename_destination);

                bool result = true;
                SkkMmap mmap;
                char *buffer = static_cast<char*>(mmap.map(filename_source));

                if (buffer == 0)
                {
                        result = false;
                }
                else
                {
                        int filesize = mmap.getFilesize();
                        if (filesize < SkkUtility::getStringOkuriAriLength() + SkkUtility::getStringOkuriNasiLength())
                        {
                                result = false;
                        }
                        else
                        {
                                int okuri_ari_index = 0;
                                int okuri_nasi_index = 0;

// まず okuri_ari_index と okuri_nasi_index を求めます。
                                if (!get_index(buffer,
                                               filesize,
                                               okuri_ari_index,
                                               okuri_nasi_index))
                                {
                                        result = false;
                                }
                                else
                                {
                                        result = create_dictionary_for_class_skk_jisyo(filename_destination,
                                                                                       buffer,
                                                                                       filesize,
                                                                                       okuri_ari_index,
                                                                                       okuri_nasi_index);
                                }
                        }
                }

                return result;
        }


/// SKK 辞書を block_size の class SkkDictionary 形式の辞書へ変換します。変換に失敗した場合は偽を返します。
02094         static bool createDictionaryForClassSkkDictionary(const char *filename_source,
                                                          const char *filename_destination,
                                                          int block_size,
                                                          bool alignment_flag = false,
                                                          bool block_short_flag = false)
        {
                DEBUG_ASSERT_POINTER(filename_source);
                DEBUG_ASSERT_POINTER(filename_destination);

                bool result;

                SkkJisyo object;
                FixedArray *fixed_array = 0;
                Block *block = 0;
                BlockShort *block_short = 0;
                char *string = 0;
                int normal_block_length = 0;
                int normal_string_size = 0;
                int special_block_length = 0;
                int special_string_size = 0;

                if (alignment_flag)
                {
                        char tmp_filename[] = "/tmp/skkjisyo.XXXXXX";

// 美しくない上に問題のある方法ですが、テンポラリファイル名を得るためだ
// けに mkstemp() を利用しています。厳密にはこの使い方ではテンポラリファ
// イル名がユニークであることは保証されないことに注意が必要です。
                        int tmp_fd = mkstemp(tmp_filename);
                        if (tmp_fd == -1)
                        {
                                return false;
                        }
                        else
                        {
                                ::close(tmp_fd);
                                result = createDictionaryForClassSkkJisyo(filename_source,
                                                                          tmp_filename);
                                if (result)
                                {
                                        result = object.open(tmp_filename);
                                        if (result)
                                        {
                                                result = object.createDictionaryIndex(block_size,
                                                                                      alignment_flag,
                                                                                      block_short_flag);
                                                if (result)
                                                {
                                                        result = object.getDictionaryIndexInformation(fixed_array,
                                                                                                      block,
                                                                                                      block_short,
                                                                                                      string,
                                                                                                      normal_block_length,
                                                                                                      normal_string_size,
                                                                                                      special_block_length,
                                                                                                      special_string_size);
                                                }
                                                if (result)
                                                {
                                                        if (unlink(tmp_filename) == -1)
                                                        {
                                                                result = false;
                                                        }
                                                        else
                                                        {
                                                                result = object.createDictionaryIndexAndDictionaryForClassSkkJisyo(block_size,
                                                                                                                                   filename_destination,
                                                                                                                                   alignment_flag,
                                                                                                                                   block_short_flag);
                                                        }
                                                }
                                        }
                                }
                        }
                }
                else
                {
                        result = createDictionaryForClassSkkJisyo(filename_source,
                                                                  filename_destination);
                        if (result)
                        {
                                result = object.open(filename_destination);
                                if (result)
                                {
                                        result = object.createDictionaryIndex(block_size);
                                        if (result)
                                        {
                                                result = object.getDictionaryIndexInformation(fixed_array,
                                                                                              block,
                                                                                              block_short,
                                                                                              string,
                                                                                              normal_block_length,
                                                                                              normal_string_size,
                                                                                              special_block_length,
                                                                                              special_string_size);
                                        }
                                }
                        }
                }


                if (!result)
                {
                        unlink(filename_destination);
                }
                else
                {
                        FILE *file = fopen(filename_destination,
                                           "r+b");
                        if (file == 0)
                        {
                                result = false;
                        }
                        else
                        {
                                if (fseek(file,
                                          -sizeof(Information),
                                          SEEK_END) == -1)
                                {
                                        result = false;
                                }

                                Information tmp_information;
                                if (result &&
                                    (fread(&tmp_information,
                                           sizeof(tmp_information),
                                           1,
                                           file) < 1))
                                {
                                        result = false;
                                }
                                if (result)
                                {
                                        if (fseek(file,
                                                  -sizeof(Information),
                                                  SEEK_END) == -1)
                                        {
                                                result = false;
                                        }

                                        IndexDataHeader index_data_header;
                                        {
                                                int32_t tmp = index_data_header.get(IndexDataHeader::ID_BIT_FLAG);
                                                tmp |= block_short_flag ? IndexDataHeader::BIT_FLAG_BLOCK_SHORT : 0;
                                                index_data_header.set(IndexDataHeader::ID_BIT_FLAG,
                                                                      tmp);
                                        }
                                        index_data_header.set(IndexDataHeader::ID_BLOCK_SIZE,
                                                              block_size);
                                        index_data_header.set(IndexDataHeader::ID_NORMAL_BLOCK_LENGTH,
                                                              normal_block_length);
                                        index_data_header.set(IndexDataHeader::ID_SPECIAL_BLOCK_LENGTH,
                                                              special_block_length);
                                        index_data_header.set(IndexDataHeader::ID_NORMAL_STRING_SIZE,
                                                              normal_string_size);
                                        index_data_header.set(IndexDataHeader::ID_SPECIAL_STRING_SIZE,
                                                              special_string_size);
                                        index_data_header.set(IndexDataHeader::ID_SPECIAL_ENTRY_OFFSET,
                                                              tmp_information.get(Information::ID_NORMAL_SIZE));

                                        if (result)
                                        {
                                                int index_data_offset = ftell(file);

                                                if (result)
                                                {
                                                        int size_of_block = block ? sizeof(Block) : sizeof(BlockShort);
                                                        struct
                                                        {
                                                                const void *p;
                                                                size_t size;
                                                        }
                                                        table[] =
                                                        {
                                                                { &index_data_header, sizeof(index_data_header), },
                                                                { fixed_array, sizeof(FixedArray) * 256, },
                                                                { block, sizeof(Block) * (normal_block_length + special_block_length), },
                                                                { block_short, sizeof(BlockShort) * (normal_block_length + special_block_length), },
                                                                { string, normal_string_size + special_string_size, },
                                                                { 0, 0, },
                                                        };

                                                        for (int i = 0;; ++i)
                                                        {
                                                                if ((table[i].p == 0) &&
                                                                    (table[i].size == 0))
                                                                {
                                                                        break;
                                                                }

                                                                if (table[i].p)
                                                                {
                                                                        if (fwrite(table[i].p,
                                                                                   table[i].size,
                                                                                   1,
                                                                                   file) < 1)
                                                                        {
                                                                                result = false;
                                                                                break;
                                                                        }
                                                                }
                                                        }

                                                        if (result)
                                                        {
                                                                result = append_terminator(file);
                                                        }

                                                        if (result)
                                                        {
                                                                int index_data_size = sizeof(index_data_header) +
                                                                        sizeof(FixedArray) * 256 +
                                                                        size_of_block * (normal_block_length + special_block_length) +
                                                                        normal_string_size + special_string_size;
                                                                tmp_information.set(Information::ID_INDEX_DATA_SIZE,
                                                                                    index_data_size);
                                                                tmp_information.set(Information::ID_INDEX_DATA_OFFSET,
                                                                                    index_data_offset);
                                                                if (fwrite(&tmp_information,
                                                                           sizeof(tmp_information),
                                                                           1,
                                                                           file) < 1)
                                                                {
                                                                        result = false;
                                                                }
                                                        }
                                                }
                                        }
                                }

                                fclose(file);
                                SkkUtility::chmod(filename_destination,
                                                  0644);
                        }
                }

                return result;
        }


/// 指定した辞書ファイルから Information を取得します。取得に失敗した場合は偽を返します。
02335         static bool getInformation(const char *filename,
                                   Information &information)
        {
                DEBUG_ASSERT_POINTER(filename);

                JisyoType type;
                bool result = getJisyoType(filename,
                                           type);

                if ((type == JISYO_TYPE_CLASS_SKK_JISYO) ||
                    (type == JISYO_TYPE_CLASS_SKK_DICTIONARY))
                {
                        FILE *file = fopen(filename,
                                           "rb");
                        if (file == 0)
                        {
                                result = false;
                        }
                        else
                        {
                                result = true;

                                if (fseek(file,
                                          -sizeof(Information),
                                          SEEK_END) == -1)
                                {
                                        result = false;
                                }

                                if (result &&
                                    (fread(&information,
                                           sizeof(information),
                                           1,
                                           file) < 1))
                                {
                                        result = false;
                                }

                                fclose(file);
                        }
                }
                else
                {
                        result = false;
                }

                return result;
        }


/// 指定した辞書ファイルから IndexDataHeader を取得します。取得に失敗した場合は偽を返します。
02386         static bool getIndexDataHeader(const char *filename,
                                       IndexDataHeader &index_data_header)
        {
                DEBUG_ASSERT_POINTER(filename);

                JisyoType type;
                bool result = getJisyoType(filename,
                                           type);

                if (type == JISYO_TYPE_CLASS_SKK_DICTIONARY)
                {
                        FILE *file = fopen(filename,
                                           "rb");
                        if (file == 0)
                        {
                                result = false;
                        }
                        else
                        {
                                result = true;

                                if (fseek(file,
                                          -sizeof(Information),
                                          SEEK_END) == -1)
                                {
                                        result = false;
                                }

                                Information tmp_information;
                                if (result &&
                                    (fread(&tmp_information,
                                           sizeof(tmp_information),
                                           1,
                                           file) < 1))
                                {
                                        result = false;
                                }

                                int index_data_offset = tmp_information.get(Information::ID_INDEX_DATA_OFFSET);
                                if (result &&
                                    (fseek(file,
                                           index_data_offset,
                                           SEEK_SET) == -1))
                                {
                                        result = false;
                                }

                                if (result &&
                                    (fread(&index_data_header,
                                           sizeof(index_data_header),
                                           1,
                                           file) < 1))
                                {
                                        result = false;
                                }

                                fclose(file);
                        }
                }
                else
                {
                        result = false;
                }

                return result;
        }


        ~SkkJisyo()
        {
                close();
        }


        SkkJisyo() :
                mmap_(0),
                filename_buffer_(0),
                state_(STATE_NORMAL),
                information_(),

                fixed_array_(0),
                block_(0),
                block_short_(0),
                string_(0),
                normal_block_length_(0),
                normal_string_size_(0),
                special_block_length_(0),
                special_string_size_(0),

                open_failure_flag_(false)
        {
                for (int i = 0; i != STATE_LENGTH; ++i)
                {
                        buffer_table_[i] = 0;
                        index_table_[i] = 0;
                        size_table_[i] = 0;
                }
        }


/// 辞書インデックスをオブジェクト内に生成し、 filename_destination が指定されていれば class SkkJisyo 形式の辞書を書き出します。変換に失敗した場合は偽を返します。
/**
 * 辞書は block_size でアラインメントされます。
 *
 * 辞書インデックスは filename_destination へ 0 を指定して生成します。
 * createDictionaryIndex() は、まさにそのような実装になっています。
 */
02493         bool createDictionaryIndexAndDictionaryForClassSkkJisyo(int block_size,
                                                                const char *filename_destination,
                                                                bool alignment_flag = false,
                                                                bool block_short_flag = false)
        {
                int normal_block_length;
                int normal_string_size;
                int special_block_length = 0;
                int special_string_size = 0;
                FILE *file;

                if (filename_destination)
                {
                        file = fopen(filename_destination,
                                     "wb");
                        if (file == 0)
                        {
                                return false;
                        }
                }
                else
                {
                        file = 0;
                }

                if (fixed_array_ == 0)
                {
                        fixed_array_ = new FixedArray[256];
                }

                bool result;

                if (normal_block_length_ == 0)
                {
                        setState(SkkJisyo::STATE_NORMAL);
                        result = create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal(*this,
                                                                                                   0,
                                                                                                   fixed_array_,
                                                                                                   0,
                                                                                                   0,
                                                                                                   0,
                                                                                                   normal_block_length,
                                                                                                   normal_string_size,
                                                                                                   block_size,
                                                                                                   alignment_flag);
                        setState(SkkJisyo::STATE_SPECIAL);
                        if (result)
                        {
                                result = create_dictionary_index_and_dictionary_for_class_skk_jisyo_special(*this,
                                                                                                            0,
                                                                                                            0,
                                                                                                            0,
                                                                                                            0,
                                                                                                            special_block_length,
                                                                                                            special_string_size,
                                                                                                            block_size,
                                                                                                            alignment_flag);
                                normal_block_length_ = normal_block_length;
                                normal_string_size_ = normal_string_size;
                                special_block_length_ = special_block_length;
                                special_string_size_ = special_string_size;
                        }
                        setState(SkkJisyo::STATE_NORMAL);
                }
                else
                {
                        result = true;
                }

                if (result)
                {
                        if (string_ == 0)
                        {
                                string_ = new char[normal_string_size_ + special_string_size_];
                                SkkUtility::clearMemory(string_,
                                                        normal_string_size_ + special_string_size_);
                        }
                        else
                        {
                                // 既に確保されている。
                        }

// block_ と block_short_ は同時に設定してはならないため、片方に確保し
// た場合は、もう片方を解放し 0 を設定します。
                        if (block_short_flag)
                        {
                                if (block_short_ == 0)
                                {
                                        delete[] block_;
                                        block_ = 0;

                                        block_short_ = new BlockShort[normal_block_length_ + special_block_length_];
                                }
                                else
                                {
                                        // 既に確保されています。
                                }
                        }
                        else
                        {
                                if (block_ == 0)
                                {
                                        delete[] block_short_;
                                        block_short_ = 0;

                                        block_ = new Block[normal_block_length_ + special_block_length_];
                                }
                                else
                                {
                                        // 既に確保されています。
                                }
                        }

                        if (result)
                        {
                                setState(SkkJisyo::STATE_NORMAL);
                                result = create_dictionary_index_and_dictionary_for_class_skk_jisyo_normal(*this,
                                                                                                           file,
                                                                                                           fixed_array_,
                                                                                                           block_,
                                                                                                           block_short_,
                                                                                                           string_,
                                                                                                           normal_block_length,
                                                                                                           normal_string_size,
                                                                                                           block_size,
                                                                                                           alignment_flag);
                                int normal_size;
                                if (file)
                                {
                                        normal_size = ftell(file);
                                }
                                else
                                {
                                        normal_size = 0;
                                }

                                if (result)
                                {
                                        setState(SkkJisyo::STATE_SPECIAL);
                                        Block *tmp_block = 0;
                                        BlockShort *tmp_block_short = 0;
                                        if (block_)
                                        {
                                                tmp_block = block_ + normal_block_length;
                                        }
                                        if (block_short_)
                                        {
                                                tmp_block_short = block_short_ + normal_block_length;
                                        }
                                        result = create_dictionary_index_and_dictionary_for_class_skk_jisyo_special(*this,
                                                                                                                    file,
                                                                                                                    tmp_block,
                                                                                                                    tmp_block_short,
                                                                                                                    string_ + normal_string_size,
                                                                                                                    special_block_length,
                                                                                                                    special_string_size,
                                                                                                                    block_size,
                                                                                                                    alignment_flag);
                                        setState(SkkJisyo::STATE_NORMAL);
                                }

                                if (result &&
                                    file)
                                {
                                        int special_size = ftell(file) - normal_size;
                                        append_information(file,
                                                           block_size,
                                                           normal_size,
                                                           information_.get(Information::ID_NORMAL_LINES),
                                                           special_size,
                                                           information_.get(Information::ID_SPECIAL_LINES),
                                                           0,
                                                           0);
                                }
                        }
                }

                if (file)
                {
                        fclose(file);
                }

                return result;
        }


/// 辞書インデックスをオブジェクト内に生成します。生成に失敗した場合は偽を返します。
02680         bool createDictionaryIndex(int block_size,
                                   bool alignment_flag = false,
                                   bool block_short_flag = false)
        {
                return createDictionaryIndexAndDictionaryForClassSkkJisyo(block_size,
                                                                          0,
                                                                          alignment_flag,
                                                                          block_short_flag);
        }


/// 辞書インデックスの情報を得ます。取得に失敗した場合は偽を返し、 DEBUG_ASSERT が有効ならばアサートします。
02692         bool getDictionaryIndexInformation(FixedArray *&fixed_array,
                                           Block *&block,
                                           BlockShort *&block_short,
                                           char *&string,
                                           int &normal_block_length,
                                           int &normal_string_size,
                                           int &special_block_length,
                                           int &special_string_size)
        {
                if ((fixed_array_ == 0) ||
                    ((block_ == 0) &&
                     (block_short_ == 0)) ||
                    (string_ == 0))
                {
                        DEBUG_ASSERT(0);

                        return false;
                }
                else
                {
                        fixed_array = fixed_array_;
                        block = block_;
                        block_short = block_short_;
                        string = string_;
                        normal_block_length = normal_block_length_;
                        normal_string_size = normal_string_size_;
                        special_block_length = special_block_length_;
                        special_string_size = special_string_size_;

                        return true;
                }
        }


        bool close()
        {
                delete[] string_;
                delete[] block_;
                delete[] fixed_array_;

                delete mmap_;
                delete[] filename_buffer_;

                mmap_ = 0;
                filename_buffer_ = 0;
                state_ = STATE_NORMAL;
//                 information_;

                fixed_array_ = 0;
                block_ = 0;
                string_ = 0;
                normal_block_length_ = 0;
                normal_string_size_ = 0;
                special_block_length_ = 0;
                special_string_size_ = 0;

                open_failure_flag_ = false;

                for (int i = 0; i != STATE_LENGTH; ++i)
                {
                        buffer_table_[i] = 0;
                        index_table_[i] = 0;
                        size_table_[i] = 0;
                }

                return true;
        }


/// 辞書をオープンします。正常にオープンできれば真を返します。失敗した場合は偽を返し、 DEBUG_ASSERT が有効ならばアサートします。
/**
 * class SkkJisyo で使用できる辞書は
 * 「createDictionaryForClassSkkJisyo()」または
 * 「createDictionaryIndexAndDictionaryForClassSkkJisyo() アラインメン
 * トなし」で生成されたものです。
 *
 * 通常の SKK-JISYO やアラインメントされた辞書は class SkkJisyo では使
 * 用できないため、オープンに失敗します。
 */
02771         bool open(const char *filename)
        {
                DEBUG_ASSERT_POINTER(filename);

                int length = 0;
                const int limit = 4096;
                const int margin = 16;

                while (*(filename + length) != '\0')
                {
                        ++length;
                }

                if ((length == 0) ||
                    (length > limit - margin))
                {
                        DEBUG_ASSERT(0);
                        open_failure_flag_ = true;

                        return false;
                }
                else
                {
                        filename_buffer_ = new char[length + margin];

                        int i = 0;
                        for (i = 0; *(filename + i) != '\0'; ++i)
                        {
                                *(filename_buffer_ + i) = *(filename + i);
                        }
                        *(filename_buffer_ + i) = '\0';
                        mmap_ = new SkkMmap();
                        buffer_table_[STATE_NORMAL] = static_cast<char*>(mmap_->map(filename_buffer_));
                        if (buffer_table_[STATE_NORMAL] == 0)
                        {
                                DEBUG_ASSERT(0);
                                open_failure_flag_ = true;

                                return false;
                        }

                        if (*buffer_table_[STATE_NORMAL] == ';')
                        {
// 先頭 1 バイトが ';' なデータは通常の SKK-JISYO の可能性が高いため
// (少なくとも class SkkJisyo では不正な形式なので) 失敗します。
                                DEBUG_ASSERT(0);
                                open_failure_flag_ = true;

                                return false;
                        }

                        information_.initialize(buffer_table_[STATE_NORMAL] + mmap_->getFilesize() - Information::getSize());

                        if ((information_.get(Information::ID_IDENTIFIER) != IDENTIFIER) ||
                            (information_.get(Information::ID_BLOCK_ALIGNMENT_SIZE) != 0))
                        {
// IDENTIFIER が不正、またはアラインメントされた辞書ならば失敗します。
                                open_failure_flag_ = true;

                                return false;
                        }

                        size_table_[STATE_NORMAL] = information_.get(Information::ID_NORMAL_SIZE);
                        size_table_[STATE_SPECIAL] = information_.get(Information::ID_SPECIAL_SIZE);

                        buffer_table_[STATE_SPECIAL] = buffer_table_[STATE_NORMAL] + size_table_[STATE_NORMAL];

                        if (buffer_table_[STATE_NORMAL])
                        {
                                seek(SEEK_POSITION_TOP);
                        }
                }

                return true;
        }


        void getInformation(Information &information)
        {
                information = information_;
        }


        State getState()
        {
                return state_;
        }


        void setState(State state)
        {
                state_ = state;
        }


        char *getBuffer()
        {
                return buffer_table_[state_];
        }


        int getIndex()
        {
                return index_table_[state_];
        }


        void setIndex(int index)
        {
                index_table_[state_] = index;
        }


/// 指定位置にシークします。バッファ外をアクセスしようとした場合に偽を返します。
/**
 *
 * 偽を返す条件は以下の通り
 *
 * \li インデックスが -1 。つまりバッファ終端に差し掛かった。
 *
 * \li バッファ先端から(インデックスが 0 で)更に前の行へシークしようとした。
 *
 * なお、先端に差し掛かっただけではインデックスが 0 になるだけで、偽を
 * 返さないことに注意が必要です。 (インデックス 0 の状態で更にシークし
 * て初めて偽を返す)
 *
 */
02898         bool seek(SeekPosition position)
        {
                if (open_failure_flag_)
                {
                        return false;
                }

                bool result = true;

                switch (position)
                {
                case SEEK_POSITION_TOP:
                        index_table_[state_] = 0;
                        break;

                case SEEK_POSITION_BOTTOM:
                        index_table_[state_] = size_table_[state_] - 1;
                        break;

                case SEEK_POSITION_NEXT:
                        index_table_[state_] = SkkUtility::getNextLineIndex(buffer_table_[state_],
                                                                            index_table_[state_],
                                                                            size_table_[state_]);
                        break;

                case SEEK_POSITION_PREVIOUS:
                        if (index_table_[state_] == 0)
                        {
                                result = false;
                        }
                        else
                        {
                                index_table_[state_] = SkkUtility::getPreviousLineIndex(buffer_table_[state_],
                                                                                        index_table_[state_],
                                                                                        size_table_[state_]);
                        }
                        break;

                case SEEK_POSITION_BEGINNING_OF_LINE:
                        index_table_[state_] = SkkUtility::getBeginningOfLineIndex(buffer_table_[state_],
                                                                                   index_table_[state_],
                                                                                   size_table_[state_]);
                        break;

                default:
                        DEBUG_ASSERT(0);
                        break;
                }

                if (index_table_[state_] < 0)
                {
                        result = false;
                }

                return result;
        }


/// 指定文字を探索します。本メソッドは内部でステートを変更することに注意が必要です。「見出し」はスペースでターミネートされます。見付からなければ偽を返します。
02957         bool search(const char *midasi)
        {
                DEBUG_ASSERT_POINTER(midasi);

                if (open_failure_flag_)
                {
                        return false;
                }

                const int margin = 8;
                char encoded_midasi[SkkUtility::ENCODED_MIDASI_BUFFER_SIZE];

                int encoded_size = SkkUtility::encodeHiragana(midasi,
                                                              encoded_midasi,
                                                              sizeof(encoded_midasi) - margin);
                if (encoded_size == 0)
                {
                        encoded_midasi[0] = '\1';
                        int i;
                        for (i = 0; sizeof(encoded_midasi) - margin; ++i)
                        {
                                if ((*(midasi + i) == ' ') ||
                                    (*(midasi + i) == '\0'))
                                {
                                        goto FOUND_TERMINATOR;
                                }
                                encoded_midasi[i + 1] = *(midasi + i);
                        }

                        return false;

                FOUND_TERMINATOR:
                        encoded_midasi[i + 1] = '\0';
                }
                else
                {
                        encoded_midasi[encoded_size] = '\0';
                }


// まずバイナリサーチで探索し、見付からない場合はバイナリサーチの出力を
// 利用した上で線型探索します。
                if (SkkUtility::getFixedArrayIndex(encoded_midasi) == -1)
                {
                        state_ = STATE_SPECIAL;

                        int index;
                        if (SkkUtility::searchBinary(buffer_table_[state_],
                                                     size_table_[state_],
                                                     encoded_midasi,
                                                     index))
                        {
                                setIndex(index);

                                return true;
                        }
                        else
                        {
                                if (SkkUtility::searchLinear(buffer_table_[state_],
                                                             size_table_[state_],
                                                             encoded_midasi,
                                                             index))
                                {
                                        setIndex(index);

                                        return true;
                                }
                        }
                }
                else
                {
                        state_ = STATE_NORMAL;

                        int index;
                        if (SkkUtility::searchBinary(buffer_table_[state_],
                                                     size_table_[state_],
                                                     encoded_midasi,
                                                     index))
                        {
                                setIndex(index);

                                return true;
                        }
                        else
                        {
                                if (SkkUtility::searchLinear(buffer_table_[state_],
                                                             size_table_[state_],
                                                             encoded_midasi,
                                                             index))
                                {
                                        setIndex(index);

                                        return true;
                                }
                        }
                }

                return false;
        }


        int getMidasiSize()
        {
                return SkkUtility::getMidasiSize(buffer_table_[state_],
                                                 getIndex(),
                                                 size_table_[state_]);
        }


        char *getMidasiPointer()
        {
                return buffer_table_[state_] + getIndex();
        }


/// index が行頭位置にあるとして、「変換文字列」のサイズを返します。サイズに改行文字は含みません。
03073         int getHenkanmojiretsuSize()
        {
                return SkkUtility::getHenkanmojiretsuSize(buffer_table_[state_],
                                                          getIndex(),
                                                          size_table_[state_]);
        }


/// index が行頭位置にあるとして、「変換文字列」のポインタを返します。
03082         const char *getHenkanmojiretsuPointer()
        {
                return SkkUtility::getHenkanmojiretsuPointer(buffer_table_[state_],
                                                             getIndex(),
                                                             size_table_[state_]);
        }


/// index が行頭位置にあるとして、 1 行のサイズを返します。サイズに改行文字は含みません。
03091         int getLineSize()
        {
                return SkkUtility::getLineSize(buffer_table_[state_],
                                               getIndex(),
                                               size_table_[state_]);
        }


        int getFixedArrayIndex()
        {
                return SkkUtility::getFixedArrayIndex(buffer_table_[state_] + getIndex());
        }




private:
        SkkMmap *mmap_;
        char *filename_buffer_;
        State state_;
        Information information_;

        FixedArray *fixed_array_;
        Block *block_;
        BlockShort *block_short_;
        char *string_;
        int normal_block_length_;
        int normal_string_size_;
        int special_block_length_;
        int special_string_size_;

        bool open_failure_flag_;

        char *buffer_table_[STATE_LENGTH];
        int index_table_[STATE_LENGTH];
        int size_table_[STATE_LENGTH];
};




#endif  // SKK_JISYO_H

Generated by  Doxygen 1.6.0   Back to index