らんだむな記憶

blogというものを体験してみようか!的なー

CRuby (4)

CRuby (3) - らんだむな記憶 の続き。

もう少し文字列にフォーカスしたい。https://github.com/ruby/ruby/blob/v3_0_1/string.c#L230-L258エンコーディングを取得する関数がある。238 行目で https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L149-L175、つまり

static inline char *RSTRING_PTR(VALUE str)
{
    char *ptr = rbimpl_rstring_getmem(str).as.heap.ptr;
    return ptr;
}

を通していることからも分かるように、VALUE str の正体は struct RString である。encidxENCINDEX_UTF_16 或いは ENCINDEX_UTF_32 の時には、BOM を見てエンディアンを判定している。それ以外では基本的に rb_enc_from_index(encidx); で求まる。これは https://github.com/ruby/ruby/blob/v3_0_1/encoding.c#L413-L427 から、インデックス値によって簡単に求められているものである。

ここまで来ると、文字列のエンコーディングの本質は https://github.com/ruby/ruby/blob/v3_0_1/string.c#L263ENCODING_GET を通した瞬間に既に分かっていたことになる。https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/encoding.h#L51 から https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/encoding.h#L41-L44 を辿ると、struct RString.basic.flagsrb_enc_get_index によって求まることが分かる。後者の関数は https://github.com/ruby/ruby/blob/v3_0_1/encoding.c#L976-L1010 で定義されている。

・・・しかしこの辺は日本語の文字列リテラルを作って String#encoding を叩いても通ってない感じがする。
https://github.com/ruby/ruby/blob/v3_0_1/string.c#L11535-L11741 がメソッド類の定義のようなので、ここから潜ろうか。
https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/globals.h#L46rb_cObject を “継承” して String クラスに対応する rb_cString ができていることが https://github.com/ruby/ruby/blob/v3_0_1/string.c#L11538

rb_cString  = rb_define_class("String", rb_cObject);

から分かる。下を読んでいくと、https://github.com/ruby/ruby/blob/v3_0_1/string.c#L11681 から、https://github.com/ruby/ruby/blob/v3_0_1/encoding.c#L1201-L1209String#encoding の実装であることがわかる。

例えば

puts "ほげ".encoding

を食わせた場合、

(gdb) b encoding.c:1204

すると確かにここでブレークする。そのまま辿ると、rb_io_puts に入ってくる。String#encodingString#encoding (Ruby 3.0.0 リファレンスマニュアル) より、Encoding オブジェクトを返すので、CRuby (3) - らんだむな記憶 の場合のように文字列が直接渡ってきているわけではない。https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7901String オブジェクトとして解釈されることになる。なので、この line に対して中身を確認すると、

(gdb) p rbimpl_rstring_getmem(line)
$9 = {basic = {flags = 1619011589, klass = 94546576959560}, as = {heap = {len = 5, ptr = 0x55fd571db6d0
"UTF-8", aux = {capa = 5, shared = 5}}, ary = "\005\000\000\000\000\000\000\000\320\266\035W\375U\000\00
0\005\000\000\000\000\000\000"}}

となって、UTF-8エンコーディングされているという情報が入っていることが分かる。