読者です 読者をやめる 読者になる 読者になる

Ruby 1.9 における replica と alias の違い

端的にいえば以下の通り。

replica
「encoding」を定義する
alias
「encoding *名*」を定義する

図にすると以下のような感じ。

                                鬼車(CES)
 OnigEncodingSJIS
   │←────────────────┐
───┼─────────────────┼──────────────
   │                 │   Encoding (文字集合+CES)
   │                 │
 #<Encoding:Shift_JIS> ─(replica)→ #<Encoding:Windows-31J>
   │     │           │     │
───┼─────┼───────────┼─────┼────────
   │    (alias)          │    (alias)  Encoding名
   │     ↓           │     ↓   (charset名)
  "Shift_JIS" "SJIS"       "Windows-31J" "CP932"

(鬼車層とEncoding層の区別は正確ではないのですが便宜上こうしておきます。実際には Encoding 層 での # の実体 rb_encoding* は同じもの)
 鬼車層では文字単位の操作を行っています。具体的には str[n] が何を返すか、str.length はどのくらいか。また String#valid_encoding? の true/false 判定もこのレベルです。鬼車層の実体は、文字の最小バイト数と最大バイト数の指定や、文字列の長さを算出する関数、大文字を小文字に変換する関数等をひとまとめにした構造体 OnigEncodingType で、具体的には ONIG_ENCODING_ASCII やONIG_ENCODING_SJIS 等があります。
 Encoding 層では文字列操作全般を司ります。具体的には文字列の結合可能・比較可能、正規表現のマッチ可能判定がこの層で行われています。この層での実体は rb_encoding です。Encoding 層における実体は 鬼車が native に対応している encoding から直結している original encoding、original encoding からreplicate された replica encoding、鬼車が対応していない encoding に便宜的に与えられる dummy encoding の3種類に分けることができます。これらを見分けるには Encoding#base_encoding や Encoding#dummy? を見るわけですが、見たからどうということでもありません。
 これらの区別を超越する存在が 7bit のみで構成された文字列で、String#ascii_only? で判定可能です。これが真になる文字列はいかなるencoding の文字列とも結合・比較可能となります。
 なお、rb_encoding は OnigEncodingType の同じもののため、Encoding 層でのreplica でも、鬼車レベルの諸関数、例えば文字群に Unicode Property Nameを与えたり、valid なバイト列の範囲を定義し直すことが可能です。([ruby-dev:32947] での「レプリカはコピーをした後で関数のいくつかを上書きすることも可能」とはたぶんこのこと)
 Encoding名層は、Encoding.find() するためのテーブルです。実際の構造は以下のようになっていると考えればいいでしょう。alias で追加された名前は、文字列から rb_encoding を検索する際にのみに用いられます。

ENC_TABLE = {
  'Shift_JIS' => Encoding::Shift_JIS,
  'Windows-31J' => Encoding::Windows_31J,
  'EUC-JP' => Encoding::EUC_JP
}
ENC_TABLE_ALIAS = {
  'SJIS' => Encoding::Shift_JIS,
  'CP932' => Encoding::Windows_31J,
  'EUCJP' => Encoding::EUC_JP
}
def rb_enc_alias(alias, orig)
  ENC_TABLE_ALIAS[alias] = orig
end
def Encoding.find(name)
  return ENC_TABLE[name] if ENC_TABLE[encname]
  return ENC_TABLE_ALIAS[name] if ENC_TABLE_ALIAS[encname]
  return -1
end