Citrus iconv の実装
塩崎 拓也 Takuya SHIOZAKI ∗<tshiozak@bsdclub.org>
2003 年 10 月 18 日
概要
本稿は，POSIX[1] において定義されている文字コード変換関数である iconv のCitrus Project[2] における実装について述べたものである．本稿では，Citrus iconvの実装についての詳細を述べ，本 iconv 実装の利点および欠点について議論する．

1 はじめにiconv は Unix-like なオペレーティングシステムにおける標準的な文字コード変換API である．今日では POSIX によってその仕様が規定されており，多くのアプリケーションが iconv を利用している．ところが，各 BSD には標準では iconv が存在しておらず，これを利用するためには例
えば GNU libiconv[4] といった追加パッケージをインストールする必要があった．
このことは，各 BSD が POSIX 準拠を謳

2 概念
Citrus iconv の実装について述べる前に，いくつかの概念について説明する．

2.1 コードセット (codeset)
この言葉は非常に漠然とした言葉だが，本稿では「ある具体的な文字の並び (文字列) をビットやオクテットなどの列で表現するための方法」と定義する．ここで，「ある具体的な」と注記しているのは，のちに述べる CES と区別するためである．
コードセットの例としては，EUC-JP やう際の障壁になるだけではなく，さまざまISO-2022-JP などが挙げられる．

なアプリケーションをインストールする際

2.2 CES と CCSの手間の増加などの実用上の問題を生じさせる．

Citrus Project は各 BSD における国際化機能の実装を行うことを目的としており，iconv の実装もその目的の一つである．
筆者により NetBSD に統合された iconvは，この成果である．

これらの用語は RFC2130[6] において定義され，Unicode TR17[7] において拡張されたものである．

2.2.1 符号化文字集合 (CCS)
コンピュータ上での文字集合は，各文字に一意なインデックス値を対応づけた文字の集合として表現される．このようなインデックス付きの文字集合のことを符号化文∗ Citrus Project[2], NetBSD[3] developer.

字集合 (Coded Character Set) と呼ぶ．

たとえば，JIS X 0208 漢字集合は，区点というインデックスづけが行われた CCS

2.3 マルチバイト文字とワイド文字
CES の項で説明したようなバイト列でとして表現することができる．

表現された文字列を処理する場合，バイト

2.2.2 エンコーディングスキーム (CES)

列表現のままでは文字の区切りなどがはっ一般的に，コードセットとは CCS を 1きりせず扱いにくいため，一文字を 16bitつ以上含んだものである．必ずしも一つのや 32bit の整数値とした内部表現で扱うのCCS のみを含んでいなければならないとが一般的である．たとえば ISO C locale

いうことはなく，たとえば，EUC-JP コーの LC CTYPE カテゴリでは，ある文字にドセットは US-ASCII, JIS X 0208, JIS X

対応する内部表現は一般的に wchar t 型

0201 仮名, JIS X 0212 という 4 つの CCSの整数値となるが，この場合には外部表現を含んでいる．各コードセットにおいて，のことをマルチバイト文字列，内部表現の実際の文字列をどのようなバイト列で表ことをワイド文字列と呼ぶ．

現するかという方法を，エンコーディング

2.4 UCS 正規化と文字集合独立スキーム (Character Encoding Scheme,ワイド文字列への変換の方法としては，CES) と呼ぶ．たとえば EUC-JP コード

UCS 正規化 (UCS Normalization) と文セットの文字列は EUC (Extended Unix

字集合独立 (Character Set Independent,Codeset) という CES で表現され，これCSI) という二つの流儀が存在する．

は図 1 に示すようなバイト列により 4 つ

UCS とは Unified Character Set の略の図形文字集合を保持することができる．

であり，広義には「あらゆる文字を含ん同様に，Shift JIS コードセットの文字列だ大きな単一の文字集合」を意味し，狭義は DBCS (Double Byte Character Set)には「Unicode[5] 文字集合」を意味する．

Shift という CES で表現される1 ．

UCS 正規化方式の国際化処理では，マルチバイト文字をコードセットに依存した

G0 集合
G1 集合
G2 集合
G3 集合

(0xxxxxxx)2(1xxxxxxx)2 ...
(10001110)2 (1xxxxxxx)2 ...
(10001111)2 (1xxxxxxx)2 ...

図 1: EUC エンコーディングスキーム

ワイド文字表現へと変換した後，これをUCS のインデックス値によるワイド文字表現へと「正規化」することによって処理の単純化を狙っている．
他方，CSI 方式の国際化処理では，マルチバイト文字からコードセットに依存し

1[6] の記述は本稿で解説している CES を簡潔かつ明確に定義しているのに対し，[7] の記述は (おそらくは前者と同じものを定義しようとしているのだと推測されるのだが) 本稿における「コードセット」との区別が不明瞭である．

たワイド文字表現に変換し，この形式のままで処理を行う．これにより，UCS という特定の CCS に依存しない柔軟な処理を狙っている．

Citrus の 国 際 化 フ レ ー ム ワ ー ク で はiconv サポートよりも先に LC CTYPE のサポートを行っているため，マルチバイト文字とワイド文字との間で相互変換する部分がモジュール化されている．後で述べる
が Citrus の国際化フレームワークは CSI方式で作られているため，たとえ同じ符号化文字集合の同じ文字を表わす場合でも，3 iconv とはiconv は，ある文字コードで記述された文字列を，それと (なるべく) 等価な別の文字コードの文字列へと変換するためのAPI である．そのインターフェースの定義を図 2 に示す．

3.1 iconv の処理

一般にワイド文字の表現はコードセットにiconv の処理は基本的に，依存して異なる．たとえば，「あ」という1. 入力バッファから一文字読み込み

JIS X 0208 の文字に対応するワイド文字

2. 表変換などで出力側の文字コードへの値は，UCS 正規化実装の場合には外部と変換しのコードセットにかかわらず常に同じ値を3. それを出力バッファへと出力する

取るのに対し，CSI 実装である Citrus で

4. 1 から 3 を各文字について繰り返す

は外部のコードセットが EUC-JP と Shift

JIS の場合でそのワイド文字の値が異なる(表 1)2 ．
2という順序で行われる．前述したように，文字列をマルチバイト表現のままで扱おうとするとその後の処理が難しくので，読みこの表において，例として UTF-8 などのUnicode 系のコードセットを取り上げていな
いのは意図的なものである．CSI の観点からは，JIS X 0208 集合における「あ」という文字と Unicode 集合における「あ」という文字は，別の CCS に属する文字なのでそれぞれ論込まれた文字は何らかのワイド文字表現へ理的に関連性がないものとして扱われる．この観点で言えば，この「フレームワーク上はもともと関連性のない文字」に関連性を与えることが iconv の仕事であると言える．一方，
UCS 正規化方式では，これらの文字が「等しい」ことをフレームワークが保証していることになる．

く分けて UCS 正規化と文字集合独立の二コードセット

マルチバイト
文字表現

ワイド
文字表現

EUC-JP
0xA4 0xA2
U+3042
Shift JIS
0x82 0xA0
U+3042
UCS 正規化における表現と変換されることになる．そして iconv の場合にも，ワイド文字表現へと変換された
あとの文字の取り扱い方法としては，大きつが存在する．

コードセットマルチバイト
文字表現

ワイド
文字表現

EUC-JP
0xA4 0xA2
0xA4A2
Shift JIS
0x82 0xA0
0x82A0Citrus における表現

表 1: 文字「あ」のワイド文字表現

iconv t iconv open(const char *dstname, const char *srcname);
コンバータを開き，そのハンドルを返す．
dstname 変換先文字列の文字コード名
srcname 変換元文字列の文字コード名

iconv t iconv(iconv t cd, char **src, size t *srclen, char *dst,size t *dstlen);
文字列を変換する．
cdコンバータのハンドル
src
変換元バッファへのポインタ変数へのポインタ
srclen 変換元バッファのサイズを保持する変数へのポインタ
dst
変換先バッファへのポインタ変数へのポインタ
dstlen 変換先バッファのサイズを保持する変数へのポインタ

iconv t iconv close(iconv t cd);コンバータを閉じる．
cd
コンバータのハンドル

図 2: iconv API

ションの削減である．iconv がサポートす3.1.1 UCS 正規化による iconv 実装
UCS 正規化方式の iconv 実装では，前る文字コードが n 個存在する場合，これ述したように入力バッファから読み込んだらを入力および出力に取る変換のすべて文字を入力コードセット依存のワイド文字の組み合わせの数は n2 となる．これを単表現へと変換し，これをさらに UCS のイ純に実装しようとすると，いうまでもなくンデックス値へと正規化する．その後，こn2 通りの変換処理を作成する必要がある．の UCS のインデックス値を今度は出力側

UCS 正規化方式では常に UCS を経由すの文字コードにおけるワイド文字表現へとるため，必要な変換は「入力文字コード→変換し，これをさらにマルチバイト表現へUCS」の n 通りと「UCS →出力文字コーと変換して出力バッファへ書き込むこととド」の n 通りの計 2n 通りの変換を用意すなる (図 3)．

れば済むことになる．

iconv の実装に UCS 正規化を利用する

一方で UCS に存在しない文字をうまくことによるメリットは，変換のバリエー扱えないということと，あらゆる場面で一マルチバイト↓
マルチバイト ワイド文字
文字-ワイド文字

ワイド文字↓UCSUCSUCS
↓
ワイド文字

ワイド文字

図 3: UCS 正規化実装における iconv の処理

ワイド文字↓
マルチバイト マルチバイト
文字-旦 UCS を経由するための変換が必要にな

4 設計目標るというデメリットが存在する．

iconv を実装するにあたり，筆者は以下

UCS 正規化を用いた実装の代表例がのような指針で設計を行った:

GNU libiconv である．
3.1.2 CSI による iconv 実装

文字集合独立あらゆる国際化関数を文字一方，CSI による実装では，特定の文字集合独立な形で実装するというのが集合に対する依存を排除した実装が行われCitrus の目標の一つであり，これはなければならない．そのため，各文字に対iconv も例外ではない．

応するワイド文字表現は，その時に選択さiconv の行う処理は，他のコードの共有れている入力側と出力側のそれぞれのコー国際化関数の機能と重なる部分がドセットに依存したままのワイド文字表ある．このような部分の共有を目現で扱われ，UCS 正規化のようにフレー指す．

ムワークとしてある特定の文字集合への拡張可能性

すべてのデータや変換方式お正規化は行わず，そのインデックス値が直よびエンコーディング方式の処理

接変換される (図 4)．CSI の実装では，新ルーチンは，アプリケーションやラ規の文字集合の追加が容易である半面，実イブラリ (libc) 本体を再コンパイル

装が多少抽象的で複雑になる傾向がある．

ことなしに追加できるようにする．

また，UCS 正規化のところで述べた通り，冗長な変換テーブルの削減

前述したとお単純な方法では変換のバリエーションが文り，内部に共通の CCS を持つコー字コードの数の二乗となってしまう．そこドセットの組が存在する．これらので，いかにしてこのバリエーションを減らコードセットから別のある単一のすかが CSI 実装の鍵となる．

コードセットへと変換する場合に，既存の iconv 実装では，CSI なものの代それぞれのワイド文字表現に関連し表が Solaris の実装である．

た別々の変換表を持っていると冗長なので，これを共通化する．

マルチバイト文字

マルチバイト↓ワイド文字- インデックス
ワイド文字

変換

ワイド文字ワイド文字↓マルチバイト

図 4: CSI 実装における iconv の処理

マルチバイト文字上で述べたとなる．そのため，Citrus iconv ではエンおり，単純な方法では変換のバリコーディングスキームデータベース (En-エーションが文字コードの数の二乗

coding Scheme Database, ESDB) と呼ばとなってしまう．これをなるべく削れるデータベースによって，各コードセッ減する方法を採用する．

トの CSID と CCS 名の対応関係を保持

変換バリエーションの削減している．図 5 に，例として EUC-JP の4.1 CSID, index, ESDBEUC-JP と Shift JIS のように，同じCCS のある文字に対して違う内部表現をESDB を示す3 ．
CSID 値はコードセットに依存している
ため，同じ CCS を表わすための CSID でもっているケースを考える．ここで，両方も，コードセットが異なれば CSID 値はが共通して持っている文字集合，たとえば一般に異なる．一方で，同じ CCS の同じJIS X 0208 文字集合のある文字を，別の文字集合，たとえば Unicode 文字集合の対応する文字へと変換しようとする際に，内部表現のままで扱おうとすると，EUC-JP とShift JIS とで別々の変換表が必要になってしまい無駄である．
これを解決するために Citrus iconv では，ある一文字に対応する内部表現として単純なワイド文字ではなく，文字集合識別
子 (Character Set Identifier, CSID) とインデックス (index) という二つの 32bit 整数値のペアを用いる．ここで，どの CSIDがどの CSID 値に対応するのかはそれぞれのコードセットおよび CES によって異NAME
ENCODING
VARIABLEDEFCSIDDEFCSID
DEFCSID
DEFCSID
INVALID

文字に対する index 値は常に同一である．
たとえば，前節で挙げた「あ」という文字を例に取ると，EUC-JP の場合には (CSID,index) = (0x8080, 0x2422) であるのに対し，Shift JIS では (0x0002, 0x2422) となる．両者の CSID の値は異なるもののindex 値は一致している．このような場
合，それぞれの CSID が同じ CCS を示しているのかどうかは，それぞれのコー3(実際にはこれをコンパイルしたバイナリ形式が用いられる．また，ESDB にはマルチバイ
ト⇔ワイド文字変換モジュールの指定と，そのパラメータも含んでいる (「ENCODING」と「VARIABLE」がそれである)．

"EUC-JP""EUC""1 0x0000 2 0x8080 2 0x0080 3 0x8000 0x8080""ISO646-US"
0"JISX0208:1990"
0x8080"JISX0201-KANA"
0x0080"JISX0212"
0x80000xA2AE # GETA

図 5: EUC-JP の ESDB

ドセットに対応した ESDB を参照して判

4.3 ピボットコード別する．この例では，EUC-JP における文字集合マッパーは，入力および出力の0x8080 と Shift JIS における 0x0002 が符号化文字集合の名前を与えるとそれに両方とも「JISX0208:1990」という名前で対応するマッパーを開くしくみになっていあるということからこれが等しいものだとる．そのため，単純な方法では符号化文字判断できる．

集合の二乗の数のマッパーが必要になる．

このような方法により，Citrus iconv で

一方 UCS 正規化方式では，すべての変換はコードセットから CCS と CES を分離は一度 UCS を経由することによって表のしている．分離された後の index 値は，も数を抑えている．

ともとのコードセットとは独立した形で

Citrus iconv では，UCS 正規化方式の利用することができる．先の例では，元のこの利点を取り入れるために，ピボット

コードセットが EUC-JP であろうが Shiftコードという概念を導入している．文字集

JIS であろうが，その index 値 0x2422 を合マッパーに付随するデータとしてピボッ

JISX0208 文字集合の「あ」という文字をトデータベースというものを持っており，表わすものとして統一的に扱うことができこれは，マッパーによってたがいに隣接しるのである．ている CCS と，その変換の距離 (norm)

なお，マルチバイト表現と (CSID, in-を記述したものである．文字集合マッパーdex) ペアとを相互変換する部分は，既存に与えられた二つの CCS に対応する直の locale の LC CTYPE カテゴリを再利接の変換が存在しない場合でも，このピ用している．

ボットデータベースから中継可能なコード

4.2 文字集合マッパー(ピボットコード) が見つけられれば，そCSID と index のペアに分離されたあのコードを経由して自動的に変換が行われと，ある CSS の index 値を別の CSS のる．複数のピボットコードの候補がある場

同じ文字の index 値へと変換するのが文合には，距離の合計が一番小さなものが選

字集合マッパー (Character Set Mapper,ばれる．なお，処理が遅くなりすぎることCSMapper) である．たとえば，JIS X 0208を防ぐため，多段階の中継は行わず，たか文字集合を Unicode 文字集合に写像しただか一段のピボットコードが使われるようり，その逆を行ったりする．文字集合マッに制限されている．

パーは，入力および出力の CCS の名前かまとめると，ピボットコードは UCS とら対応する写像を見つけるためのデータいう概念を CSI に則した形で拡張したもベースと，実際に変換を行うマッパー部ののであり，逆に，UCS 正規化はこのピボッ二つからなる．マッパー部はプラグインでトコードの特殊な例ともいえる．

追加できるようになっている．

4.4 マッパーの集成

マッパーの集成の自動化を行っている．図

二つのコードセット間で変換を行う場

6 に，EUC-JP を Unicode 系のコードセッ合，両者がそれぞれ含んでいるすべてのト (UTF-8 など) に変換するケースでの，CCS に対応するマッパーを見つけ出す必集成後のブロック図を示す．

要がある．これをマッパーの集成と呼ぶ．

もし，入力側のある一つの CCS に対し

Citrus iconv では，入力側と出力側のて，出力側の複数の CCS にそれぞれ対応コードセットに対応する ESDB がそれぞするマッパーが存在する場合には，出力側れのコードセットが持つ CCS のリストをの ESDB に記述されている CCS の順序保持しているため，このすべての組み合にしたがって順に変換が試みられ，最初にわせについてマッパーを開くことにより，成功した変換が採用される (図 7)．

CSID:
0x0000

CSID/-インデックス
マルチバイト文字

CSID:
0x8080

変換CSID:0x0080

CSID:
0x8000

ASCII
↓
UCS

-

JISX0208
↓UCS

-

CSID/

-インデックス

JISX0201↓UCS-

JISX0212↓UCS-

CSID:0x0000

変換

マルチバイト文字

図 6: EUC-JP → UTF-8 変換時のマッパーの集成

上から順に処理される

-

CSID/

-インデックス
マルチバイト
文字

変換

CSID:
0x0000

-UCS
↓
ASCII
UCS
↓
JISX0208
UCS↓
JISX0201
UCS- ↓? JISX0212

CSID:
0x0000CSID:
0x8080-

CSID/-インデックス
マルチバイト変換
文字CSID:
0x0080CSID:
0x8000

図 7: UTF-8 → EUC-JP 変換時のマッパーの集成

これを見ると，どの項目も GNU libi-

5 実装

conv よりも性能が良くない．特に項目 1

Citrus iconv は，これまで述べたようなが顕著である．これは，変換の初期化に時ものをこれを iconv の API に則した形で間がかかることを意味している．その他組み合わせて実装されている．実装は既にの項目は実際の変換の性能差を示している

NetBSD の公式なソースにマージされてが，おおむね数倍の差となっている．GNU

おり，最新の NetBSD-current を取得す

libiconv がすべてのデータを静的に保持しることにより利用できる．現在利用可能なているなど，拡張性や柔軟性という点でコードセットは，ISO-8859 系や EUC 各劣っているのに対し，Citrus iconv はプラ種などを含む約 130 種類である．現在はグインによる拡張性や，これまでに述べたさまざまなアプリケーションで利用されて各種機構による柔軟性の確保のために速度おり，多くの問題は修正ずみで現在は特にが犠牲になっている．これは純粋に速度と大きな問題は報告されていない．

柔軟性のトレードオフであると言える．

6 ベンチマーク

7 結論

Citrus iconv と GNU libiconv でそれぞれ 4 つのケースでの簡単なベンチマークを行った結果を表 2 に示す．内容はそれぞれ，
1. ISO-2022-JP から UTF-8 への変換を設計目標に挙げた三つの点について検討すると，これまでに述べたような方法でCSI な iconv を作成することが可能であったこと，プラグインなどによる拡張性の確保，そして，ISO C locale の LC CTYPE

単に開いて閉じる

2. ISO-2022-JP の文字列「あいう abc」の500,000 回繰り返しを UTF-8 へ変換
3. 同様に EUC-JP から UTF-8 への変換
4. 同様に Shift JIS から UTF-8 への変換を行うためにかかった時間である．

部などとのコード共有が行われており，当初の目標を達成したことが確認できた．一方で，GNU libiconv と比べて軒並み性能が良くないという点は，今後解決すべき課題であろう．

項目1234GNU libiconv(秒)
0.005
0.734
0.639
0.679

Citrus iconv(秒)
0.191
3.899
2.738
2.692

環境:富士通 FMV-BIBLO LOOX S80C(Transmeta Crusoe TM5800 800MHz)

表 2: ベンチマーク

8 謝辞
Citrus iconv を作る土壌を与えてくださった NetBSD Project の方々や，完成した

Citrus iconv のテストに関係してくれた方々に感謝します．特に Thomas Klaus-ner 氏にはマニュアルページの構成やバグレポートの捕捉などを行っていただきました．また，埜中公博氏の報告が性能改善のヒントとなりました．

参考文献[1] IEEEStd 1003.1-2001 POSIX / Single UNIX Specification Version 3.
http://www.unix-systems.org/[2] Citrus Project.
http://citrus.bsdclub.org/[3] NetBSD Project.
http://www.netbsd.org/[4] GNU libiconv.
http://www.gnu.org/software/libiconv/[5] Unicode Consortium
http://www.unicode.org/[6] The Report of the IAB Character Set Workshop
held 29 February - 1 March,1996
RFC2130.
[7] Character Encoding Model
Unicode Technical Report No.17.
http://www.unicode.org/unicode/reports/tr17