non vorrei lavorare

昔はおもにプログラミングやガジェット系、今は?

libiconvをemscriptenでjs化し、node.jsで使った

今日は、このとろころ、暑いくらいだったり、寒かったりとの気温の変化に息子達や自分もついていけずに、長男の通院や、次男の通院で午前休を消費している@kjunichiです。
 

背景

WindowsでCビルド環境が入っていない環境下でnode.jsでSMTPを受けるサーバーを書いていた。

世界で活躍するエンジニアと違い、自分は日本語を含むメールしか扱わないため、iso-2022-jpエンコードされている文字コードUTF-8にしてコンソールに表示する必要があった。

node.js向けのiconvパッケージがいくつか見るかるも、パフォーマンス重視しているようで、libiconvのCでのバインドだった。この為、使えなかった。

sjisをutf-8に変換するjsピュア実装はあるも、JISをUTF-8にする実装は見つけることが出来なかった。

Emscriptenでやってみるか!

別環境でlibiconvをemscriptenでビルド

テスト用のSMPTサーバーを動かす環境はビルドツールの類がないので、cloud9のIDEのターミナル越しに某所のOSXマシンにsshして作業を進めた。

emconfigure時に--enable-staticを付けるだけで行けた。ちなみに、静的ライブラリにしておかないと、後の作業でつくるラッパーをemccでリンク出来ない。

emconfigure ./configure --enable-static
emmake make

うまくいけば、./lib/.lib/libiconv.aが作られる。

参考Link

jsから呼びやすい関数にしておく

nodeでrequire('libiconv.a')なんてできれば便利なのだが、それは出来ないようで、ラッパーを作る必要がある。

今回はテストで使う一時的なものなので、mallocしてメモリーリーク問題(jsで使うから関係ないかもw)を避けるべく、4096バイトの上限を設けて処理することにした。

use with Gist Search

ちょっとしたポイント

OSXでは/usr/includeにiconv.hがあるので、

#include <iconv.h>

ではなく、

#include "iconv.h"

としてローカルのヘッダを参照するようなコードにしておき、以下の作業を進める。

以下のコマンドで、libiconv.aを含めたjsファイルを生成する。

emcc -s EXPORTED_FUNCTIONS="['_jis2utf8']"  -Qunused-arguments -Wno-logical-op-parentheses -I./include -o jis2utf8lib.js jis2utf8lib.c ./lib/.libs/libiconv.a

使うには

Cの関数をnode.jsで呼ぶには

libiconvはあっけないほどemscriptenでjs化出来たのだが、このjsファイルをnode.jsでrequireして利用することが、分からなかったので、以下の力技的な方法を使った

var fs = require('fs');
eval(fs.readFileSync('./jis2utf8lib.js'));

jsでchar*を渡すには

呼び出し先で参照のみの文字列を渡す場合

Module.cwrapで'string'を指定すれば、文字列を渡すこが出来る。

呼び出し先に編集して貰うchar*を渡すには

Module.cwrapで'number'を指定し、Module._mallocで必要な領域を確保して渡せば、js化したC言語の関数内で処理した結果を受け取ることが可能。

var jis2utf8 = Module.cwrap('jis2utf8', 'number', ['string','number']);
var ptr = Module._malloc(length);
jis2utf8(jisBuf.toString('ascii'), ptr);
var view = Module.HEAPU8.subarray(ptr, ptr+length);
var str = Uint8ToString(view);

function Uint8ToString(u8a){
  var buf=new Buffer(u8a.length);
  for(var i = 0; i < u8a.length&&u8a[i]!=0; i++) {
    buf.writeUInt8(u8a[i],i);
  }
  var nBuf = new Buffer(i);
  buf.copy(nBuf,0,0,i);
  return nBuf.toString('utf8');
}

まとめ

この記事書き終わろうとしている頃、sjisutf-8に変換するjsピュア実装のリンクを貼ろうとググったらjconvがあっさり見つかった。

Link

関連記事

 

8年前の記事

2年後の記事