Object.__id__ってタダのFixnumみたいだろ.ポインタなんだぜ.それ.

RubyのObjectのIDは生のポインタが見えているかもという話.
例えば

("a".__id__) << 1

とすると求められた値は実はヒープに格納されたオブジェクトのポインタだったりする.
前提知識:第2章 オブジェクト
 

Object.__id__の実装

VALUE
rb_obj_id(VALUE obj)
{
    if (TYPE(obj) == T_SYMBOL) {
        return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
    }
    if (SPECIAL_CONST_P(obj)) {
        return LONG2NUM((SIGNED_VALUE)obj);
    }
    return (VALUE)((SIGNED_VALUE)obj|FIXNUM_FLAG);
}

それぞれのVALUEの値はFixnumに変換.つまり奇数にするわけだが,種類によって変換方法は様々.

即値(Fixnum, true, false, nilなど)の場合
  1. 右に1bitシフトして1をORする(注:Fixnumは必ず奇数になっている).

例:"0b101101" -> "0b1011011"

Symbolの場合
  1. Symbolの値を8bit右シフト.
  2. RVALUE(オブジェクト)のサイズを乗算.
    1. RVALUEの倍数かつ4の倍数なので下位2bitは00になるはずである.
  3. (4 << 2)を足す.

変換表

VALUE
symbol  ssssssssssssssssssssssss00001110

ID
symbol   000SSSSSSSSSSSSSSSSSSSSSSSSSSS0
        S...S % A = 4 (S...S = s...s * A + 4)
        A = sizeof(RVALUE)/4

sizeof(RVALUE) is
20 if 32-bit, double is 4-byte aligned
24 if 32-bit, double is 8-byte aligned
40 if 64-bit

つまりRVALUEのサイズで除算した値を2bit右シフトすると4にしたいみたい.
何で4なんだろうか..?
有識者の方がずばり回答してくれると期待..

RVALUE(オブジェクト)を指すポインタの場合
  1. 下位2bitは必ず00のはずなのでFixnumを表す01をORする.

例:"0b111...100" -> "0b111...101"
 

ObjectSpace._id2ref

IDからポインタというかVALUEに直す時の話.

即値の場合

下位1bitは必ず 1 になっているはずなので,1bit右シフトする.
そこでtrue,false,nilなど判断可能.
Fixnumの場合は,下位2bitが必ず 11 である
1bit右シフト後に奇数であればそれはFixnumと判断できる.

    ptr = NUM2PTR(objid);
    p0 = (void *)ptr;

    if (ptr == Qtrue) return Qtrue;
    if (ptr == Qfalse) return Qfalse;
    if (ptr == Qnil) return Qnil;
    if (FIXNUM_P(ptr)) return (VALUE)ptr;
Symbolの場合

下位1bitを0にした後,RVALUEサイズで剰余算すると(4 << 2)だけ余りが出るはず.
これで判断できる.

    ptr = objid ^ FIXNUM_FLAG;	/* unset FIXNUM_FLAG */

    /* 判断 */
    if ((ptr % sizeof(RVALUE)) == (4 << 2)) {
        /* 本当のIDを求める */
        ID symid = ptr / sizeof(RVALUE);
        if (rb_id2name(symid) == 0)
	    rb_raise(rb_eRangeError, "%p is not symbol id value", p0);
        /* IDからSymbolに変換 */
	return ID2SYM(symid);
    }
RVALUE(オブジェクト)を指すポインタ

下位1bitを0にしたらいい.