WSHのJScriptによるバイナリI/O

一昨日ハマってた問題は大したことなく解決できた.

上のソースをforループでなく最小完全ハッシュを使って書き直したもの.
手順:

  1. Minimal Perfect Hashing からソースコードを取ってくる(アーカイブになってないので面倒.wget再帰getでも欲しいソースだけをうまく取ることができない.w3mのaキー*1が一番便利だと思う)
  2. make -f makeperf.txt perfect と打ってコンパイル
  3. isla-plata.org Wiki > ADODB.Streamによるファイルのバイナリアクセス(調査編) を参考に,変換のかかってしまった結果のバイト列をテキストファイルに行区切りで書き込む.例:
    20ac
    81
    201a
    ...
    178
  4. perfect -mh < iso_8859_1.txt と打って最小完全ハッシュ関数を生成(phash.c) ここでキーは16進整数だと教えている (-h) .有名なGNU gperfではキーに文字列しか使えない.
  5. phash.cをJavaScriptに移植(変数の型とかは一切考えなくてよかった)
  6. make -f makeptst.txt と打ってサンプルドライバ (foo) をコンパイル
  7. このサンプルドライバfooは入力の順番を保存しないのでこのままでは役立たない.iso_8859_1.txtの各行を毎回fooを起動し直して食わせるドライバをRubyで実装(ソースコードは最後に載せる)
  8. 実行してハッシュ値でソートし,変換前のバイトを取り出す: ./driver.rb|sort|sed 's/^ *//'|cut -d ' ' -f 2
  9. 結果をJavaScriptの配列invPhashに移植.最終的なJavaScriptソースコードは以下の通り.私の改変の本質はtab, phash, invPhash, uniso8859_1の4つ.
////////////////////////////////////
//write file
//指定のファイルに、0~255の値を書き込む
////////////////////////////////////

var str = WScript.CreateObject("adodb.stream");

str.type = 2;
str.charset = "iso-8859-1";
str.open();

for(var i = 0; i < 0x100; i++){
str.writeText(String.fromCharCode(i));
}
str.saveToFile("adodbTestOut.dat", 2);

str.close();

str = null;

////////////////////////////////////
//read file
//ファイルを読み込み、16進ダンプする
////////////////////////////////////

str = WScript.CreateObject("adodb.stream");

str.type = 2;
str.charset = "iso-8859-1";
str.open();

//適当なファイルを指定すること
str.loadFromFile("adodbTestOut.dat");

WScript.Echo("file size : " + str.size);

var idx = 0;
while(!str.EOS){
    var dat = uniso8859_1(str.readText(1).charCodeAt(0));
    WScript.stdout.write(addZero(dat.toString(16)));
    WScript.stdout.write(" ");
    idx++;
    if( idx % 16 == 0) (WScript.stdOut.Write("\n"));
}

str.close();
str = null;

//0パディング用
function addZero(val){
    return (val.length == 1) ? "0" + val : val;
}

var tab = new Array(
13,3,24,31,0,21,15,0,13,0,20,9,7,21,21,18
);

/* The hash function */
function phash(n) {
  var a, b, rsl;
  n += 0xcc693560;

  n += (n << 8);
  n = n ^ (n >>> 4);
  b = (n >>> 7) & 0x0f;
  a = (n + (n << 25)) >>> 27;
  rsl = (a^tab[b]);
  return rsl;
}

var invPhash = new Array(
16, 7, 30, 22, 29, 28, 3, 27,
20, 24, 2, 14, 19, 17, 10, 11,
21, 5, 12, 31, 13, 26, 23, 8,
1, 9, 15, 18, 0, 25, 4, 6
);

function uniso8859_1(code) {
    var shifted = code >>> 8;
    if (shifted != 0) {
        return invPhash[phash(code)] + 0x80;
    }
    else {
        return code;
    }
}

Rubyで書いたドライバ:

#! /usr/bin/ruby
File.open("iso_8859_1.txt") {|f|
    idx = 0 # 変換前のバイトに対応(0x80だけずれている)
    f.each {|l|
        IO.popen("./foo -h", "r+") {|io|
            io.puts l
            io.close_write
            io.readline # 読み飛ばす
            hv, code = *io.read.split(' ')
            printf("%2d %d\n", hv, idx)
            idx += 1
        }
    }
}