RUBY VALUE

Publié le par Séverine

Je cherchais des informations sur l'identifiant des objects ruby quand je suis tombé sur cet article de Calleb Tennis. En voici un petit résumé.

 

L'interpréteur Ruby et certaines librairies core sont écrite en C.

VALUE est la représentation interne des objets RUBY.

au sens général, VALUE est juste un pointeur C vert un type de donnée objet Ruby.

Nous utilisons VALUEs dans le code C comme nous utiliserions les objets dans le code Ruby.

some_function(VALUE arg_object)
{
    some_method(arg_object)
}

 

 

On pourrait s'attendre à ce que VALUE soit juste un typedef vers un pointer C et qu'il y ait une table de lookup des objets qu'il représente et ce serait partiellement correcte. Mais il y a quelques subtilités supplémentaires.

 

Au lieu d'implémenter VALUE comme un pointeur, Ruby l'implémente comme un unsigned long.

Il se trouve justement que sizeof(void*) == sizeof(long).

Après tout, qu'est-ce qu'un pointeur ? C'est juste un entier n=byte qui représente une adresse mémoire.

Mais grâce à ça, Ruby peut se permettre certaines choses.

 

Tout d'abord, dans un objectif de performance, Ruby n'utilise pas VALUE comme un pointeur pour chaque instance. Pour Fixnums, Ruby stocke le valeur du nombre directement dans VALUE lui-même. Cela permet d'éviter de stocker un table de correspondance de chaque Fixnum possible du système.

 

L'astuce réside dans le fait que les pointeur sont positionnés par groupe de 4 d'octets (8 octets sur les systèmes 64 bits).

Par exemple, s'il y a un objet stocké à l'adresse 0x0000F000, alors, le suivant sera 0x000F004. Ce saut de 00 à 04 dans le plus petit quartet est important.

Étendu aux bits, cela donne :  00000000 et 0000100. Cela signifie que si nous utilisant VALUE comme un pointeur, les deux plus petits bits seront toujours 0.

 

Ruby utilise ceci à son avantage. Il met 1 au bit le plus bas et ensuite utilise le reste de l'espace (31 bits) pour fixer un Fixnum. Un des bits est utilisé pour le signe, donc, il reste 30 bits au Fixnum de Ruby.

irb(main):021:0> (2 ** 30).class
=> Bignum
irb(main):022:0> (2 ** 30 - 1).class
=> Fixnum
irb(main):024:0> (-(2 ** 30)).class
=> Fixnum
irb(main):025:0> (-(2 ** 30)-1).class
=> Bignum

Ruby utilise donc le premier bit pour distinguer le fixnum des autres type tel que false, true et nil. Les symboles et leurs IDs sont aussi stockés avec leur bits donc, ruby les reconnaît comme une instance spéciale et les interprète en conséquent.

 

Pour tout le reste, VALUE est une bonne vieille adresse mémoire qui pointe vers la structure en mémoire.

VALUE & 1 => Fixnum sur 31 bits

...000001 => integer 0 soit un id de 1
...000011 => integer 1 soit un id de 3
...000101 => integer 2 soit un id de 5
...000111 => integer 3 soit un id de 7
...001001 => integer 4 soit un id de 9

...000000 => false soit un id de 0
...000010 => true soit un id de 2
...000100 => nil soit un id de 4
...000110 => undefined soit un id 6
(VALUE & 0xff) == 0xe => symbol
sinon pointeur normale

Les id des objets et les VALUE sont très similaires

L'object_id est calculé comme ceci :

VALUE
rb_obj_id(VALUE obj)
{
    if (SPECIAL_CONST_P(obj)) {
        return LONG2NUM((long)obj);
    }
    return (VALUE)((long)obj|FIXNUM_FLAG);
}

en traçant la macro SPECIAL_CONST_P, on pointe vers d'autre macros qui pointent vers d'autres macros, etc...

Ceci ce termine par en général que la VALUE et l'object_id sont les mêmes.

irb(main):001:0> "some_string".object_id
=> 1136556
irb(main):002:0> nil.object_id
=> 4
irb(main):003:0> false.object_id
=> 0
irb(main):004:0> true.object_id
=> 2
irb(main):005:0> 5.object_id
=> 11

 

Notre objet string est un objet classique, son id est bien un pointeur dont l'adresse ramené en bit, se termine par deux zéros. 

Publié dans Ruby - Rails

Pour être informé des derniers articles, inscrivez vous :
Commenter cet article