## ## RACE library for Ruby ## (draft-ietf-idn-race-02.txt) ## Nov 9, 2000 by yoshidam ## require 'uconv' module Uconv BASE32TRANS = "abcdefghijklmnopqrstuvwxyz234567" ## Base32 encodeer def self._encode32(str) ret = "" rest = 5 cc = 0 str.each_byte do |c| if rest > 3 cc |= c >> (8 - rest) ret << BASE32TRANS[cc] rest -= 3 cc = (c << rest) & 31 else cc |= c >> (8 - rest) ret << BASE32TRANS[cc] cc = (c >> (3 - rest)) & 31 ret << BASE32TRANS[cc] rest += 2 cc = (c << rest) & 31 end end if ret.length < (str.length * 8 + 4) /5 ret << BASE32TRANS[cc] end ret end ## Base32 decoder def self._decode32(str) rest = 8 cc = 0 ret = "" str.each_byte do |c| b = BASE32TRANS.index(c) if rest > 5 rest -= 5 cc |= b << rest else cc |= b >> (5 - rest) ret << cc.chr rest += 3 cc = (b << rest) & 0xff end end ret end def self._compress(str) uary = str.unpack("U*") u1 = nil ## scan upper octet uary.each do |u| raise "not supported character" if u > 0xffff upper = u >> 8 if u1.nil? || u1 == 0 u1 = upper elsif upper != u1 && upper != 0 u1 = nil break end end ret = nil if u1.nil? ret = 0xd8.chr uary.each do |u| ret << [u].pack("n") end else ret = u1.chr uary.each do |u| u2 = u >> 8 n1 = u & 0xff if u2 == u1 if n1 != 0xff ret << n1.chr else ret << "\xff\x99" end else ret << "\xff" + n1.chr end end end ret end def self._decompress(str) ret = [] u1 = str[0] len = str.length - 1 if u1 == 0xd8 i = 1 while len > 0 ret << ((str[i] << 8) | str[i + 1]) i += 2 len -= 2 end else i = 1 len = str.length - 1 while len > 0 n1 = str[i] if n1 != 0xff ret << ((u1 << 8) | n1) else raise "invalid race" if len == 1 i += 1 len -= 1 n1 = str[i] if n1 == 0x99 ret << ((u1 << 8) | 0xff) else ret << n1 end end i += 1 len -= 1 end end ret.pack("U*") end def self.u8torace(str) str.gsub(/[0-9a-zA-Z\x80-\xff-]+/n) do |m| if m =~ /[\x80-\xff]/n "bq--" + _encode32(_compress(m)) else m end end end def self.racetou8(str) str.gsub(/bq--([0-9a-zA-Z-]+)/i) do |m| s = $1.downcase raise "invalid race" if s !~ /^[a-z2-7]+$/ _decompress(_decode32(s)) end end end