Not only is the Internet dead, it's starting to smell really bad.:2020年08月分

2020/08/16(Sun)

[オレオレN6] rewrite HZ/HZ8 support for Citrus iconv

介護やら葬儀やらひと段落したので、とりあえずリハビリがてらここ数年中断してた作業を再開してくかなと。

表題の件、どうにもコードが複雑でウンザリしとったのでスクラッチで 書き直した、つーのも最初に書いたときはHZって

という2つのHZをISO2022的に拡張するプロポーザルが存在したので、それを意識し拡張可能なように書いとったんだけど、当然のようにISO2022みたいにクソめんどいコードになってしまいあまり触れたくないコードになっとったのだ *1

そもそもCitrusではISO2022のサブセットであるEUCも別モジュールになってるわけで、HZ/HZ8も性能を考えて独立したコードにし、HZ+/EHZのサポートは別モジュールを用意しようかなと。 まぁHZ+/EHZなんて実際に使われてるの見たことないんですけどね…EHZがちょっとめんどくさいんですわBIG5の文字を無理矢理0x2121~0x7d7eに詰め込んでたりしててな。

なおencoding moduleの初期化パラメータであるVARIABLEに旧モジュールと互換性が無いので後方互換に問題あるけど、後述するけどいろいろ書き直していろんなとこのバージョン変更する予定だから放置。

ちなみにHZ8というのはHZの仕様書には載ってないし他にサポートするプログラムも見たことないんだけど、実際に China News Digestというサイトの*.hz8.htmlという古いドキュメントで使われてるので実装してある、最新記事はGB2312やUTF-8だけど過去記事(1989年の例のアレとか)は変換せずそのまんまみたいで歴史は残さないとね(ニッコリ)。

それと Issue #139のバグがあった *2のだけど、これも解消したはずだ。

なお数年前からCitrus iconvの内部APIを変更する作業をしてて、それに沿ったコードになってるので本家TNFにマージするにはけっこう作業が必要なはずなので以下略、まあ誰も興味ないだろうが。

この内部APIの変更ってのは Issue #120にチケット切ってあるけど、GNU libiconvと比較して遅せえよゴルァ問題を解決するための準備 *3で、かなり前から準備を進めてたのだがここ数年まともにコード書ける人生でなかったので未だ未完(馬に乗馬)なのである。

これについては大昔にCitrus iconvのどこがボトルネックなのかを調査した時にある程度の結論は出てて、iconvの内部でmbrtowc/wcrtombとほぼ同じAPIが動くというデザインそのものが悪かったという話。 というのも

という挙動の違いがあるのだけど

という問題にハマってるようなのね、なので

というデザイン変更を目下作業再開したという感じ、まあencode/decodeも実際プロトタイプとしてはこれまでのmbrtowc_priv/wcrtomb_privとたいして変わらんのだけど、restart止めたことで実装はだいぶ違う。 というか4.4BSD Rune由来である這いずり回る混乱ことsputrune/sgetruneのitojun ISO2022拡張と近い処理になり、一周回ってここかよ感はある *4

まあ残り作業はVIQRを新APIで書き直し *5、iconv_stdモジュールでそっち使うよう切り替えるだけなのでワイの生きてる間に終わる可能性が出てきたかもしれないが、明日の事など判らない時代なので以下略。

*1:まだCなんぞロクに書いたことない若造やったしなー。
*2:これGNU libiconvも同じバグあるんよな…多分それで俺も仕様を勘違いしたんかのう、ちなみにperl iconvは問題なし。
*3:チケットにはどうせUTF-8<->UTF-32だけ専用モジュールを用意して速くすれば文句言ってる人の8割は黙るだろうというkludge patch貼ったけどあまり現時点では採用したくないかな。
*4:その結果あの複雑なISO2022モジュールの書き直しの手間がほとんどなかったのは亀のご加護ですかね。
*5:とかいいながらまったく仕様思い出せねえんだよなぁ…調べるにもどこにドキュメントあるんだっけ状態。

2020/08/19(Wed)

[オレオレN6] refactoring Citrus iconv

前回Citrus iconvの内部ではmbrtowc/wcrtomb相当の処理が動いてて、特にmbrtowcの処理はiconvの内部で使うには無駄が多く性能問題になってるという話を書いた。 なのでdecode/encodeという処理に置き換える予定なのだけど、mbrtowc->decodeはかなり処理が変わったのだがwcrtomb→encodeはほとんど変化が無い。

これが

static int
_citrus_XXX_wcrtomb_priv(_XXXEncodingInfo * __restrict ei,
    char * __restrict s, size_t n, wchar_t wc,
    _XXXState * __restrict psenc, size_t * __restrict nresult)

こう

static int
_citrus_XXX_encode(_XXXEncodingInfo * __restrict ei,
    char * __restrict s, size_t n, wchar_t wc,
    _XXXState * __restrict psenc, size_t * __restrict nresult)

なので実質おんなじものなのよね、まぁwcrtombの場合mbrtowcと違ってmbstate_tに変換途中のバイト列を持つ必要が無いので変える意味が無いからだったんだけど。

しかしここでふと太古の昔よりの既知問題が記憶の底からフラッシュバック、zWやUTF-6に関してwcrtomb相当のインタフェースではまともに扱うことができないってのがあるのを失念していた。 ちなみに過去記事リンク

これまでワイド文字はそれ単体で必ずマルチバイト文字に変換できるという前提で実装されてたんだけど、後続のワイド文字(UTF-6なら入力の最後まで、zWなら完全にASCIIのみの行と判断可能な改行コードが見つかるまで)encodeは後続のワイド文字を要求し続けるというクッソめんどい処理になる。

この問題を解決するために例えばencodeを

static int
_citrus_XXX_encode(_XXXEncodingInfo * __restrict ei,
    char * __restrict s, size_t n, wchar_t * __restrict ws,
    _XXXState * __restrict psenc, size_t * __restrict nresult)

とでも改めたところで、iconvは毎回入力バッファ全てをワイド文字表現に変換しつつ変換可能と判断できるまではEINVALを返し続け、そしていざ出力バッファへ書き出すのにもサイズ足りなきゃE2BIGを返し続けるという、あらゆる意味で効率の悪いコードになる、やっぱiconvクソだわ…

これ以外にもCitrus iconvには入力 vs 出力が1:NあるいはM:N変換になるケース(例えば「ぱ」→「は゜」)を想定していないという問題もあり、こいつの解決はもっと大掛かりな内部APIの変更が必要なのも思い出して頭痛がしてきた、これについてはまたいずれ記事に書く。 ぱっと思いつくだけでも1:N変換においてN側のそれぞれの文字が異なる文字集合に属するようなケースでは今のesdb/csmapperという符号化手法/文字集合のデータベースでは対応しきれないのだよな。

まぁどっちも今回の変更では考えないようにしようか、あくまで今回の目標は性能問題解決がメインだし…

2020/08/20(Thu)

[オレオレN6] rewrite VIQR support for Citrus iconv

表題の作業にとりかかろうと思ったのだが、かつて集めた資料はファイルサーバのどこにあるか探す気力もなく、インターネットで検索しようにもかつて読んだはずの記事がでてこず、 RFC1456以上の情報が無く困った。

このVIQRってのはベトナムの文字集合VISCIIを7bitで表現するため、ダイアクリティカルマークを RFC1345風(実際には全く違うけど)のニーモニックで表現するという、いわゆるISO646による換字だ。一例を挙げると

ちゅーかんじ、普段ダイアクリティカルマークとか使わない言語属にとっては 全部同じじゃないですかと以下略。

この手の換字法では例えばĂではなく文字通りA(を表現したい場合にエスケープ文字が無いと困るはずなのだが、RFC1456にはそれについての記述が無いのよね、あれをそのまま実装したと思われる SKF(Simple Kanji Filter)では

$ echo A( | skf --ic=utf-8 --oc=viqr
A(

と両者の区別がつかない変換をしてくれよる、入力メソッドとしてのVIQRならモード切換すればいいんだろうけど、文字コードとして使うにはこれでは困るのよな、まぁでも仕様通りか…

この問題について自分は Vietnamese Quoted-Readable(WikiPedia)のページからもリンクされてるベトナム語変換サービスをあれこれした結果、A\(のようにダイアクリティカルマークを表すニーモニックの前に0x5c(なぜか円マークに見える)を入れてエスケープするのが通例と判断し今の実装を書いたんだが、じゃあ実際に使用例としてベトナム語圏のニュースグループでこの通り使われてるかを確認したかどうか記憶が無いんだよなぁ、今となってはその手のアーカイブサイトは消え去ってるしウェーイwwwバックマッスィーンに食わせるURLもファイルサーバーに埋もれてるからな。

あと変換ロジックについて、Citrus iconvの実装は 家庭教師のTrie *1作って完全一致探すというめんどくせえ実装なのだけど、SKFの方はベトナム語の声母・韻母 + 声調記号という構造を使ってコンパクトなテーブル使った変換ロジックになってる。

性能面を考えるとニーモニックは極めて短く(最大で3バイト)かつエントリ数も極めて少ないので、辞書でもあるまいしTrieで処理するのははなはだ無駄な気もするんだが、なんでこういう実装にしたのかというとRFC1456には存在しない拡張が結構存在しVISCIIの文字集合にない文字もニーモニックで表現してるのをどこかで見た記憶があるのよね、そのために将来的にいかようにでも拡張可能なようした *2という当時の私ってマジでパラノイア。

だけど今更この手の拡張の使用状況を調べようもない時代(人類はUnicode絵文字によって象形文字の時代に戻った)なので、効率重視でSKFみたいな変換ロジックにした方がいいのかもね。

*1:つってもパトリシア木とかDouble ArrayとかLOUDSとか使わないしょーもないやつだからコード読む意味はないぞ
*2:ちなみにこの拡張を意識した部分のコードをFreeBSDの誰かが出鱈目な修正をしてセキュリティアドバイザリを出してるのは 過去に書いた通り。

2020/08/22(Sat)

[オレオレN6] rewrite VIQR support for Citrus iconv (#2)

ふむ、このドキュメントに0x5cによるエスケープは載ってるな。

つーかそこよりも驚いたのは、VIQRは完全版だと

というescape sequenceが存在するstateful encodingなのですわ、などと書かれてる部分で

なんだ
これは!?
オレは
こんなの
はじめてみるぜ

と奥羽戦士みたいな声が出てしまった、翌年出されたRFC1456からはこの仕様ドロップされてるようだけどね…