1 /*
2 ** struct.c - Struct class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6 
7 #include <string.h>
8 #include <mruby.h>
9 #include <mruby/array.h>
10 #include <mruby/string.h>
11 #include <mruby/class.h>
12 #include <mruby/variable.h>
13 #include <mruby/hash.h>
14 #include <mruby/range.h>
15 #include <mruby/proc.h>
16 
17 #define RSTRUCT_LEN(st) RARRAY_LEN(st)
18 #define RSTRUCT_PTR(st) RARRAY_PTR(st)
19 
20 static struct RClass *
struct_class(mrb_state * mrb)21 struct_class(mrb_state *mrb)
22 {
23   return mrb_class_get(mrb, "Struct");
24 }
25 
26 static inline mrb_value
struct_ivar_get(mrb_state * mrb,mrb_value cls,mrb_sym id)27 struct_ivar_get(mrb_state *mrb, mrb_value cls, mrb_sym id)
28 {
29   struct RClass* c = mrb_class_ptr(cls);
30   struct RClass* sclass = struct_class(mrb);
31   mrb_value ans;
32 
33   for (;;) {
34     ans = mrb_iv_get(mrb, mrb_obj_value(c), id);
35     if (!mrb_nil_p(ans)) return ans;
36     c = c->super;
37     if (c == sclass || c == 0)
38       return mrb_nil_value();
39   }
40 }
41 
42 static mrb_value
struct_s_members(mrb_state * mrb,struct RClass * klass)43 struct_s_members(mrb_state *mrb, struct RClass *klass)
44 {
45   mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
46 
47   if (mrb_nil_p(members)) {
48     mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
49   }
50   if (!mrb_array_p(members)) {
51     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
52   }
53   return members;
54 }
55 
56 static mrb_value
struct_members(mrb_state * mrb,mrb_value s)57 struct_members(mrb_state *mrb, mrb_value s)
58 {
59   mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s));
60   if (!mrb_array_p(s)) {
61     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
62   }
63   if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
64     if (RSTRUCT_LEN(s) == 0) {  /* probably uninitialized */
65       mrb_ary_resize(mrb, s, RARRAY_LEN(members));
66     }
67     else {
68       mrb_raisef(mrb, E_TYPE_ERROR,
69                  "struct size differs (%i required %i given)",
70                  RARRAY_LEN(members), RSTRUCT_LEN(s));
71     }
72   }
73   return members;
74 }
75 
76 static mrb_value
mrb_struct_s_members_m(mrb_state * mrb,mrb_value klass)77 mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
78 {
79   mrb_value members, ary;
80 
81   members = struct_s_members(mrb, mrb_class_ptr(klass));
82   ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
83   mrb_ary_replace(mrb, ary, members);
84   return ary;
85 }
86 
87 static void
mrb_struct_modify(mrb_state * mrb,mrb_value strct)88 mrb_struct_modify(mrb_state *mrb, mrb_value strct)
89 {
90   mrb_check_frozen(mrb, mrb_basic_ptr(strct));
91   mrb_write_barrier(mrb, mrb_basic_ptr(strct));
92 }
93 
94 /* 15.2.18.4.6  */
95 /*
96  *  call-seq:
97  *     struct.members    -> array
98  *
99  *  Returns an array of strings representing the names of the instance
100  *  variables.
101  *
102  *     Customer = Struct.new(:name, :address, :zip)
103  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
104  *     joe.members   #=> [:name, :address, :zip]
105  */
106 
107 static mrb_value
mrb_struct_members(mrb_state * mrb,mrb_value obj)108 mrb_struct_members(mrb_state *mrb, mrb_value obj)
109 {
110   return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
111 }
112 
113 static mrb_value
mrb_struct_ref(mrb_state * mrb,mrb_value obj)114 mrb_struct_ref(mrb_state *mrb, mrb_value obj)
115 {
116   mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0));
117   mrb_value *ptr = RSTRUCT_PTR(obj);
118 
119   if (!ptr) return mrb_nil_value();
120   return ptr[i];
121 }
122 
123 static mrb_sym
mrb_id_attrset(mrb_state * mrb,mrb_sym id)124 mrb_id_attrset(mrb_state *mrb, mrb_sym id)
125 {
126 #define ONSTACK_ALLOC_MAX 32
127 #define ONSTACK_STRLEN_MAX (ONSTACK_ALLOC_MAX - 1) /* '=' character */
128 
129   const char *name;
130   char *buf;
131   mrb_int len;
132   mrb_sym mid;
133   char onstack[ONSTACK_ALLOC_MAX];
134 
135   name = mrb_sym_name_len(mrb, id, &len);
136   if (len > ONSTACK_STRLEN_MAX) {
137     buf = (char *)mrb_malloc(mrb, (size_t)len+1);
138   }
139   else {
140     buf = onstack;
141   }
142   memcpy(buf, name, (size_t)len);
143   buf[len] = '=';
144 
145   mid = mrb_intern(mrb, buf, len+1);
146   if (buf != onstack) {
147     mrb_free(mrb, buf);
148   }
149   return mid;
150 }
151 
152 static mrb_value
mrb_struct_set_m(mrb_state * mrb,mrb_value obj)153 mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
154 {
155   mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0));
156   mrb_value *ptr;
157   mrb_value val = mrb_get_arg1(mrb);
158 
159   mrb_struct_modify(mrb, obj);
160   ptr = RSTRUCT_PTR(obj);
161   if (ptr == NULL || i >= RSTRUCT_LEN(obj)) {
162     mrb_ary_set(mrb, obj, i, val);
163   }
164   else {
165     ptr[i] = val;
166   }
167   return val;
168 }
169 
170 static void
make_struct_define_accessors(mrb_state * mrb,mrb_value members,struct RClass * c)171 make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
172 {
173   const mrb_value *ptr_members = RARRAY_PTR(members);
174   mrb_int i;
175   mrb_int len = RARRAY_LEN(members);
176   int ai = mrb_gc_arena_save(mrb);
177 
178   for (i=0; i<len; i++) {
179     mrb_sym id = mrb_symbol(ptr_members[i]);
180     mrb_method_t m;
181     mrb_value at = mrb_fixnum_value(i);
182     struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
183     struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
184     MRB_METHOD_FROM_PROC(m, aref);
185     mrb_define_method_raw(mrb, c, id, m);
186     MRB_METHOD_FROM_PROC(m, aset);
187     mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
188     mrb_gc_arena_restore(mrb, ai);
189   }
190 }
191 
192 static mrb_value
make_struct(mrb_state * mrb,mrb_value name,mrb_value members,struct RClass * klass)193 make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *klass)
194 {
195   mrb_value nstr;
196   mrb_sym id;
197   struct RClass *c;
198 
199   if (mrb_nil_p(name)) {
200     c = mrb_class_new(mrb, klass);
201   }
202   else {
203     /* old style: should we warn? */
204     mrb_to_str(mrb, name);
205     id = mrb_obj_to_sym(mrb, name);
206     if (!mrb_const_name_p(mrb, RSTRING_PTR(name), RSTRING_LEN(name))) {
207       mrb_name_error(mrb, id, "identifier %v needs to be constant", name);
208     }
209     if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
210       mrb_warn(mrb, "redefining constant Struct::%v", name);
211       mrb_const_remove(mrb, mrb_obj_value(klass), id);
212     }
213     c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
214   }
215   MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
216   nstr = mrb_obj_value(c);
217   mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
218 
219   mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
220   mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
221   mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
222   /* RSTRUCT(nstr)->basic.c->super = c->c; */
223   make_struct_define_accessors(mrb, members, c);
224   return nstr;
225 }
226 
227 /* 15.2.18.3.1  */
228 /*
229  *  call-seq:
230  *     Struct.new( [aString] [, aSym]+> )    -> StructClass
231  *     StructClass.new(arg, ...)             -> obj
232  *     StructClass[arg, ...]                 -> obj
233  *
234  *  Creates a new class, named by <i>aString</i>, containing accessor
235  *  methods for the given symbols. If the name <i>aString</i> is
236  *  omitted, an anonymous structure class will be created. Otherwise,
237  *  the name of this struct will appear as a constant in class
238  *  <code>Struct</code>, so it must be unique for all
239  *  <code>Struct</code>s in the system and should start with a capital
240  *  letter. Assigning a structure class to a constant effectively gives
241  *  the class the name of the constant.
242  *
243  *  <code>Struct::new</code> returns a new <code>Class</code> object,
244  *  which can then be used to create specific instances of the new
245  *  structure. The number of actual parameters must be
246  *  less than or equal to the number of attributes defined for this
247  *  class; unset parameters default to <code>nil</code>.  Passing too many
248  *  parameters will raise an <code>ArgumentError</code>.
249  *
250  *  The remaining methods listed in this section (class and instance)
251  *  are defined for this generated class.
252  *
253  *     # Create a structure with a name in Struct
254  *     Struct.new("Customer", :name, :address)    #=> Struct::Customer
255  *     Struct::Customer.new("Dave", "123 Main")   #=> #<struct Struct::Customer name="Dave", address="123 Main">
256  *
257  *     # Create a structure named by its constant
258  *     Customer = Struct.new(:name, :address)     #=> Customer
259  *     Customer.new("Dave", "123 Main")           #=> #<struct Customer name="Dave", address="123 Main">
260  */
261 static mrb_value
mrb_struct_s_def(mrb_state * mrb,mrb_value klass)262 mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
263 {
264   mrb_value name, rest;
265   mrb_value *pargv;
266   mrb_int argcnt;
267   mrb_int i;
268   mrb_value b, st;
269   mrb_sym id;
270   mrb_value *argv;
271   mrb_int argc;
272 
273   name = mrb_nil_value();
274   mrb_get_args(mrb, "*&", &argv, &argc, &b);
275   if (argc == 0) { /* special case to avoid crash */
276     mrb_argnum_error(mrb, argc, 1, -1);
277   }
278   else {
279     pargv = argv;
280     argcnt = argc;
281     if (argc > 0) {
282       name = argv[0];
283       if (mrb_symbol_p(name)) {
284         /* 1stArgument:symbol -> name=nil rest=argv[0..n] */
285         name = mrb_nil_value();
286       }
287       else {
288         pargv++;
289         argcnt--;
290       }
291     }
292     rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
293     for (i=0; i<argcnt; i++) {
294       id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
295       mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
296     }
297     st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
298     if (!mrb_nil_p(b)) {
299       mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
300     }
301 
302     return st;
303   }
304   /* not reached */
305   return mrb_nil_value();
306 }
307 
308 static mrb_int
num_members(mrb_state * mrb,struct RClass * klass)309 num_members(mrb_state *mrb, struct RClass *klass)
310 {
311   mrb_value members;
312 
313   members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
314   if (!mrb_array_p(members)) {
315     mrb_raise(mrb, E_TYPE_ERROR, "broken members");
316   }
317   return RARRAY_LEN(members);
318 }
319 
320 /* 15.2.18.4.8  */
321 /*
322  */
323 static mrb_value
mrb_struct_initialize_withArg(mrb_state * mrb,mrb_int argc,mrb_value * argv,mrb_value self)324 mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
325 {
326   struct RClass *klass = mrb_obj_class(mrb, self);
327   mrb_int i, n;
328 
329   n = num_members(mrb, klass);
330   if (n < argc) {
331     mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs");
332   }
333 
334   for (i = 0; i < argc; i++) {
335     mrb_ary_set(mrb, self, i, argv[i]);
336   }
337   for (i = argc; i < n; i++) {
338     mrb_ary_set(mrb, self, i, mrb_nil_value());
339   }
340   return self;
341 }
342 
343 static mrb_value
mrb_struct_initialize(mrb_state * mrb,mrb_value self)344 mrb_struct_initialize(mrb_state *mrb, mrb_value self)
345 {
346   mrb_value *argv;
347   mrb_int argc;
348 
349   mrb_get_args(mrb, "*!", &argv, &argc);
350   return mrb_struct_initialize_withArg(mrb, argc, argv, self);
351 }
352 
353 /* 15.2.18.4.9  */
354 /* :nodoc: */
355 static mrb_value
mrb_struct_init_copy(mrb_state * mrb,mrb_value copy)356 mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
357 {
358   mrb_value s = mrb_get_arg1(mrb);
359 
360   if (mrb_obj_equal(mrb, copy, s)) return copy;
361   if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
362     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
363   }
364   if (!mrb_array_p(s)) {
365     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
366   }
367   mrb_ary_replace(mrb, copy, s);
368   return copy;
369 }
370 
371 static mrb_value
struct_aref_sym(mrb_state * mrb,mrb_value obj,mrb_sym id)372 struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
373 {
374   mrb_value members, *ptr;
375   const mrb_value *ptr_members;
376   mrb_int i, len;
377 
378   members = struct_members(mrb, obj);
379   ptr_members = RARRAY_PTR(members);
380   len = RARRAY_LEN(members);
381   ptr = RSTRUCT_PTR(obj);
382   for (i=0; i<len; i++) {
383     mrb_value slot = ptr_members[i];
384     if (mrb_symbol_p(slot) && mrb_symbol(slot) == id) {
385       return ptr[i];
386     }
387   }
388   mrb_name_error(mrb, id, "no member '%n' in struct", id);
389   return mrb_nil_value();       /* not reached */
390 }
391 
392 static mrb_value
struct_aref_int(mrb_state * mrb,mrb_value s,mrb_int i)393 struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
394 {
395   mrb_int idx = i < 0 ? RSTRUCT_LEN(s) + i : i;
396 
397   if (idx < 0)
398     mrb_raisef(mrb, E_INDEX_ERROR,
399                "offset %i too small for struct(size:%i)", i, RSTRUCT_LEN(s));
400   if (RSTRUCT_LEN(s) <= idx)
401     mrb_raisef(mrb, E_INDEX_ERROR,
402                "offset %i too large for struct(size:%i)", i, RSTRUCT_LEN(s));
403   return RSTRUCT_PTR(s)[idx];
404 }
405 
406 /* 15.2.18.4.2  */
407 /*
408  *  call-seq:
409  *     struct[symbol]    -> anObject
410  *     struct[fixnum]    -> anObject
411  *
412  *  Attribute Reference---Returns the value of the instance variable
413  *  named by <i>symbol</i>, or indexed (0..length-1) by
414  *  <i>fixnum</i>. Will raise <code>NameError</code> if the named
415  *  variable does not exist, or <code>IndexError</code> if the index is
416  *  out of range.
417  *
418  *     Customer = Struct.new(:name, :address, :zip)
419  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
420  *
421  *     joe["name"]   #=> "Joe Smith"
422  *     joe[:name]    #=> "Joe Smith"
423  *     joe[0]        #=> "Joe Smith"
424  */
425 static mrb_value
mrb_struct_aref(mrb_state * mrb,mrb_value s)426 mrb_struct_aref(mrb_state *mrb, mrb_value s)
427 {
428   mrb_value idx = mrb_get_arg1(mrb);
429 
430   if (mrb_string_p(idx)) {
431     mrb_value sym = mrb_check_intern_str(mrb, idx);
432 
433     if (mrb_nil_p(sym)) {
434       mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%v' in struct", idx);
435     }
436     idx = sym;
437   }
438   if (mrb_symbol_p(idx)) {
439     return struct_aref_sym(mrb, s, mrb_symbol(idx));
440   }
441   return struct_aref_int(mrb, s, mrb_int(mrb, idx));
442 }
443 
444 static mrb_value
mrb_struct_aset_sym(mrb_state * mrb,mrb_value s,mrb_sym id,mrb_value val)445 mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
446 {
447   mrb_value members, *ptr;
448   const mrb_value *ptr_members;
449   mrb_int i, len;
450 
451   members = struct_members(mrb, s);
452   len = RARRAY_LEN(members);
453   ptr = RSTRUCT_PTR(s);
454   ptr_members = RARRAY_PTR(members);
455   for (i=0; i<len; i++) {
456     if (mrb_symbol(ptr_members[i]) == id) {
457       mrb_struct_modify(mrb, s);
458       ptr[i] = val;
459       return val;
460     }
461   }
462   mrb_name_error(mrb, id, "no member '%n' in struct", id);
463   return val;                   /* not reach */
464 }
465 
466 /* 15.2.18.4.3  */
467 /*
468  *  call-seq:
469  *     struct[symbol] = obj    -> obj
470  *     struct[fixnum] = obj    -> obj
471  *
472  *  Attribute Assignment---Assigns to the instance variable named by
473  *  <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
474  *  returns it. Will raise a <code>NameError</code> if the named
475  *  variable does not exist, or an <code>IndexError</code> if the index
476  *  is out of range.
477  *
478  *     Customer = Struct.new(:name, :address, :zip)
479  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
480  *
481  *     joe["name"] = "Luke"
482  *     joe[:zip]   = "90210"
483  *
484  *     joe.name   #=> "Luke"
485  *     joe.zip    #=> "90210"
486  */
487 
488 static mrb_value
mrb_struct_aset(mrb_state * mrb,mrb_value s)489 mrb_struct_aset(mrb_state *mrb, mrb_value s)
490 {
491   mrb_int i;
492   mrb_value idx;
493   mrb_value val;
494 
495   mrb_get_args(mrb, "oo", &idx, &val);
496 
497   if (mrb_string_p(idx)) {
498     mrb_value sym = mrb_check_intern_str(mrb, idx);
499 
500     if (mrb_nil_p(sym)) {
501       mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%v' in struct", idx);
502     }
503     idx = sym;
504   }
505   if (mrb_symbol_p(idx)) {
506     return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
507   }
508 
509   i = mrb_int(mrb, idx);
510   if (i < 0) i = RSTRUCT_LEN(s) + i;
511   if (i < 0) {
512     mrb_raisef(mrb, E_INDEX_ERROR,
513                "offset %i too small for struct(size:%i)", i, RSTRUCT_LEN(s));
514   }
515   if (RSTRUCT_LEN(s) <= i) {
516     mrb_raisef(mrb, E_INDEX_ERROR,
517                "offset %i too large for struct(size:%i)", i, RSTRUCT_LEN(s));
518   }
519   mrb_struct_modify(mrb, s);
520   return RSTRUCT_PTR(s)[i] = val;
521 }
522 
523 /* 15.2.18.4.1  */
524 /*
525  *  call-seq:
526  *     struct == other_struct     -> true or false
527  *
528  *  Equality---Returns <code>true</code> if <i>other_struct</i> is
529  *  equal to this one: they must be of the same class as generated by
530  *  <code>Struct::new</code>, and the values of all instance variables
531  *  must be equal (according to <code>Object#==</code>).
532  *
533  *     Customer = Struct.new(:name, :address, :zip)
534  *     joe   = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
535  *     joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
536  *     jane  = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
537  *     joe == joejr   #=> true
538  *     joe == jane    #=> false
539  */
540 
541 static mrb_value
mrb_struct_equal(mrb_state * mrb,mrb_value s)542 mrb_struct_equal(mrb_state *mrb, mrb_value s)
543 {
544   mrb_value s2 = mrb_get_arg1(mrb);
545   mrb_value *ptr, *ptr2;
546   mrb_int i, len;
547 
548   if (mrb_obj_equal(mrb, s, s2)) {
549     return mrb_true_value();
550   }
551   if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
552     return mrb_false_value();
553   }
554   if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
555     mrb_bug(mrb, "inconsistent struct"); /* should never happen */
556   }
557   ptr = RSTRUCT_PTR(s);
558   ptr2 = RSTRUCT_PTR(s2);
559   len = RSTRUCT_LEN(s);
560   for (i=0; i<len; i++) {
561     if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
562       return mrb_false_value();
563     }
564   }
565 
566   return mrb_true_value();
567 }
568 
569 /* 15.2.18.4.12(x)  */
570 /*
571  * code-seq:
572  *   struct.eql?(other)   -> true or false
573  *
574  * Two structures are equal if they are the same object, or if all their
575  * fields are equal (using <code>eql?</code>).
576  */
577 static mrb_value
mrb_struct_eql(mrb_state * mrb,mrb_value s)578 mrb_struct_eql(mrb_state *mrb, mrb_value s)
579 {
580   mrb_value s2 = mrb_get_arg1(mrb);
581   mrb_value *ptr, *ptr2;
582   mrb_int i, len;
583 
584   if (mrb_obj_equal(mrb, s, s2)) {
585     return mrb_true_value();
586   }
587   if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
588     return mrb_false_value();
589   }
590   if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
591     mrb_bug(mrb, "inconsistent struct"); /* should never happen */
592   }
593   ptr = RSTRUCT_PTR(s);
594   ptr2 = RSTRUCT_PTR(s2);
595   len = RSTRUCT_LEN(s);
596   for (i=0; i<len; i++) {
597     if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
598       return mrb_false_value();
599     }
600   }
601 
602   return mrb_true_value();
603 }
604 
605 /*
606  * call-seq:
607  *    struct.length   -> Fixnum
608  *    struct.size     -> Fixnum
609  *
610  * Returns number of struct members.
611  */
612 static mrb_value
mrb_struct_len(mrb_state * mrb,mrb_value self)613 mrb_struct_len(mrb_state *mrb, mrb_value self)
614 {
615   return mrb_fixnum_value(RSTRUCT_LEN(self));
616 }
617 
618 /*
619  * call-seq:
620  *    struct.to_a    -> array
621  *    struct.values  -> array
622  *
623  * Create an array from struct values.
624  */
625 static mrb_value
mrb_struct_to_a(mrb_state * mrb,mrb_value self)626 mrb_struct_to_a(mrb_state *mrb, mrb_value self)
627 {
628   return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self));
629 }
630 
631 /*
632  * call-seq:
633  *    struct.to_h -> hash
634  *
635  * Create a hash from member names and struct values.
636  */
637 static mrb_value
mrb_struct_to_h(mrb_state * mrb,mrb_value self)638 mrb_struct_to_h(mrb_state *mrb, mrb_value self)
639 {
640   mrb_value members, ret;
641   mrb_int i;
642 
643   members = struct_members(mrb, self);
644   ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
645 
646   for (i = 0; i < RARRAY_LEN(members); ++i) {
647     mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]);
648   }
649 
650   return ret;
651 }
652 
653 static mrb_value
mrb_struct_values_at(mrb_state * mrb,mrb_value self)654 mrb_struct_values_at(mrb_state *mrb, mrb_value self)
655 {
656   mrb_int argc;
657   mrb_value *argv;
658 
659   mrb_get_args(mrb, "*", &argv, &argc);
660 
661   return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
662 }
663 
664 /*
665  *  A <code>Struct</code> is a convenient way to bundle a number of
666  *  attributes together, using accessor methods, without having to write
667  *  an explicit class.
668  *
669  *  The <code>Struct</code> class is a generator of specific classes,
670  *  each one of which is defined to hold a set of variables and their
671  *  accessors. In these examples, we'll call the generated class
672  *  "<i>Customer</i>Class," and we'll show an example instance of that
673  *  class as "<i>Customer</i>Inst."
674  *
675  *  In the descriptions that follow, the parameter <i>symbol</i> refers
676  *  to a symbol, which is either a quoted string or a
677  *  <code>Symbol</code> (such as <code>:name</code>).
678  */
679 void
mrb_mruby_struct_gem_init(mrb_state * mrb)680 mrb_mruby_struct_gem_init(mrb_state* mrb)
681 {
682   struct RClass *st;
683   st = mrb_define_class(mrb, "Struct",  mrb->object_class);
684   MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY);
685 
686   mrb_define_class_method(mrb, st, "new",             mrb_struct_s_def,       MRB_ARGS_ANY());  /* 15.2.18.3.1  */
687 
688   mrb_define_method(mrb, st,       "==",              mrb_struct_equal,       MRB_ARGS_REQ(1)); /* 15.2.18.4.1  */
689   mrb_define_method(mrb, st,       "[]",              mrb_struct_aref,        MRB_ARGS_REQ(1)); /* 15.2.18.4.2  */
690   mrb_define_method(mrb, st,       "[]=",             mrb_struct_aset,        MRB_ARGS_REQ(2)); /* 15.2.18.4.3  */
691   mrb_define_method(mrb, st,       "members",         mrb_struct_members,     MRB_ARGS_NONE()); /* 15.2.18.4.6  */
692   mrb_define_method(mrb, st,       "initialize",      mrb_struct_initialize,  MRB_ARGS_ANY());  /* 15.2.18.4.8  */
693   mrb_define_method(mrb, st,       "initialize_copy", mrb_struct_init_copy,   MRB_ARGS_REQ(1)); /* 15.2.18.4.9  */
694   mrb_define_method(mrb, st,       "eql?",            mrb_struct_eql,         MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x)  */
695 
696   mrb_define_method(mrb, st,        "size",           mrb_struct_len,         MRB_ARGS_NONE());
697   mrb_define_method(mrb, st,        "length",         mrb_struct_len,         MRB_ARGS_NONE());
698   mrb_define_method(mrb, st,        "to_a",           mrb_struct_to_a,        MRB_ARGS_NONE());
699   mrb_define_method(mrb, st,        "values",         mrb_struct_to_a,        MRB_ARGS_NONE());
700   mrb_define_method(mrb, st,        "to_h",           mrb_struct_to_h,        MRB_ARGS_NONE());
701   mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_ANY());
702 }
703 
704 void
mrb_mruby_struct_gem_final(mrb_state * mrb)705 mrb_mruby_struct_gem_final(mrb_state* mrb)
706 {
707 }
708