constify pointer from RARRAY_PTR to detect potential write barrier bugs.
[mruby.git] / mrbgems / mruby-struct / src / struct.c
blob0c1016b4c5920bcc5befcf984cecf2240eea428b
1 /*
2 ** struct.c - Struct class
3 **
4 ** See Copyright Notice in mruby.h
5 */
7 #include <ctype.h>
8 #include <string.h>
9 #include "mruby.h"
10 #include "mruby/array.h"
11 #include "mruby/string.h"
12 #include "mruby/class.h"
13 #include "mruby/variable.h"
14 #include "mruby/hash.h"
15 #include "mruby/range.h"
17 #define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len
18 #define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr
20 static struct RClass *
21 struct_class(mrb_state *mrb)
23 return mrb_class_get(mrb, "Struct");
26 static inline mrb_value
27 struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
29 struct RClass* kclass;
30 struct RClass* sclass = struct_class(mrb);
31 mrb_value ans;
33 for (;;) {
34 ans = mrb_iv_get(mrb, c, id);
35 if (!mrb_nil_p(ans)) return ans;
36 kclass = RCLASS_SUPER(c);
37 if (kclass == 0 || kclass == sclass)
38 return mrb_nil_value();
39 c = mrb_obj_value(kclass);
43 static mrb_value
44 mrb_struct_s_members(mrb_state *mrb, mrb_value klass)
46 mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__"));
48 if (mrb_nil_p(members)) {
49 mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
51 if (!mrb_array_p(members)) {
52 mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
54 return members;
57 static mrb_value
58 mrb_struct_members(mrb_state *mrb, mrb_value s)
60 mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s)));
61 if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct")) {
62 if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
63 mrb_raisef(mrb, E_TYPE_ERROR,
64 "struct size differs (%S required %S given)",
65 mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
68 return members;
71 static mrb_value
72 mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
74 mrb_value members, ary;
76 members = mrb_struct_s_members(mrb, klass);
77 ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
78 mrb_ary_replace(mrb, ary, members);
79 return ary;
82 /* 15.2.18.4.6 */
84 * call-seq:
85 * struct.members -> array
87 * Returns an array of strings representing the names of the instance
88 * variables.
90 * Customer = Struct.new(:name, :address, :zip)
91 * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
92 * joe.members #=> [:name, :address, :zip]
95 static mrb_value
96 mrb_struct_members_m(mrb_state *mrb, mrb_value obj)
98 return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
101 static mrb_value
102 mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
104 mrb_value members, slot, *ptr;
105 const mrb_value *ptr_members;
106 mrb_int i, len;
108 ptr = RSTRUCT_PTR(obj);
109 members = mrb_struct_members(mrb, obj);
110 ptr_members = RARRAY_PTR(members);
111 slot = mrb_symbol_value(id);
112 len = RARRAY_LEN(members);
113 for (i=0; i<len; i++) {
114 if (mrb_obj_equal(mrb, ptr_members[i], slot)) {
115 return ptr[i];
118 mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, id));
119 return mrb_nil_value(); /* not reached */
122 static mrb_value
123 mrb_struct_ref(mrb_state *mrb, mrb_value obj)
125 return mrb_struct_getmember(mrb, obj, mrb->c->ci->mid);
128 static mrb_value mrb_struct_ref0(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[0];}
129 static mrb_value mrb_struct_ref1(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[1];}
130 static mrb_value mrb_struct_ref2(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[2];}
131 static mrb_value mrb_struct_ref3(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[3];}
132 static mrb_value mrb_struct_ref4(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[4];}
133 static mrb_value mrb_struct_ref5(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[5];}
134 static mrb_value mrb_struct_ref6(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[6];}
135 static mrb_value mrb_struct_ref7(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[7];}
136 static mrb_value mrb_struct_ref8(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[8];}
137 static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[9];}
139 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
140 #define N_REF_FUNC numberof(ref_func)
142 static const mrb_func_t ref_func[] = {
143 mrb_struct_ref0,
144 mrb_struct_ref1,
145 mrb_struct_ref2,
146 mrb_struct_ref3,
147 mrb_struct_ref4,
148 mrb_struct_ref5,
149 mrb_struct_ref6,
150 mrb_struct_ref7,
151 mrb_struct_ref8,
152 mrb_struct_ref9,
155 static mrb_sym
156 mrb_id_attrset(mrb_state *mrb, mrb_sym id)
158 const char *name;
159 char *buf;
160 mrb_int len;
161 mrb_sym mid;
163 name = mrb_sym2name_len(mrb, id, &len);
164 buf = (char *)mrb_malloc(mrb, (size_t)len+2);
165 memcpy(buf, name, (size_t)len);
166 buf[len] = '=';
167 buf[len+1] = '\0';
169 mid = mrb_intern(mrb, buf, len+1);
170 mrb_free(mrb, buf);
171 return mid;
174 static mrb_value
175 mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
177 const char *name;
178 mrb_int i, len, slen;
179 mrb_sym mid;
180 mrb_value members, slot, *ptr;
181 const mrb_value *ptr_members;
183 /* get base id */
184 name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
185 mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
187 members = mrb_struct_members(mrb, obj);
188 ptr_members = RARRAY_PTR(members);
189 len = RARRAY_LEN(members);
190 ptr = RSTRUCT_PTR(obj);
191 for (i=0; i<len; i++) {
192 slot = ptr_members[i];
193 if (mrb_symbol(slot) == mid) {
194 return ptr[i] = val;
197 mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, mid));
198 return mrb_nil_value(); /* not reached */
201 static mrb_value
202 mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
204 mrb_value val;
206 mrb_get_args(mrb, "o", &val);
207 return mrb_struct_set(mrb, obj, val);
210 static mrb_bool
211 is_local_id(mrb_state *mrb, const char *name)
213 if (!name) return FALSE;
214 return !ISUPPER(name[0]);
217 static mrb_bool
218 is_const_id(mrb_state *mrb, const char *name)
220 if (!name) return FALSE;
221 return ISUPPER(name[0]);
224 static void
225 make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
227 const mrb_value *ptr_members = RARRAY_PTR(members);
228 mrb_int i;
229 mrb_int len = RARRAY_LEN(members);
230 int ai = mrb_gc_arena_save(mrb);
232 for (i=0; i<len; i++) {
233 mrb_sym id = mrb_symbol(ptr_members[i]);
234 const char *name = mrb_sym2name_len(mrb, id, NULL);
236 if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
237 if (i < N_REF_FUNC) {
238 mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
240 else {
241 mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
243 mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
244 mrb_gc_arena_restore(mrb, ai);
249 static mrb_value
250 make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
252 mrb_value nstr;
253 mrb_sym id;
254 struct RClass *c;
256 if (mrb_nil_p(name)) {
257 c = mrb_class_new(mrb, klass);
259 else {
260 /* old style: should we warn? */
261 name = mrb_str_to_str(mrb, name);
262 id = mrb_obj_to_sym(mrb, name);
263 if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
264 mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
266 if (mrb_const_defined_at(mrb, klass, id)) {
267 mrb_warn(mrb, "redefining constant Struct::%S", name);
268 /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
270 c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
272 MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
273 nstr = mrb_obj_value(c);
274 mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
276 mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
277 mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
278 mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
279 /* RSTRUCT(nstr)->basic.c->super = c->c; */
280 make_struct_define_accessors(mrb, members, c);
281 return nstr;
284 /* 15.2.18.3.1 */
286 * call-seq:
287 * Struct.new( [aString] [, aSym]+> ) -> StructClass
288 * StructClass.new(arg, ...) -> obj
289 * StructClass[arg, ...] -> obj
291 * Creates a new class, named by <i>aString</i>, containing accessor
292 * methods for the given symbols. If the name <i>aString</i> is
293 * omitted, an anonymous structure class will be created. Otherwise,
294 * the name of this struct will appear as a constant in class
295 * <code>Struct</code>, so it must be unique for all
296 * <code>Struct</code>s in the system and should start with a capital
297 * letter. Assigning a structure class to a constant effectively gives
298 * the class the name of the constant.
300 * <code>Struct::new</code> returns a new <code>Class</code> object,
301 * which can then be used to create specific instances of the new
302 * structure. The number of actual parameters must be
303 * less than or equal to the number of attributes defined for this
304 * class; unset parameters default to <code>nil</code>. Passing too many
305 * parameters will raise an <code>ArgumentError</code>.
307 * The remaining methods listed in this section (class and instance)
308 * are defined for this generated class.
310 * # Create a structure with a name in Struct
311 * Struct.new("Customer", :name, :address) #=> Struct::Customer
312 * Struct::Customer.new("Dave", "123 Main") #=> #<struct Struct::Customer name="Dave", address="123 Main">
314 * # Create a structure named by its constant
315 * Customer = Struct.new(:name, :address) #=> Customer
316 * Customer.new("Dave", "123 Main") #=> #<struct Customer name="Dave", address="123 Main">
318 static mrb_value
319 mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
321 mrb_value name, rest;
322 mrb_value *pargv;
323 mrb_int argcnt;
324 mrb_int i;
325 mrb_value b, st;
326 mrb_sym id;
327 mrb_value *argv;
328 mrb_int argc;
330 name = mrb_nil_value();
331 rest = mrb_nil_value();
332 mrb_get_args(mrb, "*&", &argv, &argc, &b);
333 if (argc == 0) { /* special case to avoid crash */
334 rest = mrb_ary_new(mrb);
336 else {
337 if (argc > 0) name = argv[0];
338 if (argc > 1) rest = argv[1];
339 if (mrb_array_p(rest)) {
340 if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
341 /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
342 mrb_ary_unshift(mrb, rest, name);
343 name = mrb_nil_value();
346 else {
347 pargv = &argv[1];
348 argcnt = argc-1;
349 if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
350 /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
351 name = mrb_nil_value();
352 pargv = &argv[0];
353 argcnt++;
355 rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
357 for (i=0; i<RARRAY_LEN(rest); i++) {
358 id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
359 mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
362 st = make_struct(mrb, name, rest, struct_class(mrb));
363 if (!mrb_nil_p(b)) {
364 mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(klass));
367 return st;
370 static mrb_int
371 num_members(mrb_state *mrb, struct RClass *klass)
373 mrb_value members;
375 members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
376 if (!mrb_array_p(members)) {
377 mrb_raise(mrb, E_TYPE_ERROR, "broken members");
379 return RARRAY_LEN(members);
382 /* 15.2.18.4.8 */
385 static mrb_value
386 mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
388 struct RClass *klass = mrb_obj_class(mrb, self);
389 mrb_int i, n;
391 n = num_members(mrb, klass);
392 if (n < argc) {
393 mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs");
396 for (i = 0; i < argc; i++) {
397 mrb_ary_set(mrb, self, i, argv[i]);
399 for (i = argc; i < n; i++) {
400 mrb_ary_set(mrb, self, i, mrb_nil_value());
402 return self;
405 static mrb_value
406 mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value self)
408 mrb_value *argv;
409 mrb_int argc;
411 mrb_get_args(mrb, "*", &argv, &argc);
412 return mrb_struct_initialize_withArg(mrb, argc, argv, self);
415 static mrb_value
416 inspect_struct(mrb_state *mrb, mrb_value s, mrb_bool recur)
418 const char *cn = mrb_class_name(mrb, mrb_obj_class(mrb, s));
419 mrb_value members, str = mrb_str_new_lit(mrb, "#<struct ");
420 mrb_value *ptr;
421 const mrb_value *ptr_members;
422 mrb_int i, len;
424 if (cn) {
425 mrb_str_append(mrb, str, mrb_str_new_cstr(mrb, cn));
427 if (recur) {
428 return mrb_str_cat_lit(mrb, str, ":...>");
431 members = mrb_struct_members(mrb, s);
432 ptr_members = RARRAY_PTR(members);
433 ptr = RSTRUCT_PTR(s);
434 len = RSTRUCT_LEN(s);
435 for (i=0; i<len; i++) {
436 mrb_value slot;
437 mrb_sym id;
438 const char *name;
439 mrb_int namelen;
441 if (i > 0) {
442 mrb_str_cat_lit(mrb, str, ", ");
444 else if (cn) {
445 mrb_str_cat_lit(mrb, str, " ");
447 slot = ptr_members[i];
448 id = mrb_symbol(slot);
449 name = mrb_sym2name_len(mrb, id, &namelen);
450 if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
451 mrb_str_append(mrb, str, mrb_str_new(mrb, name, namelen));
453 else {
454 mrb_str_append(mrb, str, mrb_inspect(mrb, slot));
456 mrb_str_cat_lit(mrb, str, "=");
457 mrb_str_append(mrb, str, mrb_inspect(mrb, ptr[i]));
459 mrb_str_cat_lit(mrb, str, ">");
461 return str;
465 * call-seq:
466 * struct.to_s -> string
467 * struct.inspect -> string
469 * Describe the contents of this struct in a string.
471 static mrb_value
472 mrb_struct_inspect(mrb_state *mrb, mrb_value s)
474 return inspect_struct(mrb, s, FALSE);
477 /* 15.2.18.4.9 */
478 /* :nodoc: */
479 static mrb_value
480 mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
482 mrb_value s;
483 mrb_int i, len;
485 mrb_get_args(mrb, "o", &s);
487 if (mrb_obj_equal(mrb, copy, s)) return copy;
488 if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
489 mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
491 if (!mrb_array_p(s)) {
492 mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
494 if (RSTRUCT_LEN(copy) != RSTRUCT_LEN(s)) {
495 mrb_raise(mrb, E_TYPE_ERROR, "struct size mismatch");
497 len = RSTRUCT_LEN(copy);
498 for (i = 0; i < len; i++) {
499 mrb_ary_set(mrb, copy, i, RSTRUCT_PTR(s)[i]);
501 return copy;
504 static mrb_value
505 struct_aref_sym(mrb_state *mrb, mrb_value s, mrb_sym id)
507 mrb_value *ptr, members;
508 const mrb_value *ptr_members;
509 mrb_int i, len;
511 ptr = RSTRUCT_PTR(s);
512 members = mrb_struct_members(mrb, s);
513 ptr_members = RARRAY_PTR(members);
514 len = RARRAY_LEN(members);
515 for (i=0; i<len; i++) {
516 if (mrb_symbol(ptr_members[i]) == id) {
517 return ptr[i];
520 mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
521 return mrb_nil_value(); /* not reached */
524 static mrb_value
525 struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
527 if (i < 0) i = RSTRUCT_LEN(s) + i;
528 if (i < 0)
529 mrb_raisef(mrb, E_INDEX_ERROR,
530 "offset %S too small for struct(size:%S)",
531 mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
532 if (RSTRUCT_LEN(s) <= i)
533 mrb_raisef(mrb, E_INDEX_ERROR,
534 "offset %S too large for struct(size:%S)",
535 mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
536 return RSTRUCT_PTR(s)[i];
539 /* 15.2.18.4.2 */
541 * call-seq:
542 * struct[symbol] -> anObject
543 * struct[fixnum] -> anObject
545 * Attribute Reference---Returns the value of the instance variable
546 * named by <i>symbol</i>, or indexed (0..length-1) by
547 * <i>fixnum</i>. Will raise <code>NameError</code> if the named
548 * variable does not exist, or <code>IndexError</code> if the index is
549 * out of range.
551 * Customer = Struct.new(:name, :address, :zip)
552 * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
554 * joe["name"] #=> "Joe Smith"
555 * joe[:name] #=> "Joe Smith"
556 * joe[0] #=> "Joe Smith"
558 static mrb_value
559 mrb_struct_aref(mrb_state *mrb, mrb_value s)
561 mrb_value idx;
563 mrb_get_args(mrb, "o", &idx);
564 if (mrb_string_p(idx)) {
565 mrb_value sym = mrb_check_intern_str(mrb, idx);
567 if (mrb_nil_p(sym)) {
568 mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
570 idx = sym;
572 if (mrb_symbol_p(idx)) {
573 return struct_aref_sym(mrb, s, mrb_symbol(idx));
575 return struct_aref_int(mrb, s, mrb_int(mrb, idx));
578 static mrb_value
579 mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
581 mrb_value members, *ptr;
582 const mrb_value *ptr_members;
583 mrb_int i, len;
585 members = mrb_struct_members(mrb, s);
586 len = RARRAY_LEN(members);
587 if (RSTRUCT_LEN(s) != len) {
588 mrb_raisef(mrb, E_TYPE_ERROR,
589 "struct size differs (%S required %S given)",
590 mrb_fixnum_value(len), mrb_fixnum_value(RSTRUCT_LEN(s)));
592 ptr = RSTRUCT_PTR(s);
593 ptr_members = RARRAY_PTR(members);
594 for (i=0; i<len; i++) {
595 if (mrb_symbol(ptr_members[i]) == id) {
596 ptr[i] = val;
597 return val;
600 mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
601 return val; /* not reach */
604 /* 15.2.18.4.3 */
606 * call-seq:
607 * struct[symbol] = obj -> obj
608 * struct[fixnum] = obj -> obj
610 * Attribute Assignment---Assigns to the instance variable named by
611 * <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
612 * returns it. Will raise a <code>NameError</code> if the named
613 * variable does not exist, or an <code>IndexError</code> if the index
614 * is out of range.
616 * Customer = Struct.new(:name, :address, :zip)
617 * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
619 * joe["name"] = "Luke"
620 * joe[:zip] = "90210"
622 * joe.name #=> "Luke"
623 * joe.zip #=> "90210"
626 static mrb_value
627 mrb_struct_aset(mrb_state *mrb, mrb_value s)
629 mrb_int i;
630 mrb_value idx;
631 mrb_value val;
633 mrb_get_args(mrb, "oo", &idx, &val);
635 if (mrb_string_p(idx)) {
636 mrb_value sym = mrb_check_intern_str(mrb, idx);
638 if (mrb_nil_p(sym)) {
639 mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
641 idx = sym;
643 if (mrb_symbol_p(idx)) {
644 return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
647 i = mrb_int(mrb, idx);
648 if (i < 0) i = RSTRUCT_LEN(s) + i;
649 if (i < 0) {
650 mrb_raisef(mrb, E_INDEX_ERROR,
651 "offset %S too small for struct(size:%S)",
652 mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
654 if (RSTRUCT_LEN(s) <= i) {
655 mrb_raisef(mrb, E_INDEX_ERROR,
656 "offset %S too large for struct(size:%S)",
657 mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
659 return RSTRUCT_PTR(s)[i] = val;
662 /* 15.2.18.4.1 */
664 * call-seq:
665 * struct == other_struct -> true or false
667 * Equality---Returns <code>true</code> if <i>other_struct</i> is
668 * equal to this one: they must be of the same class as generated by
669 * <code>Struct::new</code>, and the values of all instance variables
670 * must be equal (according to <code>Object#==</code>).
672 * Customer = Struct.new(:name, :address, :zip)
673 * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
674 * joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
675 * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
676 * joe == joejr #=> true
677 * joe == jane #=> false
680 static mrb_value
681 mrb_struct_equal(mrb_state *mrb, mrb_value s)
683 mrb_value s2;
684 mrb_value *ptr, *ptr2;
685 mrb_int i, len;
686 mrb_bool equal_p;
688 mrb_get_args(mrb, "o", &s2);
689 if (mrb_obj_equal(mrb, s, s2)) {
690 equal_p = 1;
692 else if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct") ||
693 mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
694 equal_p = 0;
696 else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
697 mrb_bug(mrb, "inconsistent struct"); /* should never happen */
698 equal_p = 0; /* This substuture is just to suppress warnings. never called. */
700 else {
701 ptr = RSTRUCT_PTR(s);
702 ptr2 = RSTRUCT_PTR(s2);
703 len = RSTRUCT_LEN(s);
704 equal_p = 1;
705 for (i=0; i<len; i++) {
706 if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
707 equal_p = 0;
708 break;
713 return mrb_bool_value(equal_p);
716 /* 15.2.18.4.12(x) */
718 * code-seq:
719 * struct.eql?(other) -> true or false
721 * Two structures are equal if they are the same object, or if all their
722 * fields are equal (using <code>eql?</code>).
724 static mrb_value
725 mrb_struct_eql(mrb_state *mrb, mrb_value s)
727 mrb_value s2;
728 mrb_value *ptr, *ptr2;
729 mrb_int i, len;
730 mrb_bool eql_p;
732 mrb_get_args(mrb, "o", &s2);
733 if (mrb_obj_equal(mrb, s, s2)) {
734 eql_p = 1;
736 else if (strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s2)), "Struct") ||
737 mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
738 eql_p = 0;
740 else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
741 mrb_bug(mrb, "inconsistent struct"); /* should never happen */
742 eql_p = 0; /* This substuture is just to suppress warnings. never called. */
744 else {
745 ptr = RSTRUCT_PTR(s);
746 ptr2 = RSTRUCT_PTR(s2);
747 len = RSTRUCT_LEN(s);
748 eql_p = 1;
749 for (i=0; i<len; i++) {
750 if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
751 eql_p = 0;
752 break;
757 return mrb_bool_value(eql_p);
761 * call-seq:
762 * struct.length -> Fixnum
763 * struct.size -> Fixnum
765 * Returns number of struct members.
767 static mrb_value
768 mrb_struct_len(mrb_state *mrb, mrb_value self)
770 return mrb_fixnum_value(RSTRUCT_LEN(self));
774 * call-seq:
775 * struct.to_a -> array
776 * struct.values -> array
778 * Create an array from struct values.
780 static mrb_value
781 mrb_struct_to_a(mrb_state *mrb, mrb_value self)
783 return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self));
787 * call-seq:
788 * struct.to_h -> hash
790 * Create a hash from member names and struct values.
792 static mrb_value
793 mrb_struct_to_h(mrb_state *mrb, mrb_value self)
795 mrb_value members, ret;
796 mrb_int i;
798 members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_class(mrb, self)));
799 ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
801 for (i = 0; i < RARRAY_LEN(members); ++i) {
802 mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]);
805 return ret;
808 static mrb_value
809 mrb_struct_values_at(mrb_state *mrb, mrb_value self)
811 mrb_int argc;
812 mrb_value *argv;
814 mrb_get_args(mrb, "*", &argv, &argc);
816 return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
820 * A <code>Struct</code> is a convenient way to bundle a number of
821 * attributes together, using accessor methods, without having to write
822 * an explicit class.
824 * The <code>Struct</code> class is a generator of specific classes,
825 * each one of which is defined to hold a set of variables and their
826 * accessors. In these examples, we'll call the generated class
827 * ``<i>Customer</i>Class,'' and we'll show an example instance of that
828 * class as ``<i>Customer</i>Inst.''
830 * In the descriptions that follow, the parameter <i>symbol</i> refers
831 * to a symbol, which is either a quoted string or a
832 * <code>Symbol</code> (such as <code>:name</code>).
834 void
835 mrb_mruby_struct_gem_init(mrb_state* mrb)
837 struct RClass *st;
838 st = mrb_define_class(mrb, "Struct", mrb->object_class);
840 mrb_define_class_method(mrb, st, "new", mrb_struct_s_def, MRB_ARGS_ANY()); /* 15.2.18.3.1 */
842 mrb_define_method(mrb, st, "==", mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */
843 mrb_define_method(mrb, st, "[]", mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */
844 mrb_define_method(mrb, st, "[]=", mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */
845 mrb_define_method(mrb, st, "members", mrb_struct_members_m, MRB_ARGS_NONE()); /* 15.2.18.4.6 */
846 mrb_define_method(mrb, st, "initialize", mrb_struct_initialize_m,MRB_ARGS_ANY()); /* 15.2.18.4.8 */
847 mrb_define_method(mrb, st, "initialize_copy", mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */
848 mrb_define_method(mrb, st, "inspect", mrb_struct_inspect, MRB_ARGS_NONE()); /* 15.2.18.4.10(x) */
849 mrb_define_alias(mrb, st, "to_s", "inspect"); /* 15.2.18.4.11(x) */
850 mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */
852 mrb_define_method(mrb, st, "size", mrb_struct_len, MRB_ARGS_NONE());
853 mrb_define_method(mrb, st, "length", mrb_struct_len, MRB_ARGS_NONE());
854 mrb_define_method(mrb, st, "to_a", mrb_struct_to_a, MRB_ARGS_NONE());
855 mrb_define_method(mrb, st, "values", mrb_struct_to_a, MRB_ARGS_NONE());
856 mrb_define_method(mrb, st, "to_h", mrb_struct_to_h, MRB_ARGS_NONE());
857 mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_NONE());
860 void
861 mrb_mruby_struct_gem_final(mrb_state* mrb)