ia64 での Ruby の挙動が……

で、migemo をインストールすべく、bsearch.rb, romkan.rb を home ディレクトリに配置し、migemo の configure && make... と、make test で引っ掛かりました。
どうやら migemo の辞書を作る事ができません。
色々と調べてみると、どうやら、

[ruby-dev:24748] "\000\000\000\001".unpack("N") returns [0]
■ Alpha で unpack がバグる

この辺の問題らしく、pack した offset が unpack で元に戻っていない様です。

この環境の Ruby のバージョンは、

$ ruby --version
ruby 1.8.1 (2003-12-25) [ia64-linux-gnu]

で、明確に上の url の事象と同じかどうかは確認していませんが、

irb(main):020:0> [2, 55, 2**17 + 55, 2**18 + 10].pack("N*").unpack("N*")
=> [8589934592, 236223201280, 563186176622592, 1125942856515584]

と、正しく戻らないのは間違いありません。

make では辞書を作ることができず test にも通らない訳ですが、実際の migemo の処理自体にも影響するので、辞書だけ何とかする訳にも行きません。

当然、ruby を update すれば良いのですが、いつもと違って今回の開発環境は借り物なので、管理グループにお伺いを立て、お願いしなければなりません。
現在の状況ではスムーズな update は望めなさそうなので、ここは script 側で暫定対処してみました。

非常に ad-hoc な hack ですが、

irb(main):019:0> [2, 55, 2**17+55, 2**18 + 10].pack("N*").unpack("n*")
=> [0, 2, 0, 55, 2, 55, 4, 10]

と、short (2バイト) 単位に unpack すれば情報を失なうことはない様なので、これを使って何とかしてみました。
つまり 2バイト単位に unpack して、上位桁部分の桁を合わせた上で合算してやれば元の値を得られるということですね。

ということで、

def make_unpack_corrector(constant)
  x = nil
  result = nil
  c = constant
  return Proc.new { |elem|
    if x then result = x + elem; x = nil; result else x = elem * c; nil end }
end

なんてクロージャもどきを返すメソッドを用意し、

corrector = make_unpack_corrector(2**16)
@index = File.new(filename + ".idx").read.unpack("n*").map { |e|
  corrector.call(e) }.select { |e| e }

という具合に unpack("n*") した配列要素に map でクロージャもどきを適用した上で、select で値が存在する要素だけに絞り込みます。
何だか回りくどい方法になってしまいましたが、Ruby 的にはもっと上手い方法があるんじゃないでしょうか?

という訳で、手元の migemo に、

$ diff -u migemo-dict.rb.org migemo-dict.rb
--- migemo-dict.rb.org	2002-10-22 14:38:14.000000000 +0900
+++ migemo-dict.rb	2006-04-10 14:34:20.000000000 +0900
@@ -13,6 +13,14 @@
 
 require 'bsearch'
 
+def make_unpack_corrector(constant)
+  x = nil
+  result = nil
+  c = constant
+  return Proc.new { |elem|
+    if x then result = x + elem; x = nil; result else x = elem * c; nil end }
+end
+
 class String
   def prefix_match (string)
     self[0, string.length] <=> string
@@ -56,7 +64,9 @@
 class MigemoStaticDict < MigemoDict
   def initialize (filename)
     super(filename)
-    @index = File.new(filename + ".idx").read.unpack "N*"
+    corrector = make_unpack_corrector(2**16)
+    @index = File.new(filename + ".idx").read.unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e }
   end
 
   private
@@ -106,14 +116,23 @@
 class MigemoDictCache
   def initialize (filename)
     @dict  = File.new(filename)
-    @index = File.new(filename + ".idx").read.unpack "N*"
+    corrector = make_unpack_corrector(2**16)
+    @index = File.new(filename + ".idx").read.unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e }
   end
 
   def decompose (idx)
     @dict.seek(idx)
-    keylen = @dict.read(4).unpack("N").first
+
+    corrector = make_unpack_corrector(2**16)
+    keylen = @dict.read(4).unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e}.first
+
     key = @dict.read(keylen).unpack("a*").first
-    datalen  = @dict.read(4).unpack("N").first
+
+    datalen  = @dict.read(4).unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e }.first
+
     data     = Marshal.load(@dict.read(datalen))
     return key, data
   end

ってな感じの修正をして何とか使える様になりました。

decompose() 内の、

keylen = @dict.read(4).unpack("N").first

などは、read で読むバイト数も決め打ちの処理なので、

keylen = @dict.read(4).unpack("n*").last

とかでも全然問題無い (値が short の範囲に収まる限り) んですが、折角、クロージャもどきを作ったので適用しています。

それにつけても、map して select ってのは美しくないですね。