Ruby  2.0.0p247(2013-06-27revision41674)
struct.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   struct.c -
00004 
00005   $Author: zzak $
00006   created at: Tue Mar 22 18:44:30 JST 1995
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009 
00010 **********************************************************************/
00011 
00012 #include "ruby/ruby.h"
00013 #include "internal.h"
00014 
00015 VALUE rb_cStruct;
00016 static ID id_members;
00017 
00018 static VALUE struct_alloc(VALUE);
00019 
00020 static inline VALUE
00021 struct_ivar_get(VALUE c, ID id)
00022 {
00023     for (;;) {
00024         if (rb_ivar_defined(c, id))
00025             return rb_ivar_get(c, id);
00026         c = RCLASS_SUPER(c);
00027         if (c == 0 || c == rb_cStruct)
00028             return Qnil;
00029     }
00030 }
00031 
00032 VALUE
00033 rb_struct_iv_get(VALUE c, const char *name)
00034 {
00035     return struct_ivar_get(c, rb_intern(name));
00036 }
00037 
00038 VALUE
00039 rb_struct_s_members(VALUE klass)
00040 {
00041     VALUE members = struct_ivar_get(klass, id_members);
00042 
00043     if (NIL_P(members)) {
00044         rb_raise(rb_eTypeError, "uninitialized struct");
00045     }
00046     if (!RB_TYPE_P(members, T_ARRAY)) {
00047         rb_raise(rb_eTypeError, "corrupted struct");
00048     }
00049     return members;
00050 }
00051 
00052 VALUE
00053 rb_struct_members(VALUE s)
00054 {
00055     VALUE members = rb_struct_s_members(rb_obj_class(s));
00056 
00057     if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
00058         rb_raise(rb_eTypeError, "struct size differs (%ld required %ld given)",
00059                  RARRAY_LEN(members), RSTRUCT_LEN(s));
00060     }
00061     return members;
00062 }
00063 
00064 static VALUE
00065 rb_struct_s_members_m(VALUE klass)
00066 {
00067     VALUE members = rb_struct_s_members(klass);
00068 
00069     return rb_ary_dup(members);
00070 }
00071 
00072 /*
00073  *  call-seq:
00074  *     struct.members    -> array
00075  *
00076  *  Returns an array of symbols representing the names of the instance
00077  *  variables.
00078  *
00079  *     Customer = Struct.new(:name, :address, :zip)
00080  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00081  *     joe.members   #=> [:name, :address, :zip]
00082  */
00083 
00084 static VALUE
00085 rb_struct_members_m(VALUE obj)
00086 {
00087     return rb_struct_s_members_m(rb_obj_class(obj));
00088 }
00089 
00090 VALUE
00091 rb_struct_getmember(VALUE obj, ID id)
00092 {
00093     VALUE members, slot, *ptr, *ptr_members;
00094     long i, len;
00095 
00096     ptr = RSTRUCT_PTR(obj);
00097     members = rb_struct_members(obj);
00098     ptr_members = RARRAY_PTR(members);
00099     slot = ID2SYM(id);
00100     len = RARRAY_LEN(members);
00101     for (i=0; i<len; i++) {
00102         if (ptr_members[i] == slot) {
00103             return ptr[i];
00104         }
00105     }
00106     rb_name_error(id, "%s is not struct member", rb_id2name(id));
00107 
00108     UNREACHABLE;
00109 }
00110 
00111 static VALUE
00112 rb_struct_ref(VALUE obj)
00113 {
00114     return rb_struct_getmember(obj, rb_frame_this_func());
00115 }
00116 
00117 static VALUE rb_struct_ref0(VALUE obj) {return RSTRUCT_PTR(obj)[0];}
00118 static VALUE rb_struct_ref1(VALUE obj) {return RSTRUCT_PTR(obj)[1];}
00119 static VALUE rb_struct_ref2(VALUE obj) {return RSTRUCT_PTR(obj)[2];}
00120 static VALUE rb_struct_ref3(VALUE obj) {return RSTRUCT_PTR(obj)[3];}
00121 static VALUE rb_struct_ref4(VALUE obj) {return RSTRUCT_PTR(obj)[4];}
00122 static VALUE rb_struct_ref5(VALUE obj) {return RSTRUCT_PTR(obj)[5];}
00123 static VALUE rb_struct_ref6(VALUE obj) {return RSTRUCT_PTR(obj)[6];}
00124 static VALUE rb_struct_ref7(VALUE obj) {return RSTRUCT_PTR(obj)[7];}
00125 static VALUE rb_struct_ref8(VALUE obj) {return RSTRUCT_PTR(obj)[8];}
00126 static VALUE rb_struct_ref9(VALUE obj) {return RSTRUCT_PTR(obj)[9];}
00127 
00128 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
00129 #define N_REF_FUNC numberof(ref_func)
00130 
00131 static VALUE (*const ref_func[])(VALUE) = {
00132     rb_struct_ref0,
00133     rb_struct_ref1,
00134     rb_struct_ref2,
00135     rb_struct_ref3,
00136     rb_struct_ref4,
00137     rb_struct_ref5,
00138     rb_struct_ref6,
00139     rb_struct_ref7,
00140     rb_struct_ref8,
00141     rb_struct_ref9,
00142 };
00143 
00144 static void
00145 rb_struct_modify(VALUE s)
00146 {
00147     rb_check_frozen(s);
00148     rb_check_trusted(s);
00149 }
00150 
00151 static VALUE
00152 rb_struct_set(VALUE obj, VALUE val)
00153 {
00154     VALUE members, slot, *ptr, *ptr_members;
00155     long i, len;
00156 
00157     members = rb_struct_members(obj);
00158     ptr_members = RARRAY_PTR(members);
00159     len = RARRAY_LEN(members);
00160     rb_struct_modify(obj);
00161     ptr = RSTRUCT_PTR(obj);
00162     for (i=0; i<len; i++) {
00163         slot = ptr_members[i];
00164         if (rb_id_attrset(SYM2ID(slot)) == rb_frame_this_func()) {
00165             return ptr[i] = val;
00166         }
00167     }
00168     rb_name_error(rb_frame_this_func(), "`%s' is not a struct member",
00169                   rb_id2name(rb_frame_this_func()));
00170 
00171     UNREACHABLE;
00172 }
00173 
00174 static VALUE
00175 make_struct(VALUE name, VALUE members, VALUE klass)
00176 {
00177     VALUE nstr, *ptr_members;
00178     ID id;
00179     long i, len;
00180 
00181     OBJ_FREEZE(members);
00182     if (NIL_P(name)) {
00183         nstr = rb_class_new(klass);
00184         rb_make_metaclass(nstr, RBASIC(klass)->klass);
00185         rb_class_inherited(klass, nstr);
00186     }
00187     else {
00188         /* old style: should we warn? */
00189         name = rb_str_to_str(name);
00190         id = rb_to_id(name);
00191         if (!rb_is_const_id(id)) {
00192             rb_name_error(id, "identifier %s needs to be constant", StringValuePtr(name));
00193         }
00194         if (rb_const_defined_at(klass, id)) {
00195             rb_warn("redefining constant Struct::%s", StringValuePtr(name));
00196             rb_mod_remove_const(klass, ID2SYM(id));
00197         }
00198         nstr = rb_define_class_id_under(klass, id, klass);
00199     }
00200     rb_ivar_set(nstr, id_members, members);
00201 
00202     rb_define_alloc_func(nstr, struct_alloc);
00203     rb_define_singleton_method(nstr, "new", rb_class_new_instance, -1);
00204     rb_define_singleton_method(nstr, "[]", rb_class_new_instance, -1);
00205     rb_define_singleton_method(nstr, "members", rb_struct_s_members_m, 0);
00206     ptr_members = RARRAY_PTR(members);
00207     len = RARRAY_LEN(members);
00208     for (i=0; i< len; i++) {
00209         ID id = SYM2ID(ptr_members[i]);
00210         if (i < N_REF_FUNC) {
00211             rb_define_method_id(nstr, id, ref_func[i], 0);
00212         }
00213         else {
00214             rb_define_method_id(nstr, id, rb_struct_ref, 0);
00215         }
00216         rb_define_method_id(nstr, rb_id_attrset(id), rb_struct_set, 1);
00217     }
00218 
00219     return nstr;
00220 }
00221 
00222 VALUE
00223 rb_struct_alloc_noinit(VALUE klass)
00224 {
00225     return struct_alloc(klass);
00226 }
00227 
00228 VALUE
00229 rb_struct_define_without_accessor(const char *class_name, VALUE super, rb_alloc_func_t alloc, ...)
00230 {
00231     VALUE klass;
00232     va_list ar;
00233     VALUE members;
00234     char *name;
00235 
00236     members = rb_ary_new2(0);
00237     va_start(ar, alloc);
00238     while ((name = va_arg(ar, char*)) != NULL) {
00239         rb_ary_push(members, ID2SYM(rb_intern(name)));
00240     }
00241     va_end(ar);
00242     OBJ_FREEZE(members);
00243 
00244     if (class_name) {
00245         klass = rb_define_class(class_name, super);
00246     }
00247     else {
00248         klass = rb_class_new(super);
00249         rb_make_metaclass(klass, RBASIC(super)->klass);
00250         rb_class_inherited(super, klass);
00251     }
00252 
00253     rb_ivar_set(klass, id_members, members);
00254 
00255     if (alloc)
00256         rb_define_alloc_func(klass, alloc);
00257     else
00258         rb_define_alloc_func(klass, struct_alloc);
00259 
00260     return klass;
00261 }
00262 
00263 VALUE
00264 rb_struct_define(const char *name, ...)
00265 {
00266     va_list ar;
00267     VALUE nm, ary;
00268     char *mem;
00269 
00270     if (!name) nm = Qnil;
00271     else nm = rb_str_new2(name);
00272     ary = rb_ary_new();
00273 
00274     va_start(ar, name);
00275     while ((mem = va_arg(ar, char*)) != 0) {
00276         ID slot = rb_intern(mem);
00277         rb_ary_push(ary, ID2SYM(slot));
00278     }
00279     va_end(ar);
00280 
00281     return make_struct(nm, ary, rb_cStruct);
00282 }
00283 
00284 /*
00285  *  call-seq:
00286  *     Struct.new( [aString] [, aSym]+> )                         -> StructClass
00287  *     Struct.new( [aString] [, aSym]+> ) {|StructClass| block }  -> StructClass
00288  *     StructClass.new(arg, ...)                                  -> obj
00289  *     StructClass[arg, ...]                                      -> obj
00290  *
00291  *  Creates a new class, named by <i>aString</i>, containing accessor
00292  *  methods for the given symbols. If the name <i>aString</i> is
00293  *  omitted, an anonymous structure class will be created. Otherwise,
00294  *  the name of this struct will appear as a constant in class
00295  *  <code>Struct</code>, so it must be unique for all
00296  *  <code>Struct</code>s in the system and should start with a capital
00297  *  letter. Assigning a structure class to a constant effectively gives
00298  *  the class the name of the constant.
00299  *
00300  *  If a block is given, it will be evaluated in the context of
00301  *  <i>StructClass</i>, passing <i>StructClass</i> as a parameter.
00302  *
00303  *     Customer = Struct.new(:name, :address) do
00304  *       def greeting
00305  *         "Hello #{name}!"
00306  *       end
00307  *     end
00308  *     Customer.new("Dave", "123 Main").greeting  # => "Hello Dave!"
00309  *
00310  *  <code>Struct::new</code> returns a new <code>Class</code> object,
00311  *  which can then be used to create specific instances of the new
00312  *  structure. The number of actual parameters must be
00313  *  less than or equal to the number of attributes defined for this
00314  *  class; unset parameters default to <code>nil</code>.  Passing too many
00315  *  parameters will raise an <code>ArgumentError</code>.
00316  *
00317  *  The remaining methods listed in this section (class and instance)
00318  *  are defined for this generated class.
00319  *
00320  *     # Create a structure with a name in Struct
00321  *     Struct.new("Customer", :name, :address)    #=> Struct::Customer
00322  *     Struct::Customer.new("Dave", "123 Main")   #=> #<struct Struct::Customer name="Dave", address="123 Main">
00323  *
00324  *     # Create a structure named by its constant
00325  *     Customer = Struct.new(:name, :address)     #=> Customer
00326  *     Customer.new("Dave", "123 Main")           #=> #<struct Customer name="Dave", address="123 Main">
00327  */
00328 
00329 static VALUE
00330 rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
00331 {
00332     VALUE name, rest;
00333     long i;
00334     VALUE st;
00335     ID id;
00336 
00337     rb_scan_args(argc, argv, "1*", &name, &rest);
00338     if (!NIL_P(name) && SYMBOL_P(name)) {
00339         rb_ary_unshift(rest, name);
00340         name = Qnil;
00341     }
00342     for (i=0; i<RARRAY_LEN(rest); i++) {
00343         id = rb_to_id(RARRAY_PTR(rest)[i]);
00344         RARRAY_PTR(rest)[i] = ID2SYM(id);
00345     }
00346     st = make_struct(name, rest, klass);
00347     if (rb_block_given_p()) {
00348         rb_mod_module_eval(0, 0, st);
00349     }
00350 
00351     return st;
00352 }
00353 
00354 static long
00355 num_members(VALUE klass)
00356 {
00357     VALUE members;
00358     members = struct_ivar_get(klass, id_members);
00359     if (!RB_TYPE_P(members, T_ARRAY)) {
00360         rb_raise(rb_eTypeError, "broken members");
00361     }
00362     return RARRAY_LEN(members);
00363 }
00364 
00365 /*
00366  */
00367 
00368 static VALUE
00369 rb_struct_initialize_m(int argc, VALUE *argv, VALUE self)
00370 {
00371     VALUE klass = rb_obj_class(self);
00372     long n;
00373 
00374     rb_struct_modify(self);
00375     n = num_members(klass);
00376     if (n < argc) {
00377         rb_raise(rb_eArgError, "struct size differs");
00378     }
00379     MEMCPY(RSTRUCT_PTR(self), argv, VALUE, argc);
00380     if (n > argc) {
00381         rb_mem_clear(RSTRUCT_PTR(self)+argc, n-argc);
00382     }
00383     return Qnil;
00384 }
00385 
00386 VALUE
00387 rb_struct_initialize(VALUE self, VALUE values)
00388 {
00389     return rb_struct_initialize_m(RARRAY_LENINT(values), RARRAY_PTR(values), self);
00390 }
00391 
00392 static VALUE
00393 struct_alloc(VALUE klass)
00394 {
00395     long n;
00396     NEWOBJ_OF(st, struct RStruct, klass, T_STRUCT);
00397 
00398     n = num_members(klass);
00399 
00400     if (0 < n && n <= RSTRUCT_EMBED_LEN_MAX) {
00401         RBASIC(st)->flags &= ~RSTRUCT_EMBED_LEN_MASK;
00402         RBASIC(st)->flags |= n << RSTRUCT_EMBED_LEN_SHIFT;
00403         rb_mem_clear(st->as.ary, n);
00404     }
00405     else {
00406         st->as.heap.ptr = ALLOC_N(VALUE, n);
00407         rb_mem_clear(st->as.heap.ptr, n);
00408         st->as.heap.len = n;
00409     }
00410 
00411     return (VALUE)st;
00412 }
00413 
00414 VALUE
00415 rb_struct_alloc(VALUE klass, VALUE values)
00416 {
00417     return rb_class_new_instance(RARRAY_LENINT(values), RARRAY_PTR(values), klass);
00418 }
00419 
00420 VALUE
00421 rb_struct_new(VALUE klass, ...)
00422 {
00423     VALUE tmpargs[N_REF_FUNC], *mem = tmpargs;
00424     int size, i;
00425     va_list args;
00426 
00427     size = rb_long2int(num_members(klass));
00428     if (size > numberof(tmpargs)) {
00429         tmpargs[0] = rb_ary_tmp_new(size);
00430         mem = RARRAY_PTR(tmpargs[0]);
00431     }
00432     va_start(args, klass);
00433     for (i=0; i<size; i++) {
00434         mem[i] = va_arg(args, VALUE);
00435     }
00436     va_end(args);
00437 
00438     return rb_class_new_instance(size, mem, klass);
00439 }
00440 
00441 static VALUE
00442 rb_struct_size(VALUE s);
00443 
00444 /*
00445  *  call-seq:
00446  *     struct.each {|obj| block }  -> struct
00447  *     struct.each                 -> an_enumerator
00448  *
00449  *  Calls <i>block</i> once for each instance variable, passing the
00450  *  value as a parameter.
00451  *
00452  *  If no block is given, an enumerator is returned instead.
00453  *
00454  *     Customer = Struct.new(:name, :address, :zip)
00455  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00456  *     joe.each {|x| puts(x) }
00457  *
00458  *  <em>produces:</em>
00459  *
00460  *     Joe Smith
00461  *     123 Maple, Anytown NC
00462  *     12345
00463  */
00464 
00465 static VALUE
00466 rb_struct_each(VALUE s)
00467 {
00468     long i;
00469 
00470     RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size);
00471     for (i=0; i<RSTRUCT_LEN(s); i++) {
00472         rb_yield(RSTRUCT_PTR(s)[i]);
00473     }
00474     return s;
00475 }
00476 
00477 /*
00478  *  call-seq:
00479  *     struct.each_pair {|sym, obj| block }     -> struct
00480  *     struct.each_pair                         -> an_enumerator
00481  *
00482  *  Calls <i>block</i> once for each instance variable, passing the name
00483  *  (as a symbol) and the value as parameters.
00484  *
00485  *  If no block is given, an enumerator is returned instead.
00486  *
00487  *     Customer = Struct.new(:name, :address, :zip)
00488  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00489  *     joe.each_pair {|name, value| puts("#{name} => #{value}") }
00490  *
00491  *  <em>produces:</em>
00492  *
00493  *     name => Joe Smith
00494  *     address => 123 Maple, Anytown NC
00495  *     zip => 12345
00496  */
00497 
00498 static VALUE
00499 rb_struct_each_pair(VALUE s)
00500 {
00501     VALUE members;
00502     long i;
00503 
00504     RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size);
00505     members = rb_struct_members(s);
00506     for (i=0; i<RSTRUCT_LEN(s); i++) {
00507         VALUE key = rb_ary_entry(members, i);
00508         VALUE value = RSTRUCT_PTR(s)[i];
00509         rb_yield(rb_assoc_new(key, value));
00510     }
00511     return s;
00512 }
00513 
00514 static VALUE
00515 inspect_struct(VALUE s, VALUE dummy, int recur)
00516 {
00517     VALUE cname = rb_class_name(rb_obj_class(s));
00518     VALUE members, str = rb_str_new2("#<struct ");
00519     VALUE *ptr, *ptr_members;
00520     long i, len;
00521     char first = RSTRING_PTR(cname)[0];
00522 
00523     if (recur || first != '#') {
00524         rb_str_append(str, cname);
00525     }
00526     if (recur) {
00527         return rb_str_cat2(str, ":...>");
00528     }
00529 
00530     members = rb_struct_members(s);
00531     ptr_members = RARRAY_PTR(members);
00532     ptr = RSTRUCT_PTR(s);
00533     len = RSTRUCT_LEN(s);
00534     for (i=0; i<len; i++) {
00535         VALUE slot;
00536         ID id;
00537 
00538         if (i > 0) {
00539             rb_str_cat2(str, ", ");
00540         }
00541         else if (first != '#') {
00542             rb_str_cat2(str, " ");
00543         }
00544         slot = ptr_members[i];
00545         id = SYM2ID(slot);
00546         if (rb_is_local_id(id) || rb_is_const_id(id)) {
00547             rb_str_append(str, rb_id2str(id));
00548         }
00549         else {
00550             rb_str_append(str, rb_inspect(slot));
00551         }
00552         rb_str_cat2(str, "=");
00553         rb_str_append(str, rb_inspect(ptr[i]));
00554     }
00555     rb_str_cat2(str, ">");
00556     OBJ_INFECT(str, s);
00557 
00558     return str;
00559 }
00560 
00561 /*
00562  * call-seq:
00563  *   struct.to_s      -> string
00564  *   struct.inspect   -> string
00565  *
00566  * Describe the contents of this struct in a string.
00567  */
00568 
00569 static VALUE
00570 rb_struct_inspect(VALUE s)
00571 {
00572     return rb_exec_recursive(inspect_struct, s, 0);
00573 }
00574 
00575 /*
00576  *  call-seq:
00577  *     struct.to_a     -> array
00578  *     struct.values   -> array
00579  *
00580  *  Returns the values for this instance as an array.
00581  *
00582  *     Customer = Struct.new(:name, :address, :zip)
00583  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00584  *     joe.to_a[1]   #=> "123 Maple, Anytown NC"
00585  */
00586 
00587 static VALUE
00588 rb_struct_to_a(VALUE s)
00589 {
00590     return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_PTR(s));
00591 }
00592 
00593 /*
00594  *  call-seq:
00595  *     struct.to_h     -> hash
00596  *
00597  *  Returns the values for this instance as a hash with keys
00598  *  corresponding to the instance variable name.
00599  *
00600  *     Customer = Struct.new(:name, :address, :zip)
00601  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00602  *     joe.to_h[:address]   #=> "123 Maple, Anytown NC"
00603  */
00604 
00605 static VALUE
00606 rb_struct_to_h(VALUE s)
00607 {
00608     VALUE h = rb_hash_new();
00609     VALUE members = rb_struct_members(s);
00610     long i;
00611 
00612     for (i=0; i<RSTRUCT_LEN(s); i++) {
00613         rb_hash_aset(h, rb_ary_entry(members, i), RSTRUCT_PTR(s)[i]);
00614     }
00615     return h;
00616 }
00617 
00618 /* :nodoc: */
00619 VALUE
00620 rb_struct_init_copy(VALUE copy, VALUE s)
00621 {
00622     if (!OBJ_INIT_COPY(copy, s)) return copy;
00623     if (RSTRUCT_LEN(copy) != RSTRUCT_LEN(s)) {
00624         rb_raise(rb_eTypeError, "struct size mismatch");
00625     }
00626     MEMCPY(RSTRUCT_PTR(copy), RSTRUCT_PTR(s), VALUE, RSTRUCT_LEN(copy));
00627 
00628     return copy;
00629 }
00630 
00631 static VALUE
00632 rb_struct_aref_id(VALUE s, ID id)
00633 {
00634     VALUE *ptr, members, *ptr_members;
00635     long i, len;
00636 
00637     ptr = RSTRUCT_PTR(s);
00638     members = rb_struct_members(s);
00639     ptr_members = RARRAY_PTR(members);
00640     len = RARRAY_LEN(members);
00641     for (i=0; i<len; i++) {
00642         if (SYM2ID(ptr_members[i]) == id) {
00643             return ptr[i];
00644         }
00645     }
00646     rb_name_error(id, "no member '%s' in struct", rb_id2name(id));
00647 
00648     UNREACHABLE;
00649 }
00650 
00651 /*
00652  *  call-seq:
00653  *     struct[symbol]    -> anObject
00654  *     struct[fixnum]    -> anObject
00655  *
00656  *  Attribute Reference---Returns the value of the instance variable
00657  *  named by <i>symbol</i>, or indexed (0..length-1) by
00658  *  <i>fixnum</i>. Will raise <code>NameError</code> if the named
00659  *  variable does not exist, or <code>IndexError</code> if the index is
00660  *  out of range.
00661  *
00662  *     Customer = Struct.new(:name, :address, :zip)
00663  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00664  *
00665  *     joe["name"]   #=> "Joe Smith"
00666  *     joe[:name]    #=> "Joe Smith"
00667  *     joe[0]        #=> "Joe Smith"
00668  */
00669 
00670 VALUE
00671 rb_struct_aref(VALUE s, VALUE idx)
00672 {
00673     long i;
00674 
00675     if (RB_TYPE_P(idx, T_STRING) || RB_TYPE_P(idx, T_SYMBOL)) {
00676         return rb_struct_aref_id(s, rb_to_id(idx));
00677     }
00678 
00679     i = NUM2LONG(idx);
00680     if (i < 0) i = RSTRUCT_LEN(s) + i;
00681     if (i < 0)
00682         rb_raise(rb_eIndexError, "offset %ld too small for struct(size:%ld)",
00683                  i, RSTRUCT_LEN(s));
00684     if (RSTRUCT_LEN(s) <= i)
00685         rb_raise(rb_eIndexError, "offset %ld too large for struct(size:%ld)",
00686                  i, RSTRUCT_LEN(s));
00687     return RSTRUCT_PTR(s)[i];
00688 }
00689 
00690 static VALUE
00691 rb_struct_aset_id(VALUE s, ID id, VALUE val)
00692 {
00693     VALUE members, *ptr, *ptr_members;
00694     long i, len;
00695 
00696     members = rb_struct_members(s);
00697     len = RARRAY_LEN(members);
00698     rb_struct_modify(s);
00699     if (RSTRUCT_LEN(s) != len) {
00700         rb_raise(rb_eTypeError, "struct size differs (%ld required %ld given)",
00701                  len, RSTRUCT_LEN(s));
00702     }
00703     ptr = RSTRUCT_PTR(s);
00704     ptr_members = RARRAY_PTR(members);
00705     for (i=0; i<len; i++) {
00706         if (SYM2ID(ptr_members[i]) == id) {
00707             ptr[i] = val;
00708             return val;
00709         }
00710     }
00711     rb_name_error(id, "no member '%s' in struct", rb_id2name(id));
00712 
00713     UNREACHABLE;
00714 }
00715 
00716 /*
00717  *  call-seq:
00718  *     struct[symbol] = obj    -> obj
00719  *     struct[fixnum] = obj    -> obj
00720  *
00721  *  Attribute Assignment---Assigns to the instance variable named by
00722  *  <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
00723  *  returns it. Will raise a <code>NameError</code> if the named
00724  *  variable does not exist, or an <code>IndexError</code> if the index
00725  *  is out of range.
00726  *
00727  *     Customer = Struct.new(:name, :address, :zip)
00728  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00729  *
00730  *     joe["name"] = "Luke"
00731  *     joe[:zip]   = "90210"
00732  *
00733  *     joe.name   #=> "Luke"
00734  *     joe.zip    #=> "90210"
00735  */
00736 
00737 VALUE
00738 rb_struct_aset(VALUE s, VALUE idx, VALUE val)
00739 {
00740     long i;
00741 
00742     if (RB_TYPE_P(idx, T_STRING) || RB_TYPE_P(idx, T_SYMBOL)) {
00743         return rb_struct_aset_id(s, rb_to_id(idx), val);
00744     }
00745 
00746     i = NUM2LONG(idx);
00747     if (i < 0) i = RSTRUCT_LEN(s) + i;
00748     if (i < 0) {
00749         rb_raise(rb_eIndexError, "offset %ld too small for struct(size:%ld)",
00750                  i, RSTRUCT_LEN(s));
00751     }
00752     if (RSTRUCT_LEN(s) <= i) {
00753         rb_raise(rb_eIndexError, "offset %ld too large for struct(size:%ld)",
00754                  i, RSTRUCT_LEN(s));
00755     }
00756     rb_struct_modify(s);
00757     return RSTRUCT_PTR(s)[i] = val;
00758 }
00759 
00760 static VALUE
00761 struct_entry(VALUE s, long n)
00762 {
00763     return rb_struct_aref(s, LONG2NUM(n));
00764 }
00765 
00766 /*
00767  * call-seq:
00768  *   struct.values_at(selector,... )  -> an_array
00769  *
00770  *   Returns an array containing the elements in
00771  *   +self+ corresponding to the given selector(s). The selectors
00772  *   may be either integer indices or ranges.
00773  *   See also </code>.select<code>.
00774  *
00775  *      a = %w{ a b c d e f }
00776  *      a.values_at(1, 3, 5)
00777  *      a.values_at(1, 3, 5, 7)
00778  *      a.values_at(-1, -3, -5, -7)
00779  *      a.values_at(1..3, 2...5)
00780  */
00781 
00782 static VALUE
00783 rb_struct_values_at(int argc, VALUE *argv, VALUE s)
00784 {
00785     return rb_get_values_at(s, RSTRUCT_LEN(s), argc, argv, struct_entry);
00786 }
00787 
00788 /*
00789  *  call-seq:
00790  *     struct.select {|i| block }    -> array
00791  *     struct.select                 -> an_enumerator
00792  *
00793  *  Invokes the block passing in successive elements from
00794  *  <i>struct</i>, returning an array containing those elements
00795  *  for which the block returns a true value (equivalent to
00796  *  <code>Enumerable#select</code>).
00797  *
00798  *     Lots = Struct.new(:a, :b, :c, :d, :e, :f)
00799  *     l = Lots.new(11, 22, 33, 44, 55, 66)
00800  *     l.select {|v| (v % 2).zero? }   #=> [22, 44, 66]
00801  */
00802 
00803 static VALUE
00804 rb_struct_select(int argc, VALUE *argv, VALUE s)
00805 {
00806     VALUE result;
00807     long i;
00808 
00809     rb_check_arity(argc, 0, 0);
00810     RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size);
00811     result = rb_ary_new();
00812     for (i = 0; i < RSTRUCT_LEN(s); i++) {
00813         if (RTEST(rb_yield(RSTRUCT_PTR(s)[i]))) {
00814             rb_ary_push(result, RSTRUCT_PTR(s)[i]);
00815         }
00816     }
00817 
00818     return result;
00819 }
00820 
00821 static VALUE
00822 recursive_equal(VALUE s, VALUE s2, int recur)
00823 {
00824     VALUE *ptr, *ptr2;
00825     long i, len;
00826 
00827     if (recur) return Qtrue; /* Subtle! */
00828     ptr = RSTRUCT_PTR(s);
00829     ptr2 = RSTRUCT_PTR(s2);
00830     len = RSTRUCT_LEN(s);
00831     for (i=0; i<len; i++) {
00832         if (!rb_equal(ptr[i], ptr2[i])) return Qfalse;
00833     }
00834     return Qtrue;
00835 }
00836 
00837 /*
00838  *  call-seq:
00839  *     struct == other_struct     -> true or false
00840  *
00841  *  Equality---Returns <code>true</code> if <i>other_struct</i> is
00842  *  equal to this one: they must be of the same class as generated by
00843  *  <code>Struct::new</code>, and the values of all instance variables
00844  *  must be equal (according to <code>Object#==</code>).
00845  *
00846  *     Customer = Struct.new(:name, :address, :zip)
00847  *     joe   = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00848  *     joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00849  *     jane  = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
00850  *     joe == joejr   #=> true
00851  *     joe == jane    #=> false
00852  */
00853 
00854 static VALUE
00855 rb_struct_equal(VALUE s, VALUE s2)
00856 {
00857     if (s == s2) return Qtrue;
00858     if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse;
00859     if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse;
00860     if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
00861         rb_bug("inconsistent struct"); /* should never happen */
00862     }
00863 
00864     return rb_exec_recursive_paired(recursive_equal, s, s2, s2);
00865 }
00866 
00867 static VALUE
00868 recursive_hash(VALUE s, VALUE dummy, int recur)
00869 {
00870     long i, len;
00871     st_index_t h;
00872     VALUE n, *ptr;
00873 
00874     h = rb_hash_start(rb_hash(rb_obj_class(s)));
00875     if (!recur) {
00876         ptr = RSTRUCT_PTR(s);
00877         len = RSTRUCT_LEN(s);
00878         for (i = 0; i < len; i++) {
00879             n = rb_hash(ptr[i]);
00880             h = rb_hash_uint(h, NUM2LONG(n));
00881         }
00882     }
00883     h = rb_hash_end(h);
00884     return INT2FIX(h);
00885 }
00886 
00887 /*
00888  * call-seq:
00889  *   struct.hash   -> fixnum
00890  *
00891  * Return a hash value based on this struct's contents.
00892  */
00893 
00894 static VALUE
00895 rb_struct_hash(VALUE s)
00896 {
00897     return rb_exec_recursive_outer(recursive_hash, s, 0);
00898 }
00899 
00900 static VALUE
00901 recursive_eql(VALUE s, VALUE s2, int recur)
00902 {
00903     VALUE *ptr, *ptr2;
00904     long i, len;
00905 
00906     if (recur) return Qtrue; /* Subtle! */
00907     ptr = RSTRUCT_PTR(s);
00908     ptr2 = RSTRUCT_PTR(s2);
00909     len = RSTRUCT_LEN(s);
00910     for (i=0; i<len; i++) {
00911         if (!rb_eql(ptr[i], ptr2[i])) return Qfalse;
00912     }
00913     return Qtrue;
00914 }
00915 
00916 /*
00917  * call-seq:
00918  *   struct.eql?(other)   -> true or false
00919  *
00920  * Two structures are equal if they are the same object, or if all their
00921  * fields are equal (using <code>eql?</code>).
00922  */
00923 
00924 static VALUE
00925 rb_struct_eql(VALUE s, VALUE s2)
00926 {
00927     if (s == s2) return Qtrue;
00928     if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse;
00929     if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse;
00930     if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
00931         rb_bug("inconsistent struct"); /* should never happen */
00932     }
00933 
00934     return rb_exec_recursive_paired(recursive_eql, s, s2, s2);
00935 }
00936 
00937 /*
00938  *  call-seq:
00939  *     struct.length    -> fixnum
00940  *     struct.size      -> fixnum
00941  *
00942  *  Returns the number of instance variables.
00943  *
00944  *     Customer = Struct.new(:name, :address, :zip)
00945  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00946  *     joe.length   #=> 3
00947  */
00948 
00949 static VALUE
00950 rb_struct_size(VALUE s)
00951 {
00952     return LONG2FIX(RSTRUCT_LEN(s));
00953 }
00954 
00955 /*
00956  *  A <code>Struct</code> is a convenient way to bundle a number of
00957  *  attributes together, using accessor methods, without having to write
00958  *  an explicit class.
00959  *
00960  *  The <code>Struct</code> class is a generator of specific classes,
00961  *  each one of which is defined to hold a set of variables and their
00962  *  accessors. In these examples, we'll call the generated class
00963  *  ``<i>Customer</i>Class,'' and we'll show an example instance of that
00964  *  class as ``<i>Customer</i>Inst.''
00965  *
00966  *  In the descriptions that follow, the parameter <i>symbol</i> refers
00967  *  to a symbol, which is either a quoted string or a
00968  *  <code>Symbol</code> (such as <code>:name</code>).
00969  */
00970 void
00971 Init_Struct(void)
00972 {
00973     rb_cStruct = rb_define_class("Struct", rb_cObject);
00974     rb_include_module(rb_cStruct, rb_mEnumerable);
00975 
00976     rb_undef_alloc_func(rb_cStruct);
00977     rb_define_singleton_method(rb_cStruct, "new", rb_struct_s_def, -1);
00978 
00979     rb_define_method(rb_cStruct, "initialize", rb_struct_initialize_m, -1);
00980     rb_define_method(rb_cStruct, "initialize_copy", rb_struct_init_copy, 1);
00981 
00982     rb_define_method(rb_cStruct, "==", rb_struct_equal, 1);
00983     rb_define_method(rb_cStruct, "eql?", rb_struct_eql, 1);
00984     rb_define_method(rb_cStruct, "hash", rb_struct_hash, 0);
00985 
00986     rb_define_method(rb_cStruct, "inspect", rb_struct_inspect, 0);
00987     rb_define_alias(rb_cStruct,  "to_s", "inspect");
00988     rb_define_method(rb_cStruct, "to_a", rb_struct_to_a, 0);
00989     rb_define_method(rb_cStruct, "to_h", rb_struct_to_h, 0);
00990     rb_define_method(rb_cStruct, "values", rb_struct_to_a, 0);
00991     rb_define_method(rb_cStruct, "size", rb_struct_size, 0);
00992     rb_define_method(rb_cStruct, "length", rb_struct_size, 0);
00993 
00994     rb_define_method(rb_cStruct, "each", rb_struct_each, 0);
00995     rb_define_method(rb_cStruct, "each_pair", rb_struct_each_pair, 0);
00996     rb_define_method(rb_cStruct, "[]", rb_struct_aref, 1);
00997     rb_define_method(rb_cStruct, "[]=", rb_struct_aset, 2);
00998     rb_define_method(rb_cStruct, "select", rb_struct_select, -1);
00999     rb_define_method(rb_cStruct, "values_at", rb_struct_values_at, -1);
01000 
01001     rb_define_method(rb_cStruct, "members", rb_struct_members_m, 0);
01002     id_members = rb_intern("__members__");
01003 }
01004